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.
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
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 main2026-09-10 19:50:11 -04:00
claude
deleted branch mcp-2x-migration2026-09-10 19:50:11 -04:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements #4. Ports this server off
mcp.server.fastmcp(removed in mcp 2.0.0) and lifts the<2pin, following the shapeseed-mcpalready shipped.Changes
requirements.txtmcp[fastmcp]>=1.0.0,<2→mcp>=2,<3(2.x has no[fastmcp]extra)docs_mcp/server.pyFastMCP→MCPServer;mcp.settingsis gone, so host/port/stateless_http/transport_securitybecomerun()kwargs.gitea/workflows/{refresh,image-only}.ymlCLAUDE.md,README.md@mcp.tool()decorators and every handler signature are untouched. There is nomcp.get_context()call in this repo, so the 2.xContextchange does not apply.One deliberate behaviour change:
stateless_httpis gated tostreamable-http. Under 1.x itwas a constructor arg and therefore applied to
ssetoo;sseis a dev-only path here, and thismatches seed-mcp.
PLAN.mdstill describes the 1.x API and is left alone — it tracks the upstream template, notthis 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/listin 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-mcpresult the issue predicted.
initialize→ 200, and nomcp-session-idheader, which confirmsstateless_httpis actually active on the new call path.tools/listand a realtools/callboth succeed over the wire.
initialize+tools/listboth fine.corpus_statusreads thebaked indexes (4,164 labels / 216,467 chunks). A live
search_docs('soybean herbicide for waterhemp')returns real hits withmode=hybrid-rrfagainst 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.versionnow reports""instead of the SDK version — 2.x behaviour for anunversioned server. Cosmetic, but visible to clients.
httpx2(2.12.0) andopentelemetry-api(1.44.0) arrive as 2.x deps and coexist with theexisting
httpx0.28.1 pin, as the issue predicted — separate distributions.non-importing image impossible, regardless of what the dep does next.
⚠️ This will not reach production on merge.
chem-mcpis pinned to:corpus-2026.05.24in Drawbar's compose — see Drawbar/drawbar-backend#339.Closes #4
🤖 Generated with Claude Code
https://claude.ai/code/session_01FFBDnRWHispovmJVK9rXc9
<2pin) 83af1ce3f0