This guide covers the breaking changes introduced in v2 of the MCP Python SDK and how to update your code.
Version 2 of the MCP Python SDK introduces several breaking changes to improve the API, align with the MCP specification, and provide better type safety.
The deprecated streamablehttp_client function has been removed. Use streamable_http_client instead.
Before (v1):
from mcp.client.streamable_http import streamablehttp_client
async with streamablehttp_client(
url="http://localhost:8000/mcp",
headers={"Authorization": "Bearer token"},
timeout=30,
sse_read_timeout=300,
auth=my_auth,
) as (read_stream, write_stream, get_session_id):
...After (v2):
import httpx
from mcp.client.streamable_http import streamable_http_client
# Configure headers, timeout, and auth on the httpx.AsyncClient
http_client = httpx.AsyncClient(
headers={"Authorization": "Bearer token"},
timeout=httpx.Timeout(30, read=300),
auth=my_auth,
)
async with http_client:
async with streamable_http_client(
url="http://localhost:8000/mcp",
http_client=http_client,
) as (read_stream, write_stream, get_session_id):
...The headers, timeout, sse_read_timeout, and auth parameters have been removed from StreamableHTTPTransport. Configure these on the httpx.AsyncClient instead (see example above).
The following deprecated type aliases and classes have been removed from mcp.types:
| Removed | Replacement |
|---|---|
Content |
ContentBlock |
ResourceReference |
ResourceTemplateReference |
Before (v1):
from mcp.types import Content, ResourceReferenceAfter (v2):
from mcp.types import ContentBlock, ResourceTemplateReferenceThe deprecated args parameter has been removed from ClientSessionGroup.call_tool(). Use arguments instead.
Before (v1):
result = await session_group.call_tool("my_tool", args={"key": "value"})After (v2):
result = await session_group.call_tool("my_tool", arguments={"key": "value"})The deprecated cursor parameter has been removed from the following ClientSession methods:
list_resources()list_resource_templates()list_prompts()list_tools()
Use params=PaginatedRequestParams(cursor=...) instead.
Before (v1):
result = await session.list_resources(cursor="next_page_token")
result = await session.list_tools(cursor="next_page_token")After (v2):
from mcp.types import PaginatedRequestParams
result = await session.list_resources(params=PaginatedRequestParams(cursor="next_page_token"))
result = await session.list_tools(params=PaginatedRequestParams(cursor="next_page_token"))The mount_path parameter has been removed from FastMCP.__init__(), FastMCP.run(), FastMCP.run_sse_async(), and FastMCP.sse_app(). It was also removed from the Settings class.
This parameter was redundant because the SSE transport already handles sub-path mounting via ASGI's standard root_path mechanism. When using Starlette's Mount("/path", app=mcp.sse_app()), Starlette automatically sets root_path in the ASGI scope, and the SseServerTransport uses this to construct the correct message endpoint path.
Transport-specific parameters have been moved from the FastMCP constructor to the run(), sse_app(), and streamable_http_app() methods. This provides better separation of concerns - the constructor now only handles server identity and authentication, while transport configuration is passed when starting the server.
Parameters moved:
host,port- HTTP server bindingsse_path,message_path- SSE transport pathsstreamable_http_path- StreamableHTTP endpoint pathjson_response,stateless_http- StreamableHTTP behaviorevent_store,retry_interval- StreamableHTTP event handlingtransport_security- DNS rebinding protection
Before (v1):
from mcp.server.fastmcp import FastMCP
# Transport params in constructor
mcp = FastMCP("Demo", json_response=True, stateless_http=True)
mcp.run(transport="streamable-http")
# Or for SSE
mcp = FastMCP("Server", host="0.0.0.0", port=9000, sse_path="/events")
mcp.run(transport="sse")After (v2):
from mcp.server.fastmcp import FastMCP
# Transport params passed to run()
mcp = FastMCP("Demo")
mcp.run(transport="streamable-http", json_response=True, stateless_http=True)
# Or for SSE
mcp = FastMCP("Server")
mcp.run(transport="sse", host="0.0.0.0", port=9000, sse_path="/events")For mounted apps:
When mounting FastMCP in a Starlette app, pass transport params to the app methods:
# Before (v1)
mcp = FastMCP("App", json_response=True)
app = Starlette(routes=[Mount("/", app=mcp.streamable_http_app())])
# After (v2)
mcp = FastMCP("App")
app = Starlette(routes=[Mount("/", app=mcp.streamable_http_app(json_response=True))])Note: DNS rebinding protection is automatically enabled when host is 127.0.0.1, localhost, or ::1. This now happens in sse_app() and streamable_http_app() instead of the constructor.
The uri field on resource-related types now uses str instead of Pydantic's AnyUrl. This aligns with the MCP specification schema which defines URIs as plain strings (uri: string) without strict URL validation. This change allows relative paths like users/me that were previously rejected.
Before (v1):
from pydantic import AnyUrl
from mcp.types import Resource
# Required wrapping in AnyUrl
resource = Resource(name="test", uri=AnyUrl("users/me")) # Would fail validationAfter (v2):
from mcp.types import Resource
# Plain strings accepted
resource = Resource(name="test", uri="users/me") # Works
resource = Resource(name="test", uri="custom://scheme") # Works
resource = Resource(name="test", uri="https://example.com") # WorksIf your code passes AnyUrl objects to URI fields, convert them to strings:
# If you have an AnyUrl from elsewhere
uri = str(my_any_url) # Convert to stringAffected types:
Resource.uriReadResourceRequestParams.uriResourceContents.uri(and subclassesTextResourceContents,BlobResourceContents)SubscribeRequestParams.uriUnsubscribeRequestParams.uriResourceUpdatedNotificationParams.uri
The ClientSession.read_resource(), subscribe_resource(), and unsubscribe_resource() methods now accept both str and AnyUrl for backwards compatibility.
If you encounter issues during migration:
- Check the API Reference for updated method signatures
- Review the examples for updated usage patterns
- Open an issue on GitHub if you find a bug or need further assistance