claude-code - 💡(How to fix) Fix MCP OAuth: redirect_uri sent as /callback but local listener is at /oauth/callback — causes redirect_uri_mismatch [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#58595Fetched 2026-05-14 03:44:13
View on GitHub
Comments
0
Participants
1
Timeline
6
Reactions
0
Participants
Timeline (top)
labeled ×5cross-referenced ×1

When Claude Code initiates the OAuth 2.0 PKCE flow for an MCP server configured with type: "http" and an explicit oauth.callbackPort, the redirect_uri it sends to the authorization server is:

http://localhost:<callbackPort>/callback

…but the local listener it spawns to receive the OAuth response appears to be bound at:

http://localhost:<callbackPort>/oauth/callback

Plugin authors who register http://localhost:<port>/oauth/callback server-side (because that's where the response actually lands) see redirect_uri_mismatch errors from the IdP, because Claude Code's outbound redirect_uri parameter uses the shorter /callback path.

Error Message

→ Salesforce returns error=redirect_uri_mismatch&error_description=redirect_uri must match configuration.

  • The browser was redirected to http://localhost:22428/oauth/callback?error=OAUTH_AUTHORIZATION_BLOCKED&error_description=Cross-org+OAuth+flows+are+not+supported+for+this+external+client+app&state=<...>.
  • That final landing URL implies Claude Code's local HTTP server is listening at /oauth/callback, because the browser hit it and a response came back from localhost without a connection-refused error.

Root Cause

Plugin authors who register http://localhost:<port>/oauth/callback server-side (because that's where the response actually lands) see redirect_uri_mismatch errors from the IdP, because Claude Code's outbound redirect_uri parameter uses the shorter /callback path.

Fix Action

Workaround

Plugin authors must register http://localhost:<callbackPort>/callback (without /oauth) in their OAuth provider configuration. Worth calling out in the MCP plugin authoring docs until this is resolved.

Code Example

http://localhost:<callbackPort>/callback

---

http://localhost:<callbackPort>/oauth/callback

---

{
  "mcpServers": {
    "intuit-salesforce-crm": {
      "type": "http",
      "url": "https://api.salesforce.com/platform/mcp/v1/platform/sobject-reads",
      "oauth": {
        "clientId": "<consumer-key>",
        "callbackPort": 22428
      }
    }
  }
}

---

https://login.salesforce.com/services/oauth2/authorize
  ?response_type=code
  &client_id=<consumer-key>
  &code_challenge=<...>
  &code_challenge_method=S256
  &redirect_uri=http%3A%2F%2Flocalhost%3A22428%2Fcallback
  &state=<...>
  &scope=mcp_api+refresh_token
RAW_BUFFERClick to expand / collapse

Environment

  • Claude Code version: 2.1.140
  • OS: macOS (Darwin 25.4.0)
  • MCP server type: http with PKCE OAuth (oauth.callbackPort set in .mcp.json)

Summary

When Claude Code initiates the OAuth 2.0 PKCE flow for an MCP server configured with type: "http" and an explicit oauth.callbackPort, the redirect_uri it sends to the authorization server is:

http://localhost:<callbackPort>/callback

…but the local listener it spawns to receive the OAuth response appears to be bound at:

http://localhost:<callbackPort>/oauth/callback

Plugin authors who register http://localhost:<port>/oauth/callback server-side (because that's where the response actually lands) see redirect_uri_mismatch errors from the IdP, because Claude Code's outbound redirect_uri parameter uses the shorter /callback path.

Reproduction

The bug surfaced with an internal plugin using Salesforce Hosted MCP, but the issue is generic to any MCP http server using PKCE OAuth via callbackPort.

Plugin .mcp.json:

{
  "mcpServers": {
    "intuit-salesforce-crm": {
      "type": "http",
      "url": "https://api.salesforce.com/platform/mcp/v1/platform/sobject-reads",
      "oauth": {
        "clientId": "<consumer-key>",
        "callbackPort": 22428
      }
    }
  }
}

Triggering the auth flow opens this authorization URL (relevant params only):

https://login.salesforce.com/services/oauth2/authorize
  ?response_type=code
  &client_id=<consumer-key>
  &code_challenge=<...>
  &code_challenge_method=S256
  &redirect_uri=http%3A%2F%2Flocalhost%3A22428%2Fcallback
  &state=<...>
  &scope=mcp_api+refresh_token

→ Salesforce returns error=redirect_uri_mismatch&error_description=redirect_uri must match configuration.

Confirmation that the listener is at /oauth/callback, not /callback

I manually edited the authorization URL in the browser, changing only redirect_uri from http://localhost:22428/callbackhttp://localhost:22428/oauth/callback, and re-submitted.

  • Salesforce accepted the redirect_uri (no more redirect_uri_mismatch).
  • The browser was redirected to http://localhost:22428/oauth/callback?error=OAUTH_AUTHORIZATION_BLOCKED&error_description=Cross-org+OAuth+flows+are+not+supported+for+this+external+client+app&state=<...>.
  • That final landing URL implies Claude Code's local HTTP server is listening at /oauth/callback, because the browser hit it and a response came back from localhost without a connection-refused error.

So the contradiction is on Claude Code's side: outbound redirect_uri parameter is /callback, but the inbound listener path is /oauth/callback.

Expected behavior

The redirect_uri query parameter sent to the authorization endpoint should match the path of the local HTTP listener exactly. Either:

  1. The outbound redirect_uri should be http://localhost:<port>/oauth/callback, or
  2. The local listener should bind to /callback.

Whichever path is chosen, it must be documented so plugin authors register the correct value in their OAuth provider config.

Impact

Any MCP plugin using type: "http" + oauth.callbackPort that has been registered server-side with /oauth/callback (a natural path to pick, and what at least one production plugin used) will fail to authenticate from Claude Code with redirect_uri_mismatch. The plugin owner has no way to fix this without either (a) re-registering their OAuth client with the /callback path, or (b) maintaining both paths in the allowlist, neither of which should be necessary.

Notes

  • Cursor uses a cursor:// deep-link callback for the same plugin and is unaffected — this is specific to the localhost OAuth flow.
  • Local port 22428 was free during all tests (lsof -iTCP:22428 -sTCP:LISTEN returned empty before initiating the flow).
  • Plugin .mcp.json matched the upstream repo exactly; no local modifications.

Workaround

Plugin authors must register http://localhost:<callbackPort>/callback (without /oauth) in their OAuth provider configuration. Worth calling out in the MCP plugin authoring docs until this is resolved.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

The redirect_uri query parameter sent to the authorization endpoint should match the path of the local HTTP listener exactly. Either:

  1. The outbound redirect_uri should be http://localhost:<port>/oauth/callback, or
  2. The local listener should bind to /callback.

Whichever path is chosen, it must be documented so plugin authors register the correct value in their OAuth provider config.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING