serve: dual compatibility with MCP SDK 1.x and 2.x (lifts the <2 pin) - #2311
serve: dual compatibility with MCP SDK 1.x and 2.x (lifts the <2 pin)#2311NiSHoW wants to merge 3 commits into
Conversation
mcp 2.0 removed the low-level decorator API (Server.list_tools/call_tool/ list_resources/read_resource) that serve.py is built on, so an install that resolved the unpinned extra to 2.0.0 crashed at server startup (first at `from mcp.types import AnyUrl`, then at @server.list_tools()). Pin mcp>=1,<2 in the `mcp` and `all` extras; import AnyUrl with a pydantic fallback so that piece is already 2.x-ready. Verified with a full stdio handshake (initialize / tools/list / tools/call) against the installed graphify-mcp with mcp 1.29.0. Porting to the 2.x API is a separate work item. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mcp 2.0 removed the low-level decorator API the server was built on and changed several models. _build_server now defines the four handlers as plain coroutines and binds them at the end in a version-aware way: the @server.list_tools()/... decorators on 1.x, the on_list_tools=/... constructor callbacks (with explicit ListToolsResult/CallToolResult/ ListResourcesResult/ReadResourceResult wrapping) on 2.x. Model diffs handled: Tool.inputSchema is input_schema on 2.x (constructor aliases work on both, attribute access does not); Resource.uri is a plain str on 2.x and rejects AnyUrl instances, so resource URIs are passed as strings (1.x coerces them). The 2.x server also reports graphifyy's own version in serverInfo. Extras relaxed from mcp>=1,<2 to mcp>=1,<3. Verified: full stdio handshake (initialize / tools/list / tools/call / resources/list / resources/read) and the tests in test_serve.py + test_serve_http.py (145 passed) under BOTH mcp 1.29.0 and mcp 2.0.0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Graphify review — findings
This PR makes the MCP server code in graphify/serve.py compatible with both the 1.x and 2.x versions of the mcp SDK, and lifts the <2 version cap that was previously introduced in 0.9.30. The tool/resource handlers are refactored from decorator-based registration into plain coroutines that are bound to the Server at runtime — via the 1.x @server.list_tools()-style decorators when available, or the 2.x on_* constructor callbacks otherwise. It also adapts for API differences across the two majors: the AnyUrl import (falling back to pydantic), inputSchema/input_schema attribute access, and plain-string vs AnyUrl resource URIs. The dependency specifiers in pyproject.toml (and uv.lock) for the mcp and all extras are updated from mcp>=1,<2 to mcp>=1,<3, and a changelog entry plus code comments are added.
No blocking issues surfaced. 8 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 557 functions depend on the 282 functions this change touches.
Health — this change adds coupling hotspots:
- worse:
read_resource()— 1 callers, 6 callees
Verification — 557 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 427 function(s) in the blast radius were not formally verified this run
· 1 grounded finding(s) anchored inline below.
|
Thanks @NiSHoW. Shipped in v0.9.31 (cherry-picked to v8 to preserve authorship) — resolves #2308 and lifts the mcp<2 cap from 0.9.30 to mcp>=1,<3. Verified on the published wheel: a fresh graphifyy[mcp] install resolves mcp 2.0.0 and graphify.serve imports cleanly. Follow-up worth doing: a CI leg pinning mcp>=2 with a real initialize/tools-list handshake. Closed-unmerged here, but it's in the release: https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.31 |
Summary
0.9.30 pinned
mcp>=1,<2because the mcp 2.0.0 major broke every freshgraphifyy[mcp]install (#2277, #2279, #2291), with "Adapting to the mcp 2.x API is tracked as a follow-up" in the changelog. This PR is that follow-up:graphify/serve.pynow works with both SDK majors, and the extras cap is lifted tomcp>=1,<3.What changed
mcp 2.0 removed the low-level decorator registration API (
@server.list_tools()/call_tool/list_resources/read_resource) and changed several models._build_servernow defines the four handlers as plain coroutines and binds them at the end in a version-aware way:hasattr(Server, "list_tools")): the existing decorator registration, unchanged behavior;Server(...)constructor ason_list_tools=/on_call_tool=/on_list_resources=/on_read_resource=callbacks with the(ctx, params) -> Resultcontract, wrapping the same impls inListToolsResult/CallToolResult/ListResourcesResult/ReadResourceResult(the wrapping the 1.x decorators used to do implicitly).Model differences handled so one code path serves both majors:
Tool.inputSchemaisinput_schemaon 2.x — the constructor accepts the camelCase alias on both majors, but attribute access does not, so theproject_pathschema injection reads it viagetattrfallback;Resource.uriis a plainstron 2.x and rejectsAnyUrlinstances, so resource URIs are passed as plain strings (1.x coerces them toAnyUrlitself);mcp.types.AnyUrl(dropped as a re-export in 2.0) falls back topydantic.AnyUrl— same class, and pydantic is an mcp dependency on both majors;serverInfo(the SDK no longer substitutes its own).Extras:
mcp = ["mcp>=1,<3", "starlette>=1.3.1,<2"](same forall), keeping the starlette cap 0.9.30 introduced.uv.lockre-locked accordingly. Nothing changes for users currently resolving to 1.x.Verification
tests/test_serve.py+tests/test_serve_http.py(153 tests, including the Streamable HTTP transport): passing under bothmcp==1.29.0andmcp==2.0.0in dedicated venvs.graphify-mcpunder both majors:initialize→notifications/initialized→tools/list(all 10 tools) →tools/call query_graph→resources/list(6 resources) →resources/read.🤖 Generated with Claude Code