feat(mcp): migrate to mcp 2.x (MCPServer) #23

Merged
claude merged 1 commits from feat/mcp-2x into main 2026-09-10 14:22:42 -04:00
Contributor

Pilot for moving the fleet off mcp 1.x. Replaces the mcp<2 ceiling landed in #22.

The port

1.x 2.x
import mcp.server.fastmcp.FastMCP mcp.server.mcpserver.MCPServer
constructor FastMCP(name, stateless_http=True) MCPServer(name)
transport cfg mcp.settings.host/port/transport_security run() kwargs; settings is gone
@mcp.tool() — unchanged

Verification

1. tools/list is byte-for-byte identical between mcp 1.27.1 and 2.x — 6 tools, same names, same inputSchema properties and required lists, dumped with by_alias=True so the comparison is in wire format. This was the real risk: 2.x moved the protocol types to snake_case internally, and tool schemas decide routing.

$ diff <(json.tool tools-1x.json) <(json.tool tools-2x.json)
IDENTICAL — tools/list byte-for-byte unchanged

2. Boots for real over streamable-http with stateless_http=True and DNS-rebinding protection disabled, answering initialize 200 with the same capability set:

{"result":{"capabilities":{"experimental":{},"prompts":{"listChanged":false},
 "resources":{"listChanged":false,"subscribe":false},"tools":{"listChanged":false}},
 "protocolVersion":"2025-06-18","serverInfo":{"name":"crop_seed-docs","version":""}}}

3. CI runs import docs_mcp.server inside the built image before pushing (added in #22).

One behavior change

serverInfo.version is "" under 2.x instead of the SDK version — 2.x stopped defaulting it. Cosmetic; left as-is rather than inventing a version scheme. Say the word if you'd rather it carry the corpus tag.

Dependency notes

2.x has no [fastmcp] extra, swaps httpx for httpx2, and requires opentelemetry-api. Our own httpx>=0.27 is unaffected — httpx2 is a separate distribution.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FnVuG79cYPcRLTp4pC8ujR

Pilot for moving the fleet off mcp 1.x. Replaces the `mcp<2` ceiling landed in #22. ## The port | | 1.x | 2.x | |---|---|---| | import | `mcp.server.fastmcp.FastMCP` | `mcp.server.mcpserver.MCPServer` | | constructor | `FastMCP(name, stateless_http=True)` | `MCPServer(name)` | | transport cfg | `mcp.settings.host/port/transport_security` | `run()` kwargs; `settings` is gone | | `@mcp.tool()` | — | **unchanged** | ## Verification **1. `tools/list` is byte-for-byte identical** between mcp 1.27.1 and 2.x — 6 tools, same names, same `inputSchema` properties and `required` lists, dumped with `by_alias=True` so the comparison is in wire format. This was the real risk: 2.x moved the protocol types to snake_case internally, and tool schemas decide routing. ``` $ diff <(json.tool tools-1x.json) <(json.tool tools-2x.json) IDENTICAL — tools/list byte-for-byte unchanged ``` **2. Boots for real** over streamable-http with `stateless_http=True` and DNS-rebinding protection disabled, answering `initialize` 200 with the same capability set: ``` {"result":{"capabilities":{"experimental":{},"prompts":{"listChanged":false}, "resources":{"listChanged":false,"subscribe":false},"tools":{"listChanged":false}}, "protocolVersion":"2025-06-18","serverInfo":{"name":"crop_seed-docs","version":""}}} ``` **3. CI** runs `import docs_mcp.server` inside the built image before pushing (added in #22). ## One behavior change `serverInfo.version` is `""` under 2.x instead of the SDK version — 2.x stopped defaulting it. Cosmetic; left as-is rather than inventing a version scheme. Say the word if you'd rather it carry the corpus tag. ## Dependency notes 2.x has no `[fastmcp]` extra, swaps `httpx` for `httpx2`, and requires `opentelemetry-api`. Our own `httpx>=0.27` is unaffected — `httpx2` is a separate distribution. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FnVuG79cYPcRLTp4pC8ujR
claude added 1 commit 2026-09-10 14:22:16 -04:00
Pilot for moving the fleet off mcp 1.x before it goes away. The port is
three mechanical changes plus docs:

  - import:      mcp.server.fastmcp.FastMCP -> mcp.server.mcpserver.MCPServer
  - constructor: transport options moved off it; MCPServer(name) only
  - run():       host/port/stateless_http/transport_security are now kwargs,
                 and `mcp.settings` no longer exists

`@mcp.tool()` is unchanged — all six tools keep their decorators and
signatures untouched.

Verified, not assumed:

1. tools/list is byte-for-byte identical between mcp 1.27.1 and 2.x
   (6 tools, same names, same inputSchema properties and required lists,
   dumped with by_alias=True so the comparison is in wire format). This
   was the real risk: 2.x switched the protocol types to snake_case
   internally, and tool schemas are what decide routing.
2. The server boots for real over streamable-http with stateless_http=True
   and DNS-rebinding protection disabled, and answers initialize 200 with
   the same capability set.
3. CI now runs `import docs_mcp.server` inside the built image before push.

One behavior change: an unversioned server reports serverInfo.version ""
under 2.x instead of the SDK version, so Drawbar will see an empty version
string. Cosmetic — left as-is rather than inventing a version scheme.

Dependency note: 2.x has no [fastmcp] extra, replaces httpx with httpx2,
and requires opentelemetry-api. Our own httpx>=0.27 pin is unaffected —
httpx2 is a separate distribution.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01FnVuG79cYPcRLTp4pC8ujR
claude merged commit 0eb8ad0db6 into main 2026-09-10 14:22:42 -04:00
claude deleted branch feat/mcp-2x 2026-09-10 14:22:42 -04:00
Sign in to join this conversation.