Breaking API rewrite. mcp 2.0's Server class (mcp.server.lowlevel.Server) is "rebuilt around a shared dispatcher engine" and drops decorator-based handler registration (@mcp_server.list_resources(), .read_resource(), .list_tools(), .call_tool(), etc.) entirely. Handlers are now passed as on_list_tools=, on_call_tool=, on_list_resources=, on_list_resource_templates=, on_read_resource= constructor kwargs, with a new handler signature (ctx: ServerRequestContext, params) -> Result returning wrapper objects (types.ListToolsResult, types.ListResourcesResult, types.ReadResourceResult) instead of the bare list[Tool] / list[Resource] this codebase returns today.
opencontractserver/mcp/server.py uses the old decorator pattern at 10 registration sites across two server factories: create_mcp_server() (module-level, ~lines 757, 764, 795, 797, 1024) and create_scoped_mcp_server() (~lines 1396, 1404, 1410, 1412, 1417). Since mcp_server = create_mcp_server() runs at module import time, this is not a "breaks on first request" issue — it would be an AttributeError at Django/ASGI process startup, taking down the whole MCP subsystem.
By contrast, the plain imports (mcp.server.Server, mcp.server.sse.SseServerTransport, mcp.server.stdio.stdio_server, mcp.server.streamable_http_manager.StreamableHTTPSessionManager, mcp.types.{Resource,ResourceTemplate,TextContent,Tool}) all resolve fine under 2.0.0 — the import lines alone give a false sense that the bump is safe.
Background
Dependabot PR #2207 proposed bumping
mcpfrom>=1.28.1to>=2.0.0. It was not merged — see PR #2216, which instead pinnedrequirements/base.txttomcp>=1.28.1,<2with a comment explaining why. This issue tracks the deferred migration work.Why the bump was rejected
Two independent blockers, investigated directly against the published packages (not just release notes):
Pip resolution conflict.
pydantic-ai-slim[mcp]→fastmcp-slimcapsmcp<2.0across its entire published range (verified against bothpydantic-ai-slim==1.107.1, the newest version satisfying<2, andfastmcp-slimat both its floor3.3.0and latest3.4.5). An unboundedmcp>=1.28.1pin was only ever resolving to 1.x by transitive accident; Dependabot'smcp>=2.0.0directly contradicts that cap and pip's resolver gives up withresolution-too-deep. This half is not fixable from our side — it requiresfastmcp-slimto shipmcp2.x support upstream.Breaking API rewrite.
mcp2.0'sServerclass (mcp.server.lowlevel.Server) is "rebuilt around a shared dispatcher engine" and drops decorator-based handler registration (@mcp_server.list_resources(),.read_resource(),.list_tools(),.call_tool(), etc.) entirely. Handlers are now passed ason_list_tools=,on_call_tool=,on_list_resources=,on_list_resource_templates=,on_read_resource=constructor kwargs, with a new handler signature(ctx: ServerRequestContext, params) -> Resultreturning wrapper objects (types.ListToolsResult,types.ListResourcesResult,types.ReadResourceResult) instead of the barelist[Tool]/list[Resource]this codebase returns today.opencontractserver/mcp/server.pyuses the old decorator pattern at 10 registration sites across two server factories:create_mcp_server()(module-level, ~lines 757, 764, 795, 797, 1024) andcreate_scoped_mcp_server()(~lines 1396, 1404, 1410, 1412, 1417). Sincemcp_server = create_mcp_server()runs at module import time, this is not a "breaks on first request" issue — it would be anAttributeErrorat Django/ASGI process startup, taking down the whole MCP subsystem.By contrast, the plain imports (
mcp.server.Server,mcp.server.sse.SseServerTransport,mcp.server.stdio.stdio_server,mcp.server.streamable_http_manager.StreamableHTTPSessionManager,mcp.types.{Resource,ResourceTemplate,TextContent,Tool}) all resolve fine under 2.0.0 — the import lines alone give a false sense that the bump is safe.What migration work looks like
fastmcp-slim(or its replacement in thepydantic-ai-slim[mcp]dependency chain) has shippedmcp2.x support, or drop that indirection and depend onmcp>=2.0.0directly if pydantic-ai's own MCP client integration is no longer needed here.opencontractserver/mcp/server.pyfrom decorators toon_*=constructor kwargs.(ctx: ServerRequestContext, params) -> Resultand its return type to the appropriate*Resultwrapper class.SseServerTransport,stdio_server, andStreamableHTTPSessionManagerconstruction/usage against the 2.0.0 API (imports are unchanged, but confirm no other behavioral shifts — e.g. the 4 MiB request-body cap mentioned in the 2.0.0 release notes for Streamable HTTP servers).opencontractserver/mcp/tests/(test_mcp.py,test_mcp_extended.py) plusopencontractserver/tests/test_unified_rate_limiting.py's MCP-adjacent coverage.<2pin and its explanatory comment inrequirements/base.txt.Pointers
requirements/base.txt— themcppin and comment (added in PR Pin mcp below v2 to fix pip resolution and avoid a breaking-API bump #2216)opencontractserver/mcp/server.py— the file needing migrationopencontractserver/mcp/tests/— existing MCP test coverage