When you connect a remote Model Context Protocol (MCP) server to tools like Claude, Cursor, or Claude Code, the experience is seamless: a browser window opens, you log in, and the connection is established. Behind this frictionless interface lies a robust OAuth 2.1 implementation, enhanced by specific rules from the MCP authorization specification. A recent technical breakdown by developer Usama Ejaz details this handshake using a live server at justblogged.com/mcp, revealing how modern clients negotiate access without exposing secrets in configuration files.
The Initial Handshake and Metadata Discovery
The process begins when an unauthenticated client attempts to call a protected tool, such as list_blogs. The server responds with an HTTP 401 status and a WWW-Authenticate header. This header is critical because it directs the client to the resource_metadata URL and specifies the required scope, such as blogs:manage. By following the resource_metadata link, defined in RFC 9728, the client discovers which authorization server issues tokens for that specific resource. This dynamic discovery eliminates the need for hardcoded server-specific configurations, allowing any compliant client to connect to any compliant server.
Dynamic Client Registration and PKCE Security
To obtain a client ID, the MCP spec supports two methods: Dynamic Client Registration (RFC 7591) and Client ID Metadata Documents. Dynamic registration allows the client to register itself at runtime by POSTing to a registration_endpoint, receiving a client_id and client_secret (or using none for public clients). Alternatively, the newer Client ID Metadata Document approach uses an HTTPS URL as the client ID, where the server fetches a JSON document to verify the client's domain and redirect URIs. Crucially, the flow mandates PKCE (Proof Key for Code Exchange) with the S256 method. The client generates a code verifier and challenge, ensuring that even if the authorization code is intercepted, only the original client can exchange it for an access token.
Token Exchange and Scope Implications
After the user approves the consent screen in their browser, the client exchanges the authorization code for an access token and a refresh token. This exchange includes the code_verifier and the resource parameter (RFC 8707), binding the token to the specific MCP endpoint. Subsequent requests include the Authorization: Bearer header. A significant caveat in the current implementation is the scope granularity. The example server uses a single blogs:manage scope, which grants the agent full read and write capabilities across all blogs the user can access. This simplifies the consent screen but prevents fine-grained permissions, such as read-only access or per-blog restrictions, forcing developers to rely on trust and careful prompt engineering to prevent accidental destructive actions.
Key Takeaways
- MCP relies on standard OAuth 2.1 with PKCE (S256) to secure remote connections without API keys.
- Clients dynamically discover authorization servers via
resource_metadataandauthorization_server_metadataendpoints. - Dynamic Client Registration or Client ID Metadata Documents replace manual app registration.
- Current scope implementations often lack granularity, granting broad access via single scopes like
blogs:manage.
The Bottom Line
The shift toward OAuth 2.1 and PKCE for MCP servers is a massive win for developer experience, eliminating the security pitfalls of static API keys. However, the lack of fine-grained scoping in early implementations is a dangerous compromise that requires users to place blind trust in their AI agents.