MCP Python SDK v2 shipped stable on July 27. If you have a Python MCP server — including one built last week following a tutorial — you have breaking changes waiting. FastMCP
is gone, import paths moved, and every field flipped to snake_case. The good news: most migrations take five minutes. Here’s exactly what to change.
The Rename That Breaks Everything First #
The most immediate change is the class rename: FastMCP
is now MCPServer
. There is no deprecation shim. There is no backward-compatible alias. The old import path simply does not exist in v2. The official migration guide covers the full list — but the rename is the one that breaks your server at startup, before anything else runs.
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-server")
python
from mcp.server.mcpserver import MCPServer, Context
mcp = MCPServer("my-server")
Everything under mcp.server.fastmcp.*
moved to mcp.server.mcpserver.*
. The ctx.fastmcp
property is now ctx.mcp_server
. One more detail worth catching: the default server name string changes from "FastMCP"
to "mcp-server"
— pass an explicit name if you rely on serverInfo.name
in your client.
snake_case Is Now Everywhere #
All Pydantic model fields switched from camelCase to snake_case for Python access. The JSON wire format is unchanged — but the Python attributes are not. This one hides well: your server may start fine and only fail at runtime when code reads a tool or resource field.
tool.inputSchema
→tool.input_schema
result.isError
→result.is_error
listing.nextCursor
→listing.next_cursor
content.mimeType
→content.mime_type
If you serialize models for wire format, pass by_alias=True
: tool.model_dump(by_alias=True, mode="json")
still produces the camelCase JSON that clients expect.
Context Injection Replaces get_context() #
Handler context is now injected as an explicit parameter. mcp.get_context()
is removed. The fix is a one-line signature change — but it’s pervasive if your tools use progress reporting or logging:
@mcp.tool()
async def search(query: str) -> str:
ctx = mcp.get_context()
await ctx.report_progress(0, 100)
return f"Results for: {query}"
php
@mcp.tool()
async def search(query: str, ctx: Context) -> str:
await ctx.report_progress(0, 100)
return f"Results for: {query}"
The @mcp.tool()
decorator stays. Only the context acquisition changes.
Transport Parameters Move to run() #
Constructor-level transport arguments no longer exist. Move them to the run()
call:
mcp = FastMCP("Demo", json_response=True, stateless_http=True)
mcp.run(transport="streamable-http")
mcp = MCPServer("Demo")
mcp.run(transport="streamable-http", json_response=True, stateless_http=True)
Not Ready Yet? Pin Your Version #
If your team cannot migrate immediately, pin before an unpinned install auto-upgrades:
mcp>=1.28.1,<2
This is exactly what IBM’s mcp-context-forge team did. SDK v1.x continues to receive critical bug fixes and security patches. The deprecated features — sampling, roots, logging — have a one-year grace period, and a v2 server falls back to legacy behavior for older clients. Pinning is not a cop-out; it’s responsible dependency management while your team plans the migration.
Why FastMCP Had to Go #
The rename reflects a deeper protocol shift. The MCP 2026-07-28 specification eliminates the initialize
handshake and session IDs entirely — any server instance can handle any request. Round-robin load balancers work without sticky sessions. Serverless deployments become straightforward. “FastMCP” made sense as a high-level layer on top of session-based MCP. Once sessions disappear from the protocol, all MCP servers are stateless by design — the name stops meaning anything, and MCPServer
is simply what it is.
Migration Checklist #
-
Update package:
pip install "mcp>=2.0" -
Replace all
FastMCP
imports →MCPServer
frommcp.server.mcpserver
- Update any
mcp.server.fastmcp.*
submodule paths - Replace
ctx = mcp.get_context()
withctx: Context
parameter in handler signatures - Search codebase for
inputSchema
,isError
,nextCursor
,mimeType
— rename each - Move constructor transport params to
run()
- If using HTTP transport directly: swap
httpx
forhttpx2
If you built a v1 server from our MCP Server in Python guide, the structure still holds — apply these changes on top. For anything beyond the mechanical renames, the full v2 changelog covers the low-level server API, union type adapter changes, and the new multi-round-trip request pattern.