Skip to content

Migrate to the mcp 2.0 server API (closes #20) - #21

Open
linsamtw wants to merge 1 commit into
masterfrom
feat/mcp-2-migration
Open

Migrate to the mcp 2.0 server API (closes #20)#21
linsamtw wants to merge 1 commit into
masterfrom
feat/mcp-2-migration

Conversation

@linsamtw

@linsamtw linsamtw commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Ports server.py off the mcp 1.x decorator API and drops the mcp<2.0.0 ceiling that 0.0.6 added as a stopgap (#18 / #16). mcp>=2.0.0 from here.

Everything below was verified against a real mcp==2.0.0 install, not from memory.

What mcp 2.0 actually changed

  1. Handler registration moved to the constructor. The lowlevel Server still exists; @app.list_tools() is replaced by Server("finmind", on_list_tools=..., on_call_tool=..., on_list_resources=..., on_read_resource=...). Handlers take (ctx, params) and return full result models (ListToolsResult, CallToolResult, …) instead of bare lists.
  2. Every type field is snake_case now, with camelCase kept as the JSON alias: inputSchemainput_schema, isErroris_error, mimeTypemime_type. This reaches tools.py and knowledge.py, not just server.py. The wire format is unchanged — verified below.

The one behaviour that needed restoring by hand

A raising call_tool handler mcp 1.x mcp 2.0
becomes CallToolResult(isError=true) — the model sees the message and can retry a JSON-RPC protocol error — the host reports a server failure

tools.dispatch raises only on an unknown tool name (everything else is already rendered as a 繁中 message), so the blast radius is narrow — but a purely mechanical port would have silently downgraded that path. on_call_tool now catches and returns is_error=True itself, and tests/test_server.py has a test whose only job is to keep it that way.

Verification

Unit tests — tests/test_server.py is new; server.py previously had zero unit coverage, and the handlers are now plain async functions that need no subprocess:

uv run --extra dev pytest -q          →  39 passed  (was 32)
uv run --python 3.10 pytest -q        →  39 passed  (3.10 floor holds)
uv run python smoke.py                →  SMOKE OK
uv run python chatgpt/build_instructions.py →  OK (7987/8000 chars)

End-to-end over real stdio JSON-RPC, checking the raw frames rather than the SDK's own view:

protocolVersion: 2024-11-05        ← old-protocol hosts still handshake fine
tool keys:       ['description', 'inputSchema', 'name']    ← camelCase intact
call keys:       ['content', 'isError']
unknown tool  →  result with isError: true   (NOT a protocol error)
resource keys:   [..., 'mimeType', ...]

mcp 2.0 accepts handshakes from 2024-11-05 through the new 2026-07-28, so existing Claude Desktop / Cursor / Gemini CLI configs keep working.

Not done here

smoke.py and regression/runner.py needed no changes — they speak raw JSON-RPC and are version-agnostic. Worth one manual run in a real host before tagging.

Release: the dependency floor crosses a major version, so 0.1.0 reads better than 0.0.7 — tag is yours to push.

🤖 Generated with Claude Code

mcp 2.0 replaced the 1.x `@app.list_tools()` decorators with constructor
kwargs (`on_list_tools=`, `on_call_tool=`, ...), where handlers take
(ctx, params) and return full result models. It also renamed every type
field to snake_case, keeping camelCase as the JSON wire alias — so
`inputSchema`/`isError`/`mimeType` become `input_schema`/`is_error`/
`mime_type` in Python while the on-the-wire JSON is unchanged.

One behaviour needed restoring by hand: mcp 1.x wrapped call_tool handlers
and turned exceptions into CallToolResult(isError=True), which the model
sees and can act on. mcp 2.0 lets them escape and become JSON-RPC protocol
errors, which hosts report as a server failure. on_call_tool now catches
and reports isError itself; tests/test_server.py guards it.

Drops the mcp<2.0.0 ceiling added in 0.0.6 (#18). Python floor stays 3.10 —
mcp 2.0 requires >=3.10 too.

Closes #20

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.

1 participant