MCP OAuth regression: client metadata document redirect_uris missing port causes auth failure for providers supporting CIMD

Resolved 💬 17 comments Opened Mar 23, 2026 by giampaolocasolla Closed May 24, 2026
💡 Likely answer: A maintainer (localden, collaborator) responded on this thread — see the highlighted reply below.

Severity: All MCP OAuth providers advertising client_id_metadata_document_supported: true are broken in 2.1.80+

Problem

Starting in 2.1.80, Claude Code sends a portless redirect_uri via the published client metadata document, while the local OAuth callback server listens on a specific port (default 3118). This mismatch causes every provider that supports Client ID Metadata Documents (CIMD) to reject the authorization request with invalid_redirect_uri.

Root cause

In 2.1.80, the MCP OAuth provider class gained a clientMetadataUrl getter that always returns a value (defaults to https://claude.ai/oauth/claude-code-client-metadata). Previously this property was undefined on the class.

This changes the client registration path. The OAuth flow has this logic:

if (client_id_metadata_document_supported && clientMetadataUrl)
    client_id = clientMetadataUrl  // Use metadata document instead of dynamic registration
else
    // Dynamic client registration (sends actual redirect_uris with port)

Before 2.1.80, clientMetadataUrl was undefined, so the flow always fell through to dynamic client registration, which sends the correct redirect_uris including the port (e.g. http://localhost:3118/callback).

Now, when a provider advertises client_id_metadata_document_supported: true, Claude Code uses the published metadata at https://claude.ai/oauth/claude-code-client-metadata. That document contains:

{
  "redirect_uris": [
    "http://localhost/callback",
    "http://127.0.0.1/callback"
  ]
}

These URIs have no port. The actual redirect URI Claude Code constructs is http://localhost:3118/callback. The provider compares the two, they don't match, auth fails.

Reproduction

  1. Install Claude Code >= 2.1.80
  2. Configure any MCP server whose OAuth discovery metadata includes "client_id_metadata_document_supported": true (e.g. Granola: https://mcp-auth.granola.ai/.well-known/oauth-authorization-server)
  3. Attempt to authenticate
  4. Browser opens, immediately shows invalid_redirect_uri

Affected providers (confirmed)

  • Granola (mcp.granola.ai): advertises client_id_metadata_document_supported: true
  • Slack (mcp.slack.com): same error pattern, likely same root cause

Any provider supporting CIMD is affected.

Verification

Confirmed by diffing cli.js between 2.1.79 and 2.1.81. The only relevant change is the addition of get clientMetadataUrl() on the OAuth provider class, which flips the registration path for all CIMD-supporting providers.

Suggested fix

The published client metadata document needs to account for the dynamic port. Options in order of preference:

  1. Don't use CIMD when the redirect URI has a non-standard port. If the locally constructed redirect_uri doesn't match any URI in the published metadata, fall back to dynamic client registration.
  2. Include the default port in the published metadata. Add http://localhost:3118/callback and http://127.0.0.1:3118/callback to the redirect_uris array at https://claude.ai/oauth/claude-code-client-metadata.
  3. Use port 80 (implicit) for the local callback server when CIMD is active. This would match the portless URIs in the metadata but may conflict with other services.

Option 1 is the most robust since it handles custom ports via MCP_OAUTH_CALLBACK_PORT and random port fallback.

Secondary issue

When OAuth fails, Claude Code caches an empty accessToken with expiresAt: 0 in the macOS Keychain. On subsequent sessions it sees this entry and skips the OAuth flow entirely, so the browser never opens again. This was reported separately in #29934 but compounds the problem here: users can't even retry after the regression is fixed without manually clearing the Keychain.

Environment

  • Claude Code 2.1.81 (regression introduced in 2.1.80)
  • Last working version: 2.1.79
  • macOS Darwin 25.3.0

View original on GitHub ↗

17 Comments

Koriit · 3 months ago

Not sure if it is related but I'm also failing mcp auth (Slack) on 2.1.74 today (stable).

redirect_uri did not match any configured URIs. Passed URI: http://localhost:3118/callback

<img width="608" height="463" alt="Image" src="https://github.com/user-attachments/assets/7fa2ed8d-e532-44b8-a3b8-ad42db39a340" />

galElmalah · 3 months ago

same here, getting redirect_uri did not match any configured URIs. Passed URI: http://localhost:3118/callback

orcaman · 3 months ago

Same here

<img width="1576" height="988" alt="Image" src="https://github.com/user-attachments/assets/54439ac9-f9d5-40c1-8117-51aeda6f4ee3" />

yurano · 3 months ago

I also reported a similar issue in #37767.

After investigating further, I found that OAuth 2.1 draft (draft-ietf-oauth-v2-1-13), Section 8.4.2 explicitly states:

The authorization server MUST allow any port to be specified at the time of the request for loopback IP redirect URIs

This requirement exists because native apps typically use an ephemeral port assigned at runtime.

Additionally, the Model Context Protocol (MCP) Authorization Mechanism is defined as being based on OAuth 2.1:
https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization

Given this, it seems that the root cause of this issue may be that the Authorization Server does not support dynamic loopback ports as required by OAuth 2.1.

In other words, this may not be a client-side bug, but rather a server-side incompatibility with the OAuth 2.1 specification.

WladmirJunior · 3 months ago

Same here

rlucioni · 3 months ago
this may not be a client-side bug, but rather a server-side incompatibility with the OAuth 2.1 specification

@yurano this is likely true as far as the ephemeral port goes, but I think the problem is compounded by the use of localhost vs. the loopback IP literal. Also from https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13#section-8.4.2:

While redirect URIs using the name localhost (i.e., http://localhost:{port}/{path}) function similarly to loopback IP redirects, the use of localhost is NOT RECOMMENDED. Specifying a redirect URI with the loopback IP literal rather than localhost avoids inadvertently listening on network interfaces other than the loopback interface. It is also less susceptible to client-side firewalls and misconfigured host name resolution on the user's device.

Some OAuth provider libraries - we use @cloudflare/workers-oauth-provider - interpret this guidance by implementing support for the dynamic port only when the loopback IP literal is used, not localhost: https://github.com/cloudflare/workers-oauth-provider/pull/145.

erodriguezds · 3 months ago

Same here, trying to authenticate against a FastMCP-based server. After a few hours of research, I figured out that the problem is simple: Claude Code binds a random port to handle the Oauth redirect callback, but Claude public CIMD (https://claude.ai/oauth/claude-code-client-metadata) states that the allowed redirect callbacks are:

  "redirect_uris": [
    "http://localhost/callback",
    "http://127.0.0.1/callback"
  ],

The fix is as simple as updating the CIMD file to allow any port:

  "redirect_uris": [
    "http://localhost:*/callback",
    "http://127.0.0.1:*/callback"
  ],
priley86 · 3 months ago

👋 - auth0 engineer here currently testing some CIMD integrations. 👷 🚀

I wanted to provide a bit of initial feedback here on this document being used for Claude Code after some initial testing.

  • we have no application_type specified, and this is actually quite important semantically speaking on our platform. Per the current spec, we actually default to web when omitted on our side, but in this case I think we want to specify native. This will be more consistent with the usage here it seems.
  • the behavior described in the issue here for redirect_uris is actually a breaking issue at the moment on our side, given we do not currently allow wildcards for ports in callback urls. The current workaround I've found on our end is actually patching a client/application after creation (either using a random port assigned or by using the --callback-port param you've provided and specifying the port explicitly in our Allowed Callback URLs after construction. Is there a default port we can set here as I'm not sure that relying on wildcards is a good policy? I note this in the Visual Studio document.
  • we have no token_endpoint_auth_method and i think we should be explicit here that this is a public client (type none) now, and is not a confidential client at this time. Our platform will soon support public and confidential clients though (via jwks_uri) 😄
  • A logo_uri may be a nice addition also here and would add further context in our application and login screens for the user. (similar addition was just made in VSCode https://github.com/microsoft/vscode/issues/298300)

cc'ing some other Auth0 folks for further consideration: @btiernay @aaronpk @yissellokta

btiernay · 3 months ago

I think there is a second interoperability issue here beyond the redirect URI port mismatch already discussed in this thread.

The current published Claude Code client metadata document also omits application_type. Under RFC 7591, omitting application_type defaults the client to "web". But the redirect URI pattern used here, especially loopback callbacks, is associated with native apps in RFC 8252.

More importantly, RFC 8252 says the redirect URI alone is not enough to distinguish public native apps from confidential web clients, and that the client type needs to be recorded during registration so the authorization server can determine how to treat the client.

So even if the port-handling issue gets fixed, the metadata may still be underspecified for some providers if application_type is left implicit and servers apply the RFC 7591 default of "web".

That seems consistent with the additional provider feedback in this thread, especially from Auth0, and may explain why behavior differs across authorization servers.

I opened a separate bug specifically for that angle here:

There is also related upstream standards discussion here:

So my read is that there are really two separate issues:

  1. the current CIMD redirect_uris do not line up with Claude Code's actual callback behavior in some environments
  2. the CIMD also omits application_type, which can cause some authorization servers to classify the client as "web" by default

Both seem worth addressing independently.

melhossaryk · 2 months ago

Did anyone find a workaround for this issue with Notion MCP? Notion MCP can no longer authenticate.
Any ETA to fix this?
We can downgrade the Claude Code version to 2.1.79 but we will lose A LOT of fixes

alyson-goff · 2 months ago

+1 on Notion MCP specifically. /mcp → Notion → authenticate returns invalid redirect_uri for oauth client on 2.1.80+. Happy to downgrade as a workaround if an ETA isn't close, but losing the 2.1.80+ fixes is painful. Any signal on when a patch might land?

melhossaryk · 2 months ago
+1 on Notion MCP specifically. /mcp → Notion → authenticate returns invalid redirect_uri for oauth client on 2.1.80+. Happy to downgrade as a workaround if an ETA isn't close, but losing the 2.1.80+ fixes is painful. Any signal on when a patch might land?

@alyson-goff it got fixed here! https://github.com/anthropics/claude-code/issues/52922

Ranex · 2 months ago

Adding a data point: the official hosted Meta Ads MCP at https://mcp.facebook.com/ads is also affected by this regression on Claude Code 2.1.123.

13:12:35.817 [DEBUG] MCP server "meta-ads": Initializing HTTP transport to https://mcp.facebook.com/ads
13:12:36.573 [DEBUG] MCP server "meta-ads": HTTP Connection failed after 756ms: The provided redirect_uris are not registered for this client.
13:12:41.065 [DEBUG] MCP server "meta-ads": SDK auth error: jo: The provided redirect_uris are not registered for this client.

Failure happens entirely server-to-server in ~750ms — no browser launch, fully consistent with the CIMD path described above. Platform: WSL2 / Linux.

One thing worth flagging that's distinct from the Granola/Slack/Linear cases in this thread: because this is the official hosted integration, none of the documented escape hatches apply — users can't supply --client-id, --client-secret, or --callback-port to pin a registered redirect URI, since the OAuth client identity is fixed by Claude Code itself. So when the CIMD fix lands, please make sure Meta's hosted MCP is included in the verification matrix; affected users currently have zero workaround.

Originally tracked at #55002, closing that as a duplicate of this one.

hmagoutas · 2 months ago

Still reproing on Claude Code 2.1.141 (released 2026-05-13) with the official Meta Ads MCP at https://mcp.facebook.com/ads.

Same error: SDK auth failed: The provided redirect_uris are not registered for this client.

Confirmed via /mcp → meta-ads → Authenticate on 2026-05-14. The Notion-targeted fix that landed via #52922 doesn't resolve the Meta path — suggests the remaining work is on Meta's side (extending their OAuth client allowlist to include Claude Code's redirect_uri), not Anthropic's.

If anyone has a Meta partner contact who can flag this, that would unblock the official Meta Ads MCP for every Claude Code user.

melhossaryk · 1 month ago

Confirming this still reproduces on Claude Code 2.1.141 with the official Meta Ads MCP at https://mcp.facebook.com/ads (matching @hmagoutas's report on 2026-05-14).

Adding one data point that may help future debugging: providing a pre-registered oauth.clientId in our managed .mcp.json partially gets past the initial redirect_uris are not registered error, but the OAuth flow then fails downstream with a series of cascading errors — Can't load URL — domain not in app's domains from Meta's consent dialog, despite App Domains, Valid OAuth Redirect URIs, and Facebook Login for Business → Configurations all being configured per Meta's docs. Strongly suggests the underlying CIMD/port mismatch is still in play even when bypassing dynamic client registration.

Net effect: there's no workaround currently available for the official hosted Meta Ads MCP in Claude Code — neither DCR (rejected by Meta) nor pre-registered clientId (downstream errors). Flagging this for the verification matrix when the fix lands.

Adding a +1 to prioritize this one — it's blocking real Marketing/Growth pilot work on our side.

localden collaborator · 1 month ago

Thanks for digging into this, and to @priley86 and @btiernay for flagging application_type. That was the actual gap.

Two changes on our side:

  1. We're looking at setting application_type: "native" in the hosted client metadata document. Without it we defaulted to web, and the RFC 9700 4.1.3 loopback-port exception only applies to native apps.
  2. We're looking at having the CLI send 127.0.0.1 instead of localhost for the redirect (RFC 8252 7.3 is scoped to IP literals).

We are not adding port-specific entries to the metadata document. Portless registration with an ephemeral runtime port is the RFC 8252 7.3 pattern for native apps, and OAuth 2.1 2.3.1 carries the same exception. Adding a fixed port would be a workaround for providers that don't implement that exception, not a spec requirement.

For providers that don't implement RFC 8252 7.3, the workaround is to set MCP_OAUTH_CALLBACK_PORT to a fixed port and register http://127.0.0.1:<port>/callback with the provider. If a specific provider is rejecting on port mismatch after the changes above, please raise it with that provider and reference RFC 8252 7.3.

We've also commented on oauth-wg/draft-ietf-oauth-client-id-metadata-document#81 about getting the loopback exception into CIMD 4.5.

Closing this; per-provider gaps should go to those providers.

cjellick · 27 days ago

I'd like to petition that this issue remains open until it is actually resolved by adding the application_type: "native".

Or do you have another issue tracking that work?

Also, thanks for the great explanation.