pip install -U mcp. Docs: https://py.sdk.modelcontextprotocol.io/
A few defaults changed in this release. If you run a server or client on 2.x, skim these first:
Behaviour changes
HTTP client redirects are only followed within the endpoint's origin (#3397)
Client("https://..."),streamable_http_clientandsse_clientfollow a redirect only if it stays on the same scheme, host and port (or upgradeshttptohttpson the same host).- A redirect anywhere else is not followed: the call fails with
MCPErrorand the session stays usable (an SSE connect fails withhttpx2.HTTPStatusError). If that other URL is the server you meant, use it as the endpoint URL. - The
follow_redirectssetting on anhttpx2.AsyncClientyou pass in is no longer used for MCP requests, so you don't need it for the trailing-slash redirect any more. - The OAuth providers apply the same rule to their own requests.
Idle Streamable HTTP sessions now expire (legacy <=2025-11-25 spec( (#3395)
- A stateful session with nothing in flight for 30 minutes is closed. The client's next request gets a 404 and it has to initialize again.
- Clients that keep the GET stream open (the SDK's
Clientdoes) are not affected. Neither are stateless servers or 2026-07-28 connections. - A server also holds at most 10 000 sessions at once; beyond that, new sessions get a 503.
- To turn either off:
mcp.run(transport="streamable-http", session_idle_timeout=None, max_sessions=None)(also onstreamable_http_app()andrun_streamable_http_async()).
The OAuth client checks the authorization server's issuer on the legacy path too (#3398)
- For servers without protected resource metadata, authorization server metadata whose
issuerisn't the server's own origin is now rejected withOAuthFlowError: Authorization server metadata issuer mismatch. The protected-resource-metadata path has done this since 2.0. - A 403 that isn't an
insufficient_scopechallenge is returned to the caller instead of retried. - If protected resource metadata can't be fetched because of a 5xx/429, the flow now stops instead of falling back to the legacy endpoints.
Two new MCPDeprecationWarnings (#3435, #3447)
ClientCredentialsOAuthProvider/PrivateKeyJWTOAuthProviderwithoutissuer=. Pass your authorization server's issuer URL; 3.0 will require it.AuthSettingswithresource_server_urlset butvalidate_token_resourceunset. Set it toTrueorFalse; 3.0 defaults it toTrue.- Both keep working as before in 2.x; this mostly matters if your tests turn warnings into errors.
New
AuthSettings.validate_token_resource: only accept tokens yourTokenVerifierreports as issued for this server (#3447).issuer=onClientCredentialsOAuthProviderandPrivateKeyJWTOAuthProvider(#3398).session_idle_timeout=andmax_sessions=on the Streamable HTTP server entry points (#3395).
Fixes
- A client
DELETEfrees its session immediately, and a refused opening request no longer leaves a session behind (#2455, #3228, #3300). $refs in a tool'soutputSchemaresolve within that schema only; an unresolvable one surfaces asRuntimeError: Invalid schema for tool ...(#3394).
Known gaps
The tasks extension (SEP-2663), DPoP (SEP-1932) and the jwt-bearer grant are not implemented yet; https://github.com/modelcontextprotocol/python-sdk/blob/main/ROADMAP.md tracks them.
What's Changed
- Gate draft PRs too and rewrite the auto-close comment by @maxisbey in #3378
- Resolve tool output-schema references within the schema document only by @maxisbey in #3394
- Expire idle Streamable HTTP sessions by default and cap concurrent sessions by @maxisbey in #3395
- Validate the authorization server metadata issuer on every discovery path by @maxisbey in #3398
- Deprecate constructing the pre-provisioned OAuth clients without an issuer by @maxisbey in #3435
- Exercise the SEP-2575 stateless probes and SEP-2243 resource/prompt headers in the conformance fixtures by @maxisbey in #3442
- Bump the github-actions group with 6 updates by @dependabot[bot] in #3424
- Move the docs-preview workflow scripts out of the YAML into .github/scripts by @maxisbey in #3446
- Skip automatic docs previews for fork PRs and drop the setup-uv retry steps by @maxisbey in #3445
- Follow redirects only within the MCP endpoint's origin by @maxisbey in #3397
- Bump pymdown-extensions from 11.0 to 11.0.1 by @dependabot[bot] in #3285
- Bump the locked versions of eight dev and test dependencies by @maxisbey in #3449
- Keep following a relative redirect when the endpoint URL carries userinfo by @maxisbey in #3450
- Add AuthSettings.validate_token_resource to check a bearer token's resource by @maxisbey in #3447
- docs: stop presenting the in-memory client as the way to connect by @maxisbey in #3443
- docs: ask for AI disclosure on comments too by @maxisbey in #3459
- docs: refresh translations, and translate pages in parallel by @maxisbey in #3458
- Replace RootModel wrappers with type aliases and TypeAdapter validation by @Kludex in #3470
Full Changelog: v2.1.1...v2.2.0