Fix Cloudflare Worker deployment: SQLite migration, SSE message routing, AJV crash#111
Open
iamugahh wants to merge 1 commit into
Open
Fix Cloudflare Worker deployment: SQLite migration, SSE message routing, AJV crash#111iamugahh wants to merge 1 commit into
iamugahh wants to merge 1 commit into
Conversation
…ng, AJV crash
Three fixes that make the Worker deployment actually work:
- wrangler.toml: use new_sqlite_classes instead of new_classes in the
[[migrations]] block. new_classes requests a non-SQLite Durable Object,
which fails to deploy on Cloudflare free plans with error 10097.
- src/worker.ts fetch handler: add a sessionId-authenticated pass-through
for POST /mcp/message. The secret-stripping rewrite made the SSE
endpoint event advertise /mcp/message?sessionId=... (no secret
segment), which the worker's own secret check then 404'd, so SSE
clients could never POST messages. Also route streamable HTTP
(POST/DELETE /mcp, or GET with an mcp-session-id header) to
McpAgent.serve() instead of only mounting the legacy SSE transport.
- src/worker.ts init(): drop validator.initializeValidators(). It calls
ajv.compile(), which uses runtime code generation that Cloudflare
Workers forbids ("EvalError: Code generation from strings disallowed"),
crashing every session. The call is dead code in the Worker path since
validateInput() is only invoked from the stdio entry point.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
I deployed the Worker following the README and hit three independent blockers that together make the Cloudflare deployment unusable out of the box. This PR fixes all three; with these changes the Worker deploys on a free plan and works with both the legacy SSE transport and streamable HTTP.
1.
wrangler.toml:new_classes→new_sqlite_classeswrangler deployfails on Cloudflare free plans becausenew_classesrequests a non-SQLite-backed Durable Object, which requires a paid plan:Switching the migration to
new_sqlite_classesdeploys everywhere (SQLite-backed DOs are the current Cloudflare recommendation regardless of plan).2.
src/worker.ts: SSE clients could never POST messages, and streamable HTTP wasn't routedThe fetch handler routed everything through
McpAgent.mount()(legacy SSE transport only) after stripping the secret from the path. Two problems fall out of that:endpointevent — but because the agent only ever sees the rewritten URL, it advertises/mcp/message?sessionId=...with no secret segment. When the client then POSTs there, the worker's own secret check (pathParts[1] !== env.MOTION_MCP_SECRET) 404s it. Net effect: the SSE handshake succeeds but every subsequent message is rejected, so no client can actually use the server.mount()only handles the legacy SSE protocol.The fix:
POST /mcp/message?sessionId=...ahead of the secret check. ThesessionIdis an unguessable value issued only on a stream that was opened with the secret, so it authenticates these POSTs; the secret still gates opening a session.POST/DELETE /mcp, orGETwith anmcp-session-idheader) toMcpAgent.serve(), keeping a bareGET /mcponmount()for legacy SSE clients.3.
src/worker.tsinit(): removevalidator.initializeValidators()Every session crashed at init with:
initializeValidators()callsajv.compile(), which generates validator functions from strings at runtime — forbidden in the Workers runtime. The call is also dead code on this path:validateInput()is only invoked from the stdio entry point (src/mcp-server.ts), and the Worker already gets input validation from the Zod schemas passed toserver.tool(). The stdio server is untouched.Testing
wrangler deploysucceeds on a free-plan account (previously error 10097).endpointevent's advertised URL now accepts POSTs, tool calls round-trip.POST /mcpinitialize + subsequent requests withmcp-session-idare served byserve().EvalError.🤖 Generated with Claude Code