Skip to content

Commit c3642db

Browse files
devallibusclaude
andcommitted
fix(playground): address PR review findings
1. High: Fix SSR crash — move window.history.replaceState out of createResource fetcher into a client-only createEffect with isServer guard and useNavigate for URL updates. 2. Medium: Remove auth check from /update endpoint — the session UUID is the capability token. Both browser editor and MCP worker can update. Only /create requires PLAYGROUND_SECRET. 3. Medium: Add uniforms array to create_playground tool schema in both TOOLS and TOOLS_MCP_FORMAT so schema-driven MCP clients expose the parameter correctly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 71cc344 commit c3642db

3 files changed

Lines changed: 52 additions & 9 deletions

File tree

apps/web/src/routes/api/playground/$.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,9 @@ async function handlePlayground(request: Request): Promise<Response> {
138138
}
139139

140140
// POST /api/playground/:sessionId/update
141+
// No auth required — the session UUID is the capability token.
142+
// Both the browser editor and the MCP worker can update.
141143
if (action === 'update' && request.method === 'POST') {
142-
if (!isAuthorized(request)) return unauthorizedResponse()
143144
const session = getSession(sessionId)
144145
if (!session) return jsonResponse({ error: 'Session not found' }, 404)
145146

apps/web/src/routes/playground.tsx

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { createFileRoute, useSearch } from '@tanstack/solid-router'
1+
import { createFileRoute, useNavigate, useSearch } from '@tanstack/solid-router'
22
import { createServerFn } from '@tanstack/solid-start'
3-
import { createResource, Show, Suspense } from 'solid-js'
3+
import { createEffect, createResource, on, Show, Suspense } from 'solid-js'
4+
import { isServer } from 'solid-js/web'
45
import PlaygroundLayout from '../components/playground/PlaygroundLayout'
56
import type { PlaygroundSession } from '../lib/playground-types'
67

@@ -28,21 +29,30 @@ export const Route = createFileRoute('/playground')({
2829

2930
function PlaygroundPage() {
3031
const search = useSearch({ from: '/playground' })
32+
const navigate = useNavigate()
3133

3234
const [session] = createResource(
3335
() => search.session,
3436
async (sessionId) => {
3537
const result = await getOrCreateSession({ data: { sessionId } })
36-
// Update URL with session ID if we created a new one
37-
if (!sessionId && result?.id) {
38-
const url = new URL(window.location.href)
39-
url.searchParams.set('session', result.id)
40-
window.history.replaceState({}, '', url.toString())
41-
}
4238
return result as PlaygroundSession
4339
},
4440
)
4541

42+
// Update URL with session ID after hydration (client-only)
43+
createEffect(
44+
on(
45+
() => session(),
46+
(s) => {
47+
if (isServer || !s) return
48+
if (!search.session) {
49+
navigate({ search: { session: s.id }, replace: true })
50+
}
51+
},
52+
{ defer: true },
53+
),
54+
)
55+
4656
return (
4757
<Suspense
4858
fallback={

packages/mcp/src/index.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,22 @@ const TOOLS = [
117117
properties: {
118118
vertexSource: { type: "string", description: "Initial vertex shader GLSL source." },
119119
fragmentSource: { type: "string", description: "Initial fragment shader GLSL source." },
120+
uniforms: {
121+
type: "array",
122+
items: {
123+
type: "object",
124+
properties: {
125+
name: { type: "string" },
126+
type: { type: "string" },
127+
defaultValue: {},
128+
description: { type: "string" },
129+
min: { type: "number" },
130+
max: { type: "number" },
131+
},
132+
required: ["name", "type", "defaultValue"],
133+
},
134+
description: "Uniform definitions for the shader.",
135+
},
120136
pipeline: { type: "string", description: "Rendering pipeline: 'surface', 'postprocessing', or 'geometry'." },
121137
},
122138
additionalProperties: false,
@@ -250,6 +266,22 @@ const TOOLS_MCP_FORMAT = [
250266
type: "string",
251267
description: "Initial fragment shader GLSL source (defaults to an animated color gradient)",
252268
},
269+
uniforms: {
270+
type: "array",
271+
items: {
272+
type: "object",
273+
properties: {
274+
name: { type: "string" },
275+
type: { type: "string" },
276+
defaultValue: {},
277+
description: { type: "string" },
278+
min: { type: "number" },
279+
max: { type: "number" },
280+
},
281+
required: ["name", "type", "defaultValue"],
282+
},
283+
description: "Uniform definitions (name, type, defaultValue, optional min/max/description)",
284+
},
253285
pipeline: {
254286
type: "string",
255287
description: "Rendering pipeline: 'surface' (default), 'postprocessing', or 'geometry'",

0 commit comments

Comments
 (0)