Skip to content

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

Description

@giampaolocasolla

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions