deps: migrate to mcp 2.x (lift the <2 pin) #6

Merged
claude merged 1 commits from mcp-2x-migration into main 2026-09-10 19:50:11 -04:00
Contributor

Implements #4. Ports this server off mcp.server.fastmcp (removed in mcp 2.0.0) and lifts the
<2 pin, following the shape seed-mcp already shipped.

Changes

File What
requirements.txt mcp[fastmcp]>=1.0.0,<2 → mcp>=2,<3 (2.x has no [fastmcp] extra)
docs_mcp/server.py FastMCP → MCPServer; mcp.settings is gone, so host/port/stateless_http/transport_security become run() kwargs
.gitea/workflows/{refresh,image-only}.yml import smoke-test against the built image, before the push
CLAUDE.md, README.md correct the FastMCP references

@mcp.tool() decorators and every handler signature are untouched. There is no
mcp.get_context() call in this repo, so the 2.x Context change does not apply.

One deliberate behaviour change: stateless_http is gated to streamable-http. Under 1.x it
was a constructor arg and therefore applied to sse too; sse is a dev-only path here, and this
matches seed-mcp.

PLAN.md still describes the 1.x API and is left alone — it tracks the upstream template, not
this repo. That is now called out in CLAUDE.md so the next agent doesn't copy from it.

Verification — mcp 1.27.1 → 2.2.0

The risk in this port is tool routing, since 2.x moved the protocol types to snake_case
internally and tool schemas decide routing. So that is what got tested hardest:

  • tools/list in wire format is byte-for-byte identical. Same md5 on both sides
    (cea0326f59b55abbbf47c9679252d38f), all 5 tools present and unchanged — search_docs,
    get_page, list_versions, corpus_status, crop_chem_api_lessons. Matches the seed-mcp
    result the issue predicted.
  • streamable-http: initialize → 200, and no mcp-session-id header, which confirms
    stateless_http is actually active on the new call path. tools/list and a real tools/call
    both succeed over the wire.
  • stdio: initialize + tools/list both fine.
  • Image: built, and the new CI smoke command passes against it. corpus_status reads the
    baked indexes (4,164 labels / 216,467 chunks). A live search_docs('soybean herbicide for waterhemp') returns real hits with mode=hybrid-rrf against Ollama on .0.125.

No eval numbers, deliberately: this touches transport and packaging only. The chunker,
embedder, Chroma/BM25 stores, RRF and the reranker are all untouched, and the live search above
confirms retrieval still runs end-to-end.

Notes

  • serverInfo.version now reports "" instead of the SDK version — 2.x behaviour for an
    unversioned server. Cosmetic, but visible to clients.
  • httpx2 (2.12.0) and opentelemetry-api (1.44.0) arrive as 2.x deps and coexist with the
    existing httpx 0.28.1 pin, as the issue predicted — separate distributions.
  • The CI smoke-test is the durable half of this PR. It is what makes a green build that ships a
    non-importing image impossible, regardless of what the dep does next.

⚠️ This will not reach production on merge. chem-mcp is pinned to
:corpus-2026.05.24 in Drawbar's compose — see Drawbar/drawbar-backend#339.

Closes #4

🤖 Generated with Claude Code

https://claude.ai/code/session_01FFBDnRWHispovmJVK9rXc9

Implements #4. Ports this server off `mcp.server.fastmcp` (removed in mcp 2.0.0) and lifts the `<2` pin, following the shape `seed-mcp` already shipped. ## Changes | File | What | |---|---| | `requirements.txt` | `mcp[fastmcp]>=1.0.0,<2` → `mcp>=2,<3` (2.x has no `[fastmcp]` extra) | | `docs_mcp/server.py` | `FastMCP` → `MCPServer`; `mcp.settings` is gone, so host/port/`stateless_http`/`transport_security` become `run()` kwargs | | `.gitea/workflows/{refresh,image-only}.yml` | import smoke-test against the built image, before the push | | `CLAUDE.md`, `README.md` | correct the FastMCP references | `@mcp.tool()` decorators and every handler signature are untouched. There is no `mcp.get_context()` call in this repo, so the 2.x `Context` change does not apply. One deliberate behaviour change: **`stateless_http` is gated to `streamable-http`**. Under 1.x it was a constructor arg and therefore applied to `sse` too; `sse` is a dev-only path here, and this matches seed-mcp. `PLAN.md` still describes the 1.x API and is left alone — it tracks the upstream template, not this repo. That is now called out in CLAUDE.md so the next agent doesn't copy from it. ## Verification — mcp 1.27.1 → 2.2.0 The risk in this port is **tool routing**, since 2.x moved the protocol types to snake_case internally and tool schemas decide routing. So that is what got tested hardest: - **`tools/list` in wire format is byte-for-byte identical.** Same md5 on both sides (`cea0326f59b55abbbf47c9679252d38f`), all 5 tools present and unchanged — `search_docs`, `get_page`, `list_versions`, `corpus_status`, `crop_chem_api_lessons`. Matches the seed-mcp result the issue predicted. - **streamable-http**: `initialize` → **200**, and **no `mcp-session-id` header**, which confirms `stateless_http` is actually active on the new call path. `tools/list` and a real `tools/call` both succeed over the wire. - **stdio**: `initialize` + `tools/list` both fine. - **Image**: built, and the new CI smoke command passes against it. `corpus_status` reads the baked indexes (4,164 labels / 216,467 chunks). A live `search_docs('soybean herbicide for waterhemp')` returns real hits with `mode=hybrid-rrf` against Ollama on `.0.125`. **No eval numbers**, deliberately: this touches transport and packaging only. The chunker, embedder, Chroma/BM25 stores, RRF and the reranker are all untouched, and the live search above confirms retrieval still runs end-to-end. ## Notes - `serverInfo.version` now reports `""` instead of the SDK version — 2.x behaviour for an unversioned server. Cosmetic, but visible to clients. - `httpx2` (2.12.0) and `opentelemetry-api` (1.44.0) arrive as 2.x deps and coexist with the existing `httpx` 0.28.1 pin, as the issue predicted — separate distributions. - The CI smoke-test is the durable half of this PR. It is what makes a green build that ships a non-importing image impossible, regardless of what the dep does next. ⚠️ **This will not reach production on merge.** `chem-mcp` is pinned to `:corpus-2026.05.24` in Drawbar's compose — see Drawbar/drawbar-backend#339. Closes #4 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FFBDnRWHispovmJVK9rXc9
claude added 1 commit 2026-09-10 19:48:46 -04:00
mcp 2.0.0 removed `mcp.server.fastmcp`; the `<2` ceiling in 23c7a3d
stopped the bleeding but left this server on the previous major.
Ports to the 2.x API, following the same shape seed-mcp shipped.

- requirements.txt: `mcp[fastmcp]>=1.0.0,<2` -> `mcp>=2,<3` (2.x has
  no [fastmcp] extra)
- server.py: FastMCP -> mcp.server.mcpserver.MCPServer; `mcp.settings`
  is gone, so host/port/stateless_http/transport_security are now
  run() kwargs
- stateless_http is gated to streamable-http. Under 1.x it was a
  constructor arg and so applied to sse too; sse is a dev-only path
  here, and this matches seed-mcp.
- CI: both workflows now run `python -c "import docs_mcp.server"`
  against the built image before pushing it. This is the durable
  half — a green build can no longer ship a non-importing image.
- CLAUDE.md / README.md: correct the FastMCP references. PLAN.md is
  left alone; it tracks the upstream template, not this repo.

Verification (mcp 1.27.1 -> 2.2.0):
- tools/list dumped in wire format is byte-for-byte identical,
  md5 cea0326f59b55abbbf47c9679252d38f both sides. All 5 tools
  (search_docs, get_page, list_versions, corpus_status,
  crop_chem_api_lessons) unchanged, so routing is unaffected.
- streamable-http: `initialize` -> 200, no mcp-session-id header
  (stateless_http confirmed active); tools/list and a real
  tools/call over the wire both succeed.
- stdio: initialize + tools/list both fine.
- Image built; the new CI smoke command passes against it.
  corpus_status reads the baked indexes (4,164 labels / 216,467
  chunks) and a live `search_docs` returns hits with mode=hybrid-rrf
  against Ollama on .0.125.

No eval numbers: this touches transport and packaging only. The
chunker, embedder, Chroma/BM25 stores, RRF and the reranker are all
untouched, and the live search above confirms retrieval still runs.

Note: `serverInfo.version` now reports "" instead of the SDK version
(2.x behaviour for an unversioned server). Cosmetic, but visible to
clients. httpx2 + opentelemetry-api come in as 2.x deps and coexist
with the existing httpx 0.28.1 pin, as expected.

Closes #4

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01FFBDnRWHispovmJVK9rXc9
claude merged commit e390655640 into main 2026-09-10 19:50:11 -04:00
claude deleted branch mcp-2x-migration 2026-09-10 19:50:11 -04:00
Sign in to join this conversation.