The default moved
The Python MCP SDK changed four defaults, and each one can stop a working server
Version 2.2.0 of the reference SDK confines redirects to the endpoint’s origin, closes idle sessions after thirty minutes, caps a server at ten thousand sessions and rejects an authorization server whose issuer is not its own. None of these is filed under fixes.
The Model Context Protocol project released Python SDK 2.2.0 on 7 September with four behaviour changes, each one a default that used to be looser.
This is a trust boundary redrawn in code, and the cost lands on deployments that never knew they relied on the old one.
Of the four, the redirect rule is the one most likely to fail loudly. The HTTP client, whether created through Client, streamable_http_client or sse_client, now follows a redirect only when it stays on the same scheme, host and port, or upgrades http to https on the same host. A redirect anywhere else is not followed: the call fails with MCPError and the session stays usable, while an SSE connect fails with httpx2.HTTPStatusError. The follow_redirects setting on an httpx2.AsyncClient you pass in is no longer consulted for MCP requests, so the trailing-slash workaround people added is now dead code. The OAuth providers apply the same rule to their own requests.
The second is quieter. A stateful Streamable HTTP session with nothing in flight for thirty minutes is closed, and the client’s next request gets a 404 rather than an answer and has to initialize again. Clients that hold the GET stream open, which the SDK’s own Client does, are not affected; stateless servers and connections on the 2026-07-28 protocol revision are not either. A server also now holds at most 10,000 sessions at once, up from no limit at all, and answers new sessions with a 503 beyond that. Both limits switch off with session_idle_timeout=None and max_sessions=None on mcp.run, streamable_http_app and run_streamable_http_async.
The third closes a gap on the legacy authorization path. For servers without protected resource metadata, the client now rejects authorization server metadata whose issuer is not the server’s own origin, with OAuthFlowError: Authorization server metadata issuer mismatch; the protected-resource path has done this since v2.0. A 403 that is not an insufficient_scope challenge is returned to the caller instead of retried, and if protected resource metadata cannot be fetched because of a 5xx or a 429, the flow stops instead of falling back to the legacy endpoints.
The case against these defaults is that they break things that worked. That is the point, and the notes say so plainly: if the other URL is the server you meant, use it as the endpoint URL. A redirect a client follows off-origin is a way to hand a bearer token to a host nobody named, and a session that never expires is a resource an unauthenticated caller can consume until the process dies. The looser behaviour was never a feature. It was the absence of a decision.
Two deprecation warnings arrive with the release and become errors in v3.0: constructing ClientCredentialsOAuthProvider or PrivateKeyJWTOAuthProvider without issuer=, and AuthSettings with resource_server_url set but validate_token_resource left unset, which v3.0 will default to True. The tasks extension, DPoP and the jwt-bearer grant are still not implemented; the project’s roadmap tracks them.
The verdict: pin before you upgrade, then upgrade on purpose. Grep your client code for follow_redirects and your server config for anything that assumed sessions were immortal, and read the OAuth section if you serve behind a gateway that rewrites hosts. One item filed under fixes will also bite: a $ref inside a tool’s outputSchema now resolves within that schema only, and an unresolvable one raises RuntimeError: Invalid schema for tool.
What it changes If you run pip install -U mcp this week, check three things first: does any client rely on a cross-origin redirect, does any server hold sessions idle past thirty minutes, and does your OAuth setup name its issuer. Set session_idle_timeout and max_sessions deliberately rather than by default.
Primary source read