MCP OAuth Evolution: SEP-991 Simplifies Client Registration
Overview
The Problem with Dynamic Client Registration
In my previous deep-dive into MCP authorization, I analyzed how the protocol builds on OAuth 2.1 with mandatory PKCE, Resource Indicators (RFC 8707), and the "Discovery Trifecta" of RFC 7591, 8414, and 9728. Dynamic Client Registration (DCR) was positioned as the key enabler for MCP's federated ecosystem.
However, DCR has significant practical limitations:
| Challenge | Impact |
|---|---|
| Requires AS support for public registration API | Many identity providers don't offer this |
| Forces OAuth proxy infrastructure | Added complexity when AS lacks DCR |
| Manual IT involvement | End users need admin help for each registration |
The MCP ecosystem faces a unique challenge: unbounded clients connecting to unbounded servers with no prior relationship. DCR, while standardized, often requires workarounds in practice.
SEP-991: URL-Based Client Identity
On MCP's first anniversary, the team announced a simplified approach: OAuth Client ID Metadata Documents (SEP-991). This mechanism is now officially part of the 2025-11-25 stable specification.
Core Concept
Instead of registering with the Authorization Server, the client hosts its own identity document at an HTTPS URL. The client_id itself becomes the URL pointing to this metadata.
1client_id = "https://my-mcp-client.com/.well-known/oauth-client.json"
Metadata Document Structure
The client hosts a JSON document containing its OAuth metadata:
1{
2 "client_id": "https://my-mcp-client.com/.well-known/oauth-client.json",
3 "client_name": "My MCP Client",
4 "redirect_uris": [
5 "https://my-mcp-client.com/callback",
6 "http://localhost:8080/callback"
7 ],
8 "token_endpoint_auth_method": "none",
9 "grant_types": ["authorization_code"],
10 "response_types": ["code"]
11}
Key fields:
client_id: Must exactly match the document's URLclient_name: Displayed to users during authorizationredirect_uris: Allowed callback URLstoken_endpoint_auth_method:nonefor public clients,private_key_jwtfor confidential
New Client Registration Priority
The specification defines a clear priority order:
| Priority | Method | When to Use |
|---|---|---|
| 1 | Pre-registered credentials | Known client-server relationships |
| 2 | Client ID Metadata Documents | Server supports client_id_metadata_document_supported |
| 3 | Dynamic Client Registration | Fallback if AS supports RFC 7591 |
| 4 | Manual user entry | Last resort |
SEP-991 now takes precedence over DCR when supported.
Authorization Flow Comparison
Traditional DCR Flow
sequenceDiagram
participant Client
participant AS as Authorization Server
Note over Client,AS: Registration Phase
Client->>AS: POST /register {redirect_uris, client_name...}
AS->>AS: Validate & store client
AS-->>Client: {client_id: "generated-id-123", client_secret...}
Note over Client,AS: Authorization Phase
Client->>AS: GET /authorize?client_id=generated-id-123&...
SEP-991 Flow
sequenceDiagram
participant Client
participant AS as Authorization Server
participant Meta as Client Metadata URL
Note over Client,AS: No Registration Phase!
Client->>AS: GET /authorize?client_id=https://client.com/meta.json&...
AS->>Meta: GET https://client.com/meta.json
Meta-->>AS: {client_id, client_name, redirect_uris...}
AS->>AS: Validate: client_id matches URL, redirect_uri allowed
AS-->>Client: Continue authorization flow
Key difference: The client never registers. The AS fetches and validates the metadata on-demand.
Server Discovery Support
Authorization Servers declare SEP-991 support in their metadata (RFC 8414):
1{
2 "issuer": "https://auth.example.com",
3 "authorization_endpoint": "https://auth.example.com/authorize",
4 "token_endpoint": "https://auth.example.com/token",
5 "client_id_metadata_document_supported": true
6}
Clients check this field before using URL-based client IDs.
Specification Status
| Version | SEP-991 Status |
|---|---|
| 2025-03-26 | Not included |
| 2025-06-18 | Not included |
| 2025-11-25 (current) | Included |
The feature is now officially part of the stable MCP specification.
SDK Implementation Status
Not all official MCP SDKs have implemented SEP-991 yet. Here's the current support matrix:
| SDK | Language | SEP-991 Support | Notes |
|---|---|---|---|
| typescript-sdk | TypeScript | ✅ Implemented | Full CIMD support with capability detection |
| python-sdk | Python | ✅ Implemented | Full CIMD support with graceful fallback |
| rust-sdk | Rust | ❌ Not yet | Standard OAuth 2.1 + DCR only |
| go-sdk | Go | ❌ Not yet | RFC 8414 metadata only |
| kotlin-sdk | Kotlin | ❓ Unknown | OAuth support not documented |
| csharp-sdk | C# | ❌ No OAuth | Protocol implementation only |
TypeScript SDK Example
1// The SDK automatically detects server support
2const supportsUrlBasedClientId =
3 metadata?.client_id_metadata_document_supported === true;
4
5// When supported, uses URL as client_id
6if (supportsUrlBasedClientId && clientMetadataUrl) {
7 clientInformation = { client_id: clientMetadataUrl };
8}
Python SDK Example
1# Validates metadata URL format
2# "client_metadata_url must be a valid HTTPS URL with a non-root pathname"
3
4# Creates client info from metadata URL when supported
5client_information = create_client_info_from_metadata_url(
6 self.context.client_metadata_url,
7 redirect_uris=self.context.client_metadata.redirect_uris,
8)
Both SDKs implement the priority order: check client_id_metadata_document_supported → use CIMD if available → fall back to DCR.
Implementation Impact
For MCP Clients
Requirements:
- Host metadata document at HTTPS URL with path component
- Ensure
client_idin document matches the URL exactly - Include all required fields:
client_id,client_name,redirect_uris
Benefits:
- No registration API calls needed
- Self-managed identity
- Works with any AS that supports SEP-991
For Authorization Servers
Requirements:
- Implement metadata document fetching and validation
- Verify
client_idmatches fetched URL - Respect HTTP cache headers for metadata
- Declare support via
client_id_metadata_document_supported
Trust model:
- HTTPS domain ownership proves client identity
- Servers can restrict to trusted domains or allow any HTTPS client
Relationship to Existing Infrastructure
For those who followed my guide on implementing MCP OAuth with Keycloak, SEP-991 represents a significant simplification. Instead of configuring DCR endpoints and client registration flows, implementations can now:
- Host a static JSON file on the client's domain
- Configure the Authorization Server to fetch and validate client metadata URLs
- Eliminate the need for client pre-registration or DCR infrastructure
This aligns with MCP's goal of zero-configuration federation.
Summary
SEP-991 shifts the client registration paradigm:
| Aspect | DCR (Legacy) | Client ID Metadata (New) |
|---|---|---|
| Who registers | Authorization Server | Client self-hosts |
| client_id format | Server-generated string | HTTPS URL |
| Coordination needed | Yes (API call) | No |
| Identity verification | Registration-time | Fetch-time (HTTPS domain) |
The change transforms "server registers client" into "client proves identity"—a fundamental simplification for MCP's open ecosystem.
Resources
Specifications
- SEP-991 Discussion: Original proposal on GitHub
- MCP Authorization Spec (2025-11-25): Current stable specification with Client ID Metadata Documents
Related Articles
- Technical Deconstruction of MCP Authorization: Deep-dive into MCP's OAuth 2.1 foundation
- Implementing MCP OAuth 2.1 with Keycloak on AWS: Practical deployment guide