Support pre-configured OAuth client credentials without requiring Dynamic Client Registration
Problem
Claude Code's OAuth implementation requires Dynamic Client Registration (DCR) even when a clientId is pre-configured in .mcp.json. This makes Azure AD/Entra ID (and other enterprise OAuth providers) incompatible with Claude Code's MCP client.
Enterprise OAuth providers like Azure AD do not support DCR - they require pre-registered applications with fixed client IDs. This is standard practice in enterprise environments for security and compliance reasons.
Current Behavior
Even with this configuration:
{
"mcpServers": {
"my-server": {
"type": "http",
"url": "http://localhost:5042/mcp",
"oauth": {
"clientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
}
}
}
Claude Code still:
- Fetches authorization server metadata
- Looks for
registration_endpoint - Fails with "Incompatible auth server: does not support dynamic client registration"
The pre-configured clientId is ignored.
Expected Behavior
When oauth.clientId is provided in the configuration, Claude Code should:
- Skip the DCR check entirely
- Use the pre-configured client ID for the OAuth flow
- Proceed directly to authorization
VS Code Comparison
VS Code's MCP implementation handles this correctly:
- Reads
/.well-known/oauth-protected-resourcefrom MCP server - Uses a pre-registered client ID
- Redirects to Azure AD login
- Works without any additional endpoints or workarounds
Workarounds Attempted
To make Azure AD work with Claude Code, we had to implement:
- A fake
/.well-known/oauth-authorization-serverendpoint proxying Azure AD metadata - A
/registerDCR endpoint returning pre-configured credentials - Even then, failures occur due to
resourceparameter / scope mismatches (AADSTS9010010)
This adds significant complexity and still doesn't fully work.
Proposed Solution
When oauth.clientId is present in the MCP server configuration:
- Skip checking for
registration_endpointin authorization server metadata - Skip the DCR call to
/register - Use the configured
clientIddirectly in the authorization flow
This aligns with the MCP spec which states DCR is a "SHOULD", not a "MUST":
"MCP clients and authorization servers SHOULD support the OAuth 2.0 Dynamic Client Registration Protocol"
Impact
This limitation affects:
- All Azure AD/Entra ID users
- Enterprise environments with pre-registered OAuth apps
- GitHub's official MCP server (see github/github-mcp-server#549)
- Any OAuth provider that doesn't support DCR
Related Issues
- #2527 (closed due to inactivity, but issue persists)
Environment
- Claude Code CLI
- MCP server with Azure AD/Entra ID OAuth
.mcp.jsonwith pre-configuredoauth.clientId
16 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Even in the official MCP spec, DCR is a fallback option "if supported", furthermore clients SHOULD support provided client information (first bullet):
<img width="390" height="275" alt="Image" src="https://github.com/user-attachments/assets/01bc0b54-64c2-45e8-8bce-f62e034d114f" />
I am also encountering this issue.
having the same issue as well
+1 please look into a solution for this, DCR should not a blocker for standard app registration through OATH
I am having the same issue
I have this issue too!
Throwing my hat in the ring - also affected by this
Same issue here.
Commenting because this is silly that Anthropic, the progenitor of MCP, cannot properly implement a written protocol specification in their own clients with an unlimited supply of tokens, access to frontier models, and extremely well-paid, top-tier engineers. Every part of this feature is a mature, documented spec.
Anthropic: too busy hyping random, useless agent-built code to get their own protocol working on their own clients....
There is any update on this ?
<img width="599" height="359" alt="Image" src="https://github.com/user-attachments/assets/ca37f41e-0ce2-42cb-b82d-3398cf5fa7c6" />
I found that you can manually specify client_id and client_secret for claude code instance. It might be helpful
Adding context from our duplicate (#38102) — we hit this with AWS Cognito as the OAuth provider for a custom MCP server.
Workaround that works today: Use
${ENV_VAR}expansion in.mcp.jsonheaders instead of the OAuth flow:With a helper script that completes the OAuth PKCE flow externally and exports the token as an env var. Launch
claudefrom that shell.Additional UX issue: After connecting via the env var workaround, the
/mcpmenu shows the server asconnectedbut withAuth: ✘ not authenticatedand an "Authenticate" button. Clicking "Authenticate" triggers the broken dynamic registration flow and disconnects the working session. The Authenticate option should not appear (or should be safe) when the server is already connected.Server-side setup (in case it helps others with Cognito):
/.well-known/oauth-authorization-server(RFC 8414) — points to Cognitooauth2/authorize+oauth2/token/.well-known/oauth-protected-resource(RFC 9728) — advertises the resource serverWWW-Authenticate: Bearer resource_metadata="..."headercode_challenge_methods_supported: ["S256"],token_endpoint_auth_methods_supported: ["none"](public client)All the server-side plumbing works — the issue is purely that Claude Code ignores the configured
clientIdand attempts dynamic registration.+1 — production non-DCR scenario (AWS Cognito) and scope note across related issues
Adding a production data point and a scope clarification for triagers consolidating across #43000 / #3273 / #57674 / this issue.
https://example.com/mcp/fronts AWS Cognito for OAuth. Cognito does not implement RFC 7591 (DCR) — architectural, not a config gap. Bug-flavor repro posted in #3273.127.0.0.1loopback redirect URI per OAuth 2.1 §7.5.2 — a great fix for DCR-capable servers that mint a new client_id per launch — does not help non-DCR providers. Cognito (and similar: Okta in some configurations, Azure AD without DCR add-ons, custom OIDC servers) refuses DCR before any redirect URI is checked. Honoring a pre-configuredclientIdfrom.mcp.json— this issue — is the only fix that closes the non-DCR gap. The #43000 fix and this one are complementary, not equivalent.Workarounds in production:
get-token.shenv-var injection;mcp-stdioas a standby Python stdio-to-HTTP relay that honors--client-iddirectly.+1 from the Cowork (Claude desktop) side — full context at anthropics/claude-plugins-official#283 (comment) and a Cowork-specific repro just posted at anthropics/claude-code#3273 (comment).
Claude Code already has a partial story here (the
claude mcp add --client-id ... --client-secret ...flag flow noted earlier in this thread, plus the${ENV_VAR}header trick in user-authored.mcp.json). The remaining gaps to keep in scope when this lands:.mcp.jsonfiles (e.g.cowork_plugins/marketplaces/knowledge-work-plugins/engineering/.mcp.json) live at app-managed paths that get overwritten on plugin update. So even with full SDK support for pre-configured creds, plugin-bundled servers have nowhere stable to put aclientId/clientSecret. Some kind of per-plugin user-overlay config (~/.claude/plugins/<plugin-id>/overrides.jsonor similar) would let users inject creds without forking the plugin..envfile. The Claude Code CLI workarounds aren't reachable from Cowork at all — see the #3273 comment for what users actually have to do (abandon the plugin's connector entirely and shell out togh).clientIdconfig case. Today when a plugin's.mcp.jsondeclares a remote MCP server URL with noclientIdfield at all, the SDK goes straight to DCR and fails on servers like GitHub's that don't support it. Worth deciding: should the SDK fall back to a hosted proxy / static-creds path in this case, or hard-fail at server-init with a clear "this plugin needs pre-configured credentials" error instead of exposing perpetualauthenticatestub tools?Net: the fix needs to ship a config path that's reachable from both surfaces (CLI and desktop) and survives plugin updates. Otherwise Cowork users in particular remain stuck even after Claude Code is unblocked.
This looks like it works now, at least on 2.1.206. I connected to GitHub's remote MCP server (a non-DCR provider) using a pre-configured client ID:
claude mcp add --scope user --transport http \
--client-id <client-id> --client-secret --callback-port 8080 \
github https://api.githubcopilot.com/mcp
The clientId was honoured and DCR wasn't attempted.
--callback-portmatters — GitHub needs an exact redirect URI registered on the OAuth App (http://localhost:8080/callback), and the port is random otherwise.