SimbaStack Main site →

MCP output schemas can break clients on edge runtimes

MCP output schemas can break clients on edge runtimes

mcp.so's SlopIt listing on October 10, while "Fetch tools" kept failing.

I listed SlopIt's MCP server on the MCP directory mcp.so on October 10, and its "Fetch tools" button kept loading and then nothing. SlopIt is our blog platform for AI agents, and its MCP server lives at slopit.io/mcp. Claude Code fixed that, and the next click gave SSE error: Non-200 status code (405). It fixed that too, and then I got Code generation from strings disallowed for this context.

Earlier that day we'd added an outputSchema to every tool, as part of the work that took our Smithery score from 73 to 98. mcp.so's checker seems to run on Cloudflare-style hosting, where code isn't allowed to build functions from strings (new Function). The v1 TypeScript SDK client compiles every output schema with Ajv, which does exactly that, and since version 1.21 a failed compile kills the whole tool list. It's a known SDK problem (typescript-sdk#689 hit the same error on Workers). So mcp.so's normal attempt crashed and it fell back to the old SSE transport, which we weren't serving properly either. The first two errors came from that fallback.

Diagram: mcp.so's Fetch tools first tries Streamable HTTP, where the client crashes compiling our 12 output schemas, then falls back to legacy HTTP+SSE with GET /mcp. Before any fix our server answered with an empty 200 event stream and mcp.so kept loading. Fix 1 returned 405, giving "SSE error: Non-200 status code (405)". Fix 2 served a real SSE stream but tools/list still had outputSchema, giving "Code generation from strings disallowed for this context". Fix 3 dropped outputSchema on SSE and all 12 tools listed.

What mcp.so's "Fetch tools" went through, and what each fix changed.

For anyone writing a client, the SDK has had a validator that doesn't generate code since 1.21, and the v2 client switches to it on its own on Workers:

import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/sdk/validation/cfworker'

const client = new Client(
  { name: 'my-client', version: '1.0.0' },
  { jsonSchemaValidator: new CfWorkerJsonSchemaValidator() },
)

On our side we couldn't change mcp.so's client, so we stopped sending output schemas over the old SSE transport (they were added to the spec after it was deprecated anyway). That's what got mcp.so working. We also dropped the SDK's default of answering a GET with an empty stream that never sends anything, which was the endless loading. Now that GET gets a 405, unless it comes from an old SSE client, which gets a real stream.

The same mcp.so listing after the fixes, with Tools showing 12 and the signup tool listed first

The same listing after the fixes, with all 12 tools.

We still send the schemas over Streamable HTTP, though. So a v1 client on 1.21 or later, running on Workers with the default validator, still can't list our tools unless it falls back to SSE like mcp.so does. We're keeping them for now, and I think that's fine. Every client we tested outside an edge runtime reads them, and they're part of the Smithery 98.

Two checks worth running before you list an MCP server anywhere:

— NJ

I run SimbaStack, where we build AI agents and the systems they work in. If you need help getting an agent into production, tell me what you're building: nj@simbastack.com.

#case-study#mcp#typescript#cloudflare-workers#slopit#claude-code