Upgrade to FastMCP 4 and MCP SDK v2 - #189
Open
zzstoatzz wants to merge 7 commits into
Open
Conversation
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
📊 ObservabilityView eval run traces in Logfire: prefect-mcp-server-evals @ b69bc85 |
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Carries #190/#191/#192 forward: main's `tool_annotations()` factory and the per-tool titles now use the SDK v2 snake_case kwarg spelling. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A branch ref moves under CI, so the same commit could build differently from one run to the next. Pin the #6738 revision explicitly instead. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Moves the server to FastMCP 4 and the MCP SDK v2. The server code itself needed almost nothing — the interesting work was everywhere else, and most of it was pre-existing breakage that the upgrade dragged into the light.
The mechanical half is what you'd expect:
mcp.typesbecame the standalonemcp_typespackage, and protocol fields went snake_case. FastMCP 4 bridges camelCase reads with a deprecation shim, but it cannot bridge camelCase construction —ToolAnnotations(readOnlyHint=...)silently becomes an unknown kwarg.tycaught those; a grep of the upgrade guide's checklist would not have.The evals then needed a second, unrelated migration.
pydantic-ai2.0 droppedMCPServer*andFastMCPToolsetforMCPToolset, which takes a transport rather than connection kwargs. That swap is mostly mechanical, but one default flips a lifecycle behavior:StdioTransportkeeps the subprocess alive between connections, so a session-scoped fixture caches a session bound to one test's event loop and every later test dies withEvent loop is closed. Worth knowing for anyone else making this move.Session state, checked rather than assumed
PrefectAuthMiddlewarestores credentials viactx.set_state, and sessionless v4 doesn't persist state across requests. This is safe:on_call_toolre-extracts from headers on every call and_prefect_client/client.pyreads them back within that same call, so it never crosses a request boundary. The eval logs confirm we're exercising the real2026-07-28era — they showMCP send server/discover, not a legacy fallback.The OAuth provider is
RemoteAuthProvideron Cloud JWKS. It doesn't mint tokens, so the v4issuer_urlchange and its one-time client re-authorization don't apply here.Two eval rubrics that were already failing on main
CI treats eval failures as
exit 0, so these sat red without a signal. The latest main run was2 failed, 18 passed; both failures are fixed here, and neither was caused by the upgrade.test_latest_release_notesfed the judge onlyrelease["body"], which omits the release title, then failed the agent for "inventing" it. It also rejected adocs.prefect.iorelease-notes link as a "non-existent docs page" — that URL returns HTTP 200, and the rubric's own text permits a docs link. The agent was right on both counts. The rubric now passes the title as authoritative material and states that either a docs or GitHub link is acceptable.test_cloud_oauth_agent_triages_unknown_workspace_failuregraded on the exact flow run name while the prompt only asked to identify the workspace and explain the failure. The fixture also creates a similarly-named deployment, so the agent reported that instead. The prompt now asks for the failing run, and the judge is told that also naming the deployment is correct.Both changes tighten the ask rather than lower the bar — the agent still has to find the right workspace, run, and failure reason. Each was run repeatedly to confirm it's stable, not merely lucky.
Also raised the client
init_timeoutto 60s on the eval toolsets: each xdist worker cold-startsuv run -m prefect_mcp_server, and importing Prefect under that much parallelism routinely exceeds the 5s default.Verified with the full suite (124 passed, 2 skipped), evals green 3 runs running, and
ruff+tyclean.This cannot merge yet.
pydantic-aicapsfastmcp-slim[client]<4, and the compat work in pydantic/pydantic-ai#6738 deliberately leaves that cap in place. Sopyproject.tomlcurrently points at that unmerged branch and carries auvoverride lifting the cap — both marked temporary in-file. When #6738 lands and the follow-up widens the range, those come out and this becomes an ordinary version bump. Tracking issue: pydantic/pydantic-ai#6661.🤖 Generated with Claude Code