Skip to content

1.2.1

Latest

Choose a tag to compare

@fuwasegu fuwasegu released this 07 Aug 06:09
ccd58ed

Fixes a regression in 1.2.0 that broke stdio proxy mode. Closes #30.

Who this affects

Anyone running --mode proxy (the recommended setup for Cursor and for multiple editor instances) on 1.2.0. Cursor showed the server as errored and no exo_* tool reached the agent:

Invalid input: expected object, received undefined
path: ["tools", N, "inputSchema"]

Direct --mode server was never affected — the SSE server always emitted correct MCP. Only the proxy corrupted the payload on its way to stdout.

Upgrade to 1.2.1. In proxy mode the server restarts itself on the version change.

What went wrong

The proxy re-serializes results from the SSE server before writing them to stdout. Under mcp 1.x the Pydantic field names were the protocol names, so a plain model_dump() happened to produce valid MCP. mcp 2.0 renamed the fields to snake_case and moved the protocol names to aliases:

mcp 1.x mcp 2.0
field inputSchema input_schema (alias inputSchema)
plain model_dump() inputSchema ✅ input_schema ❌

So the proxy started emitting a key the MCP schema does not define, and strict clients rejected the entire response. This came in with the mcp 2.x migration in 1.2.0.

inputSchema was not the only casualty — outputSchema, mimeType and _meta are aliased too, and were equally broken.

Serialization now goes through a single helper that applies by_alias=True, so no branch can omit it.

Why it was not caught

The 1.2.0 migration verified the server end thoroughly — tool registration, input schemas, concurrency, ASGI mounts — but nothing covered the proxy's re-serialization, which is the only place SDK models are re-dumped by hand. That gap is now closed by tests asserting the general rule: no field may be sent under its Python name when it has a differing wire alias. That will catch the next rename, not just this one.

Verification

Against a real stdio proxy bridging a live SSE server: all 19 tools carry camelCase inputSchema, none leak input_schema. 340 tests pass on Python 3.10, 3.11 and 3.12.

Thanks to @novr for the diagnosis, the exact root cause, and a working patch.

Full Changelog: 1.2.0...1.2.1