feat: migrate to mcp 2.0 (FastMCP → MCPServer) - #104
Merged
Conversation
mcp 2.0.0 renames FastMCP to MCPServer and moves mcp.server.fastmcp.* to mcp.server.mcpserver.* with no back-compat alias, so the bump in #103 could not import server.main at all. - rename FastMCP -> MCPServer and update imports across 10 modules - drop host/port from the constructor (removed in 2.x, now a TypeError) and pass host to streamable_http_app()/sse_app() instead - bump mcp to >=2.0.0 and relock The host kwarg is load-bearing, not cosmetic: the app factories auto-enable DNS rebinding protection when host is a loopback address, so leaving it at the 127.0.0.1 default made the containerized server (MCP_HOST=0.0.0.0) reject every request with 421 Invalid Host header. Verified: custom_route, tool, prompt and settings.log_level all survive the move, both NDJSON routes still register, and all 293 tests pass. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
mcp 2.x adds a `version` kwarg to the server constructor; left unset it defaults to "" and clients showed an empty version. Source it from installed package metadata so pyproject stays the single place to bump, falling back to 0.0.0 when the package isn't installed (bare source checkout) rather than failing at import. Verified in Docker: serverInfo.version now reports "1.0.0". Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
GoodbyePlanet
added a commit
that referenced
this pull request
Aug 11, 2026
Rebuilt on current main and reduced to just the tree-sitter-language-pack bump. The mcp 1.28.1 -> 2.0.0 bump is dropped from this PR: it is a major version whose module paths and class names changed, so it cannot land without a source migration (see #104). Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
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.
Why
Dependabot #103 bumps
mcp1.28.1 → 2.0.0, but that is a major version with no source migration, so it cannot be merged as-is:mcp.server.fastmcpno longer exists in 2.0.0 and there is no back-compat alias. On that branchserver.mainfails to import andpytesterrors during collection.This PR lands the migration so the bump becomes mergeable.
What changed
FastMCP→MCPServer,mcp.server.fastmcp→mcp.server.mcpserver, across 10 modules (8 source, 2 test).host=/port=from the constructor — removed in 2.x, they now raiseTypeError. The HTTP path already passed host/port touvicorn.run(), so nothing is lost.host=settings.mcp_hosttostreamable_http_app()/sse_app()— see below, this one is load-bearing.mcp>=2.0.0and relocked. Lock diff is confined to mcp + transitive deps (httpx2,httpcore2,mcp-types,opentelemetry-api,truststore).serverInfo.versionfrom package metadata (2.x adds aversionkwarg; unset it serialises as""), and bump the project to 1.1.0..claude/CLAUDE.md, renamed.claude/rules/fastmcp-http.md→mcp-http.md.The one non-obvious trap
A rename-only migration compiles, passes CI, and is broken in Docker. In 2.x the app factories auto-enable DNS rebinding protection when
hostis a loopback address, andhostdefaults to127.0.0.1. Since compose setsMCP_HOST=0.0.0.0, every request would be rejected:The test suite cannot catch this — it is container-only. Hence threading the configured host through explicitly, with a comment at the call site.
Verified against a real mcp 2.0.0 install
Not just read off the migration guide — I introspected the installed package and ran the server:
custom_route,tool/prompt, andsettings.log_levelall survive the move. The NDJSON endpoints were never at risk.EMBEDDINGS_PROVIDER=openai): clean startup with 0 errors; lifespan runs exactly once (no per-connection store re-init);POST /mcpinitialize → 200 with"serverInfo":{"name":"semcode","version":"1.1.0"};POST /reindexandPOST /reindex-history→ 200 with valid NDJSON frames;POST /nope→ 404, confirming real routing.Notes for the reviewer
mcp<2if you would rather defer the major). This branch deliberately does not include chore(deps): Bump the dependencies group with 2 updates #103'stree-sitter-language-pack1.13.3 → 1.13.7 bump, which is unrelated and low-risk.serverInfois not asserted anywhere in the suite, which is why the empty version went unnoticed. A small test asserting a non-empty version would stop that regressing — not included here, happy to add.1.1.0; if you consider the transitivemcpmajor a breaking change for consumers, 2.0.0 would be the semver-strict call.🤖 Generated with Claude Code