Bump mcp from 1.28.1 to 2.0.0 - #113
Conversation
1fd72fe to
bc482ad
Compare
Bumps [mcp](https://github.com/modelcontextprotocol/python-sdk) from 1.28.1 to 2.0.0. - [Release notes](https://github.com/modelcontextprotocol/python-sdk/releases) - [Changelog](https://github.com/modelcontextprotocol/python-sdk/blob/main/RELEASE.md) - [Commits](modelcontextprotocol/python-sdk@v1.28.1...v2.0.0) --- updated-dependencies: - dependency-name: mcp dependency-version: 2.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
bc482ad to
1b28f37
Compare
The bump alone does not build: 2.0 renamed `mcp.server.fastmcp` to `mcp.server.mcpserver` and `FastMCP` to `MCPServer`, so every test errored at collection. Around that rename sit four smaller moves. `host` and `port` are stated when serving rather than when constructing, which lets `build_mcp_server` drop a `port` that an in-process caller only ever passed to satisfy the signature. `streamable_http_path` and `transport_security` likewise belong to `streamable_http_app`. `Context` takes two type parameters. And `OAuthClientInformationFull.client_id` is no longer optional, so a guard against it went dead. `request_ctx` is gone entirely, and that one is a redesign rather than a rename. Per-agent `tools/list` filtering read the requesting agent out of that contextvar from inside a `FastMCP.list_tools` override. In 2.0 the per-request context is handed to middleware explicitly, so the filter is middleware now, which is the supported seam rather than an override reaching for a global. None of it was tested. The suite passed on the migration before this commit's test file existed, because nothing exercised the filter: a filter that silently stopped filtering would show every agent every other agent's tools and no test would have failed. The decision is now a pure `visible_tools`, covered for the tools it hides, the communication tools it must never hide, and both ways an agent is identified. `exchange_identity_assertion` is new on the provider contract. The default implementation refuses, and this one refuses explicitly, so that reading the class says the grant is unsupported rather than leaving it to a base class. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Coverage reportClick to see where and how coverage changed
This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Verified against a running server rather than inferred: `/mcp/register` refuses a client that asks for the jwt-bearer grant, because the grant needs a pre-registered confidential client and this server registers dynamically, and `/mcp/token` refuses the grant type before any provider method runs. The implementation is there to satisfy the contract, and the docstring now says so rather than leaving the next reader to find out. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| self._runtime = runtime | ||
| def is_tool_allowed(self, agent_id: str, tool_name: str) -> bool: | ||
| """Return whether ``agent_id`` may call ``tool_name``.""" | ||
| ... |
The filter was dead code. mcp 2.0 gives middleware the serialized JSON-RPC result, a dict whose tools are dicts, not the `ListToolsResult` the handler returned, so `isinstance(result, ListToolsResult)` was false on every request. The middleware returned the result untouched, `is_tool_allowed` was never asked anything, and an agent authorized for no scenario tools received all of them. Measured over Streamable HTTP: `stabilization_engineer`, whose tool_names hold only `send_message`, could see `stabilize_veyru`. It failed open, and silently: the one warning that would have shown it sat behind the same type guard. The dict shape is now handled, the model shape still is, and a third shape is logged as an error naming what every agent can suddenly see, rather than passed through as if nothing happened. The tests are the reason this got as far as it did. Two of them assert `visible_tools` and `requesting_agent_id` behave, and both did: the functions were right and nothing called them. So the new ones drive a real `MCPServer` through a real `ClientSession` over in-memory streams, ask as two agents with different allowlists, and assert the authorizer was questioned at all. In-memory streams rather than a socket, so there is no port to bind and no startup to wait on. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Do not merge yet — this needs a prerelease in the agent hot pathTwo findings from review, both reproduced. The first is fixed on the branch; the second is a call for a human. Fixed:
|
| package | this branch | main |
|---|---|---|
fastmcp-slim |
4.0.0b3 | 3.4.2 |
mcp |
2.0.0 | 1.28.1 |
httpx2 |
2.10.0 | (absent) |
httpx-sse |
(absent) | present |
fastmcp-slim is pydantic-ai's MCP client: the path every agent takes for every tool call in every run. Merging this puts a prerelease there, and swaps the HTTP stack underneath, ahead of pydantic-ai supporting mcp 2.x.
Recommendation: hold. Keep the migration — it will be needed — and revisit when fastmcp-slim ships a stable 4.x. An ignore rule for mcp majors would stop dependabot re-proposing this in the meantime.
Verification as it stands
make lint-server clean; 549 passed, 1 skipped in CI's environment (uv sync --all-groups --extra evals). The OAuth flow was also exercised by hand against a running server on 2.0 — discovery, dynamic registration, PKCE authorize, token and refresh exchange, 401 on a missing or bogus bearer, an authenticated tools/call, and the REST bearer fallback. Note that the repository has no automated auth coverage at all, so none of that is guarded by CI.
What this is waiting on
So the lock here takes Upgrading pydantic-ai does not avoid it. Its latest release still declares the same That range already admits a stable What to checkThe signal is curl -s https://pypi.org/pypi/fastmcp-slim/json \
| python3 -c "import json,sys; print(json.load(sys.stdin)['info']['version'])"That prints the latest stable version. While it prints There is no 4.0 milestone in the fastmcp repo and no due date on any milestone, so the only
On 3.0 they went beta to rc in 5 days and rc to stable in 6, cutting three rcs. Staying on When it lands, re-run this
Validation already done, so it need not be repeatedAgainst
One gap left: the new defensive branch that logs an ERROR when the 🤖 Generated with Claude Code |
Bumps mcp from 1.28.1 to 2.0.0.
Release notes
Sourced from mcp's releases.
... (truncated)
Commits
6f69a37Present v2 as the stable release across the README, docs, and policies (#3178)78e6fbbServe v2 docs at the site root, with permanent per-major paths (#3176)af06330Remove unused StreamableHTTPTransport.get_session_id() (#3205)68ca87eDocument the two-line release process for stable v2 (#3179)c9c431bExpose the middleware chain on MCPServer and stop sending unrequested change ...528e366Fail fast on server-to-client requests in JSON-response mode instead of hangi...27f5cc7Remove unused mcpserver.exceptions.ValidationError (#3199)89c5e70Gate log notifications on the per-request log-level opt-in at 2026-07-28 (#3198)b61ce38docs: fix off-by-one hl_lines in apps.md (#3196)b7c9a91Add mcp.types as a permanent alias for mcp_types (#3190)