This repo is UNPINNED (unpinned) — it is one rebuild away from an outage.
Landing mcp<2 is a one-line change and should happen now, independently of the
migration below.
What happens if this is left alone
seed-mcp had exactly this line. Its 2026-09-01 rebuild resolved to mcp 2.1.1 and the
container crash-looped for nine days before anyone noticed:
File ".../server.py", line 28, in <module>
from mcp.server.fastmcp import FastMCP
ModuleNotFoundError: No module named 'mcp.server.fastmcp'
mcp 2.0.0 (2026-07-28) removed mcp.server.fastmcp. zerto-docs went down the same
way on 2026-08-11. There is no upper bound here, so the next image build picks up 2.x.
Worse, the crashloop blocks its own fix: Watchtower only scans running containers,
so a crash-looping service is skipped forever and needs a manual compose pull && up -d.
The port (proven, not theoretical)
Ported end-to-end on seed-mcp in justin/seed-mcp#23. Three mechanical changes:
# 1. importfrommcp.server.mcpserverimportMCPServer# was: from mcp.server.fastmcp import FastMCPfrommcp.server.transport_securityimportTransportSecuritySettings# 2. constructor — transport options moved OFF itmcp=MCPServer(NAME)# was: FastMCP(NAME, stateless_http=True)# 3. run() — host/port/stateless_http/transport_security are now kwargs,# and `mcp.settings` no longer existsmcp.run(transport="streamable-http",host=...,port=...,stateless_http=True,transport_security=TransportSecuritySettings(enable_dns_rebinding_protection=False))
@mcp.tool() is unchanged — decorators and handler signatures stay as they are.
(If this server calls mcp.get_context(), that was removed: take ctx: Context as a
parameter instead.)
Verification this needs (do not skip)
The real risk is tool routing, since 2.x moved the protocol types to snake_case
internally and tool schemas decide routing.
Diff tools/list before and after, dumped in wire format:
This repo shares the docs_mcp/server.py lineage with seed-mcp, so the port is close to copy-paste from justin/seed-mcp#23.
> **This repo is UNPINNED (`unpinned`) — it is one rebuild away from an outage.**
> Landing `mcp<2` is a one-line change and should happen now, independently of the
> migration below.
## What happens if this is left alone
`seed-mcp` had exactly this line. Its 2026-09-01 rebuild resolved to mcp 2.1.1 and the
container crash-looped for **nine days** before anyone noticed:
```
File ".../server.py", line 28, in <module>
from mcp.server.fastmcp import FastMCP
ModuleNotFoundError: No module named 'mcp.server.fastmcp'
```
mcp 2.0.0 (2026-07-28) removed `mcp.server.fastmcp`. `zerto-docs` went down the same
way on 2026-08-11. There is no upper bound here, so the next image build picks up 2.x.
Worse, the crashloop **blocks its own fix**: Watchtower only scans *running* containers,
so a crash-looping service is skipped forever and needs a manual `compose pull && up -d`.
## The port (proven, not theoretical)
Ported end-to-end on `seed-mcp` in justin/seed-mcp#23. Three mechanical changes:
```python
# 1. import
from mcp.server.mcpserver import MCPServer # was: from mcp.server.fastmcp import FastMCP
from mcp.server.transport_security import TransportSecuritySettings
# 2. constructor — transport options moved OFF it
mcp = MCPServer(NAME) # was: FastMCP(NAME, stateless_http=True)
# 3. run() — host/port/stateless_http/transport_security are now kwargs,
# and `mcp.settings` no longer exists
mcp.run(transport="streamable-http", host=..., port=..., stateless_http=True,
transport_security=TransportSecuritySettings(enable_dns_rebinding_protection=False))
```
`@mcp.tool()` is **unchanged** — decorators and handler signatures stay as they are.
(If this server calls `mcp.get_context()`, that was removed: take `ctx: Context` as a
parameter instead.)
## Verification this needs (do not skip)
The real risk is tool routing, since 2.x moved the protocol types to snake_case
internally and tool schemas decide routing.
1. **Diff `tools/list` before and after**, dumped in wire format:
```python
tools = asyncio.run(mcp.list_tools())
json.dumps([t.model_dump(by_alias=True, mode="json", exclude_none=True) for t in tools],
indent=2, sort_keys=True)
```
On seed-mcp this came out **byte-for-byte identical** between 1.27.1 and 2.x.
2. **Boot it for real** on its production transport and confirm `initialize` returns 200.
3. **Add an import smoke-test to CI** so a green build can never ship a non-importing
image again:
```yaml
docker run --rm --entrypoint python "${IMAGE}:latest" -c "import <pkg>.server"
```
## Notes
- 2.x has **no `[fastmcp]` extra** — the requirement becomes plain `mcp>=2,<3`.
- 2.x swaps `httpx` for `httpx2` and requires `opentelemetry-api`. A local `httpx`
pin is unaffected — `httpx2` is a separate distribution.
- An unversioned server now reports `serverInfo.version` as `""` instead of the SDK
version. Cosmetic, but it is a visible change to clients.
- `zerto-msp-mcp` has run 2.x in production since 2026-07-30, and `seed-mcp` since
today — the shape is proven in this estate.
Related: justin/seed-mcp#22 (the pin), justin/seed-mcp#23 (the port), justin/zerto-docs#80.
_This repo shares the `docs_mcp/server.py` lineage with seed-mcp, so the port is close to copy-paste from justin/seed-mcp#23._
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.
What happens if this is left alone
seed-mcphad exactly this line. Its 2026-09-01 rebuild resolved to mcp 2.1.1 and thecontainer crash-looped for nine days before anyone noticed:
mcp 2.0.0 (2026-07-28) removed
mcp.server.fastmcp.zerto-docswent down the sameway on 2026-08-11. There is no upper bound here, so the next image build picks up 2.x.
Worse, the crashloop blocks its own fix: Watchtower only scans running containers,
so a crash-looping service is skipped forever and needs a manual
compose pull && up -d.The port (proven, not theoretical)
Ported end-to-end on
seed-mcpin justin/seed-mcp#23. Three mechanical changes:@mcp.tool()is unchanged — decorators and handler signatures stay as they are.(If this server calls
mcp.get_context(), that was removed: takectx: Contextas aparameter instead.)
Verification this needs (do not skip)
The real risk is tool routing, since 2.x moved the protocol types to snake_case
internally and tool schemas decide routing.
tools/listbefore and after, dumped in wire format:initializereturns 200.image again:
Notes
[fastmcp]extra — the requirement becomes plainmcp>=2,<3.httpxforhttpx2and requiresopentelemetry-api. A localhttpxpin is unaffected —
httpx2is a separate distribution.serverInfo.versionas""instead of the SDKversion. Cosmetic, but it is a visible change to clients.
zerto-msp-mcphas run 2.x in production since 2026-07-30, andseed-mcpsincetoday — the shape is proven in this estate.
Related: justin/seed-mcp#22 (the pin), justin/seed-mcp#23 (the port), justin/zerto-docs#80.
This repo shares the
docs_mcp/server.pylineage with seed-mcp, so the port is close to copy-paste from justin/seed-mcp#23.