Skip to content

Fix Cloudflare Worker deployment: SQLite migration, SSE message routing, AJV crash#111

Open
iamugahh wants to merge 1 commit into
devondragon:mainfrom
iamugahh:fix/cloudflare-worker-deployment
Open

Fix Cloudflare Worker deployment: SQLite migration, SSE message routing, AJV crash#111
iamugahh wants to merge 1 commit into
devondragon:mainfrom
iamugahh:fix/cloudflare-worker-deployment

Conversation

@iamugahh

@iamugahh iamugahh commented Jul 6, 2026

Copy link
Copy Markdown

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_classesnew_sqlite_classes

wrangler deploy fails on Cloudflare free plans because new_classes requests a non-SQLite-backed Durable Object, which requires a paid plan:

Cannot create binding for class MotionMCPAgent that is not currently in the Workers,
you should either use new_sqlite_classes instead of new_classes... [code: 10097]

Switching the migration to new_sqlite_classes deploys 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 routed

The fetch handler routed everything through McpAgent.mount() (legacy SSE transport only) after stripping the secret from the path. Two problems fall out of that:

  • The secret-stripping rewrite breaks the SSE message endpoint. When a client opens the SSE stream, the agent advertises its message endpoint via the endpoint event — 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.
  • Streamable HTTP (the current MCP transport) wasn't served at allmount() only handles the legacy SSE protocol.

The fix:

  • Adds a pass-through for POST /mcp/message?sessionId=... ahead of the secret check. The sessionId is 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.
  • Routes streamable HTTP requests (POST/DELETE /mcp, or GET with an mcp-session-id header) to McpAgent.serve(), keeping a bare GET /mcp on mount() for legacy SSE clients.

3. src/worker.ts init(): remove validator.initializeValidators()

Every session crashed at init with:

EvalError: Code generation from strings disallowed for this context

initializeValidators() calls ajv.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 to server.tool(). The stdio server is untouched.

Testing

  • wrangler deploy succeeds on a free-plan account (previously error 10097).
  • Legacy SSE: stream opens, the endpoint event's advertised URL now accepts POSTs, tool calls round-trip.
  • Streamable HTTP: POST /mcp initialize + subsequent requests with mcp-session-id are served by serve().
  • Sessions no longer crash with the AJV EvalError.

🤖 Generated with Claude Code

…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants