feat(mcp)!: migrate to MCP SDK 2.0 - #36
Draft
fedorov wants to merge 1 commit into
Draft
Conversation
SDK 2.0 removes `mcp.server.fastmcp`. Port the adapter to the renamed high-level API:
FastMCP -> MCPServer (mcp.server.mcpserver)
fastmcp.exceptions.ToolError -> mcpserver.exceptions.ToolError
stateless_http/json_response/ -> streamable_http_app() kwargs
transport_security, host, port
mcp._mcp_server.version = ... -> MCPServer(version=...)
Two things this cleans up. The private `_mcp_server` reach-in for serverInfo.version is gone —
2.0 takes `version=` on the constructor and plumbs it to the handshake, so the comment
explaining the reach-in goes with it. And `http_app()` no longer reads the endpoint path back
off server state; it takes `streamable_http_path` as an argument, which is also what let the
slash-agnostic test drop its FASTMCP_-env caveat.
`transport_security` must now be passed to `streamable_http_app()` explicitly: 2.0 enables
DNS-rebinding protection by default, and without it every request on a hosted domain answers
421 "Invalid Host header". Covered by the existing HTTP transport tests.
Behaviour is unchanged — same tools, same resources, same response shapes, both `/mcp` and
`/mcp/` still served directly. The one user-visible loss is the SDK's undocumented FASTMCP_*
environment variables; --host/--port keep their 127.0.0.1:8000 defaults.
New transitive dependencies: mcp-types, opentelemetry-api, truststore, and httpx2/httpcore2
(Pydantic's httpx fork). Floors pydantic at 2.12.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The rename
Unchanged, verified:
read_resource()still yieldsReadResourceContentswith.content;list_tools()/list_resources()identical;ToolErrorstill propagates out ofcall_tool(thepytest.raisestests hold); templated resources (idc://schema/{table}) still resolve; the SDK still registers a single exact-path route, so the/mcp+/mcp/aliasing inhttp_app()is still needed and still works.Two things this cleans up
The private
_mcp_serverreach-in forserverInfo.versionis gone — 2.0 takesversion=on the constructor and plumbs it to the handshake, so the comment explaining the reach-in goes with it. Andhttp_app()no longer reads the endpoint path back off server state; it takesstreamable_http_pathas an argument, which also let the slash-agnostic test drop itsFASTMCP_-env caveat.The sharp edge
transport_securitymust be passed tostreamable_http_app()explicitly. 2.0 enables DNS-rebinding protection by default; without it every request on a hosted domain answers421 Invalid Host header. My first pass dropped the kwarg and the Cloud Run path would have been dead on arrival._transport_security()returns disabled by default here (public, unauthenticated, read-only service), so a missing kwarg silently inverts a security posture. The existing HTTP transport tests catch it.Verification
Full suite green locally against
mcp==2.0.0on Python 3.12:That includes
test_parity.py(REST output == MCP output) and both HTTP transport tests, which drive a realinitializehandshake throughhttp_app()on/mcpand/mcp/and assert HSTS.User-visible
Behaviour is unchanged — same tools, same resources, same response shapes, both slash forms served directly. The one loss is the SDK's undocumented
FASTMCP_HOST/FASTMCP_PORT/FASTMCP_STREAMABLE_HTTP_PATHenv vars, which no longer have any effect;--host/--portkeep their127.0.0.1:8000defaults and are what the deployment has always passed. CHANGELOG entry included.Dependency surface
New transitives:
mcp-types,opentelemetry-api,truststore, andhttpx2/httpcore2(Pydantic's httpx fork). Floorspydanticat 2.12. Worth a look before this lands on an NCI-deployed service — that's part of why it's parked.🤖 Generated with Claude Code