-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Description
Initial Checks
- I confirm that I'm using the latest version of MCP Python SDK
- I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
Description
build_metadata() in mcp/server/auth/routes.py hardcodes token_endpoint_auth_methods_supported to ["client_secret_post", "client_secret_basic"]:
https://github.com/modelcontextprotocol/python-sdk/blob/main/src/mcp/server/auth/routes.py#L168
This omits "none", which is a valid value defined in RFC 7591 Section 2 for public clients:
The client is a public client as defined in OAuth 2.0, Section 2.1, and does not have a client secret.
Spec Analysis
The MCP authorization spec (2025-06-18) requires support for public clients:
Authorization servers MUST implement OAuth 2.1 with appropriate security measures for both confidential and public clients.
And explicitly recommends dynamic client registration:
MCP clients and authorization servers SHOULD support the OAuth 2.0 Dynamic Client Registration Protocol (RFC7591) to allow MCP clients to obtain OAuth client IDs without user interaction.
The best practices section further recommends:
We strongly recommend that local clients implement OAuth 2.1 as a public client: 1. Utilizing code challenges (PKCE) for authorization requests...
OAuth 2.1 (draft-ietf-oauth-v2-1-13, Section 2.1) defines public clients:
Clients without credentials are called "public clients."
RFC 7591 (Section 2) defines token_endpoint_auth_method: "none" as the way to register public clients:
"none": The client is a public client as defined in OAuth 2.0, Section 2.1, and does not have a client secret.
RFC 7591 (Section 3.2.1) makes client_secret optional in registration responses, allowing it to be omitted for public clients.
RFC 8414 (Section 2) defines token_endpoint_auth_methods_supported as using values from token_endpoint_auth_method in RFC 7591 — which explicitly includes "none".
The Problem
The SDK's registration handler (register.py:54-60) already correctly supports public clients:
if client_metadata.token_endpoint_auth_method != "none":
client_secret = secrets.token_hex(32)But build_metadata() doesn't advertise "none" as a supported method. This creates a contradictory situation: the server accepts public client registrations but tells clients it doesn't support them.
Impact: Real-World Breakage
MCP clients (Claude Code, Cursor, etc.) that follow the spec:
- Discover the authorization server metadata via
/.well-known/oauth-authorization-server - See only
["client_secret_post", "client_secret_basic"]supported - Register a dynamic client requesting
token_endpoint_auth_method: "none"(public client, per MCP best practices) - Registration succeeds — no
client_secretreturned (correct per RFC 7591) - Token exchange fails — the client has no
client_secretto send, but the metadata says one is required
Claude Code shows: "Existing OAuth client information is required when exchanging an authorization code"
The browser OAuth flow completes successfully, the authorization code is received, but the token exchange never happens because the client cannot reconcile the metadata (secrets required) with the registration response (no secret issued).
Suggested Fix
Include "none" in the default token_endpoint_auth_methods_supported list, since the registration handler already supports it:
token_endpoint_auth_methods_supported=["client_secret_post", "client_secret_basic", "none"],The same change should apply to revocation_endpoint_auth_methods_supported for consistency.
A PR is available: #2261
Related Issues
- ClientAuthenticator ignores token_endpoint_auth_method="none" when client_secret is stored #1842 —
ClientAuthenticatorignorestoken_endpoint_auth_method="none"whenclient_secretis stored (client-side counterpart) - OAuth TokenHandler should check Authorization header for client credentials #1315 — OAuth TokenHandler should check Authorization header for client credentials
Python & MCP Python SDK
Python: 3.13
MCP SDK: 1.26.0