CRITICAL: claude.ai MCP connector OAuth completes but Bearer token never sent to server

Resolved 💬 20 comments Opened Apr 10, 2026 by daveladouceur Closed Jul 8, 2026
💡 Likely answer: A maintainer (localden, collaborator) responded on this thread — see the highlighted reply below.

Priority: CRITICAL / URGENT

Summary

claude.ai's MCP connector completes the full OAuth 2.1 authorization_code + PKCE flow successfully (Dynamic Client Registration → authorize → token exchange), but never sends the subsequent MCP request with the Bearer token. The server issues a valid access_token, but claude.ai reports "Authorization with the MCP server failed" without ever attempting an authenticated request.

This affects ALL self-hosted remote MCP servers using OAuth. The server-side implementation is correct — verified by simulating the full flow with curl through the same tunnel.

Environment

  • Self-hosted MCP server behind Cloudflare named tunnel
  • Transport: Streamable HTTP (POST for JSON-RPC, GET for SSE)
  • Protocol version: 2025-03-26
  • Server correctly implements: RFC 9728 (Protected Resource Metadata), RFC 8414 (Authorization Server Metadata), RFC 7591 (Dynamic Client Registration), PKCE (S256)

Reproduction

Every attempt follows this exact sequence (from server logs):

POST /gws → 200 (initialize succeeds)
GET /.well-known/oauth-protected-resource/gws → 404
GET /.well-known/oauth-protected-resource → 404  
GET /.well-known/oauth-authorization-server → 404
POST /register → 201 (client registered successfully)
GET /authorize → 302 (code issued, redirect to claude.ai callback)
POST /token → 200 (access_token issued, PKCE verified ✓)
... silence. No authenticated MCP request ever arrives.

Claude.ai then displays: "Authorization with the MCP server failed"

Proof the server works

Full OAuth + authenticated MCP call via curl through the same tunnel:

# Register → Authorize → Token (all succeed)
# Then:
curl -X POST https://mcp.prtrust.fund/gws \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
# Returns: 6 tools successfully

The server is reachable, OAuth is correct, authenticated MCP calls work. claude.ai just never sends step 8.

Variations tested (all fail identically)

| Version | What we tried | Result |
|---------|--------------|--------|
| v1.5.19 | well-known 200 + OAuth enabled, resource=/sse | Token issued, never used |
| v1.5.20 | Added 401 gate on POST /sse for tunnel requests | claude.ai skipped 401, went straight to /register |
| v1.5.22 | Path-aware well-known (resource matches connector URL) | Token issued, never used |
| v1.5.25 | Accept client_secret as Bearer + client_credentials grant | Token issued, never used |
| v1.5.27 | 404 ALL OAuth endpoints | "Couldn't reach server" |
| v1.5.28 | 404 well-known only, OAuth endpoints live | Token issued, never used |
| v1.5.29 | 404 /register too | "Couldn't reach server" |
| v1.5.30 | Fresh domain (mcp.prtrust.fund, no cached state) | Token issued, never used |

PKCE verification proof (from server logs)

OAuth /token: grant_type=authorization_code code=a9693dc3… verifier=present
OAuth /token: PKCE: challenge=0Qdw7BNb6SUK… computed=0Qdw7BNb6SUK… match=true
OAuth: token issued for 0b5d725f1b047d1757c3b6bb3e299f2f (token=ab956009…)

PKCE passes. Token is valid. claude.ai has the token. It never uses it.

Support references

  • ofid_5b4f176052475e66
  • ofid_24385e3ebac39bf8
  • ofid_00912162dce5872c
  • ofid_3ddc9e36fada07fa
  • ofid_42c4f5ce4ebf5530
  • ofid_0124fba879402e77
  • ofid_f8bab433728a98e9
  • ofid_0c0e836314b4995f
  • ofid_5211d10fd69c4ff0

Related issues

  • modelcontextprotocol/modelcontextprotocol#2157
  • Multiple reports on community forums of the same "Authorization failed" pattern
  • Cloudflare community thread: "Authorization with the MCP server failed" (WAF ruled out)

Impact

Cannot use any self-hosted MCP server with claude.ai web interface. Claude Code CLI works perfectly with the same server (OAuth completes and Bearer token is correctly sent). This blocks all remote MCP integrations for users who need claude.ai web access.

Expected behavior

After receiving a valid access_token from POST /token, claude.ai should retry the original MCP request with Authorization: Bearer <token> header.

Actual behavior

claude.ai receives the access_token, discards it, and reports "Authorization with the MCP server failed" without attempting any authenticated request.

View original on GitHub ↗

20 Comments

raye-deng · 3 months ago

This is a critical issue – OAuth flow completes successfully but the Bearer token never gets used in subsequent requests. We've seen a similar pattern in production where AI-generated API clients complete all the setup steps correctly but fail to actually use the credentials in the actual API calls.

The tricky part is that these failures are often silent: the OAuth response looks valid (200 OK with access_token), the code path for authorization completes without exceptions, but the actual API request goes out unauthenticated or with the wrong headers.

Traditional static analysis tools miss this because they can't validate runtime protocol behavior. We've been adding runtime protocol validation as a PR check to catch these classes of issues.

For anyone debugging similar API integration issues, it's worth adding a protocol-level validation step in your CI pipeline. You can test this pattern with: npx @opencodereview/cli scan . --sla L1

Thanks for flagging this – getting OAuth right is hard enough without silent token-handling bugs.

daveladouceur · 3 months ago

Update — still broken, now with fully protocol-compliant server (v1.5.37)

Rebuilt the OAuth 2.1 implementation from scratch following the spec exactly. All server-side issues from the original report are fixed. The bug is confirmed client-side.

What was fixed server-side (v1.5.36–v1.5.37)

  • Added proper 401 gate with WWW-Authenticate: Bearer realm="MCP", resource_metadata="<absolute-url>"
  • Public client registration — no client_secret, token_endpoint_auth_methods_supported: ["none"]
  • Mandatory PKCE S256 on both /authorize and /token
  • Cache-Control: no-store on all OAuth responses
  • OAuth endpoints at domain root (/register, /authorize, /token)
  • Absolute URLs in all metadata and headers

Same result — token issued, never used

POST /clauth           → 401 + WWW-Authenticate ✅
GET  /.well-known/...  → 200 (resource metadata) ✅
GET  /.well-known/...  → 200 (auth server meta)  ✅
POST /register         → 201 (public client)     ✅
GET  /authorize        → 302 → claude.ai callback ✅
POST /token            → 200 (access_token)       ✅
POST /clauth + Bearer  → NEVER ARRIVES            ❌

New ofid_ references

  • ofid_870bc239f1fc3342 (clauth endpoint)
  • ofid_eb53b996a97a28a4 (gws endpoint)

Confirmed working with same server

  • test-mcp-oauth.mjs passes all 8 stages through Cloudflare tunnel
  • Claude Code CLI works end-to-end (claude mcp add --transport http)

This is the same bug as anthropics/claude-ai-mcp#136 — confirmed cluster across multiple independent servers including Vercel's own MCP. The claude.ai OAuth proxy completes token exchange but never sends the authenticated MCP request.

This is blocking production MCP infrastructure. Please escalate to the claude.ai client engineering team.

daveladouceur · 3 months ago

Workaround found — noauth on fresh domain

While waiting for the OAuth proxy fix, we found a working workaround:

Use a fresh domain that has never exposed OAuth endpoints. Claude.ai connects directly — same pattern as any MCP server without auth (e.g., regen-media).

What we discovered

  • regen-media MCP (media.regendevcorp.com) works on claude.ai because it never had OAuth endpoints
  • Any domain that has ever exposed /.well-known/oauth-authorization-server appears to be permanently cached as "OAuth-required" in Anthropic's proxy — even after removing those endpoints
  • Domains we burned trying OAuth: clauth.prtrust.fund, mcp.prtrust.fund, vault.regendevcorp.com

The fix

  1. Create a fresh subdomain (e.g., clauth.regendevcorp.com)
  2. CNAME it to the same Cloudflare tunnel (proxied)
  3. In the MCP server, check the Host header — if it's a "noauth host," return 404 for all /.well-known/* and OAuth endpoints (/register, /authorize, /token), and skip the 401 gate
  4. Claude.ai connects directly to the MCP endpoint without triggering OAuth

Config pattern (Node.js)

const NOAUTH_HOSTS = ["clauth.regendevcorp.com", "fs.regendevcorp.com"];
const host = (req.headers.host || "").split(":")[0].toLowerCase();

// Block OAuth discovery on noauth hosts
if (NOAUTH_HOSTS.includes(host) && reqPath.startsWith("/.well-known/")) {
  return res.writeHead(404).end('{"error":"not_found"}');
}

// Block OAuth endpoints on noauth hosts  
if (NOAUTH_HOSTS.includes(host) && ["/register", "/authorize", "/token"].includes(reqPath)) {
  return res.writeHead(404).end('{"error":"not_found"}');
}

// Skip 401 gate on noauth hosts
if (!NOAUTH_HOSTS.includes(host) && !validToken) {
  return res.writeHead(401, { "WWW-Authenticate": `Bearer resource_metadata="..."` }).end();
}

Result

We now have 27 MCP tools (credential vault + Google Workspace + filesystem) working on claude.ai through the noauth domain. The OAuth endpoints still work on the original domain for Claude Code CLI.

This is a temporary workaround — when the OAuth proxy is fixed, we can switch back to the authenticated domains. But for anyone else blocked by this bug, fresh domain + suppress OAuth = working connector.

danigrubi · 3 months ago

This affects me too as a Pro user with Home Assistant + Nabu Casa

odoremieux · 3 months ago

Same problem

trannolis · 2 months ago

+1, hitting the same symptom on a fully MCP-spec-compliant remote OAuth server. Three failed claude.ai web handshakes, all with the Authorization with the MCP server failed banner:

  • ofid_e1ef67c549d3582c
  • ofid_afae126355cb4937
  • ofid_2a000847563dfca4

Server: https://<redacted-mcp-server> (Cloudflare Worker, OAuth 2.1 + PKCE, single-tenant proxy to a backend MCP server). The Worker is built on @cloudflare/workers-oauth-provider.

Server-side completeness check (all return 200):

  • GET /.well-known/oauth-authorization-server — RFC 8414, advertises registration_endpoint
  • GET /.well-known/oauth-protected-resource — RFC 9728, MCP-spec required since April 2025
  • POST /register — RFC 7591 Dynamic Client Registration, returns valid client_id/client_secret
  • GET /authorize, POST /token — full PKCE happy path completes end-to-end via curl
  • GET /sse with valid Bearer — returns SSE event: endpoint with session-scoped URL
  • POST /messages/?session_id=... with valid Bearer — 202 Accepted, upstream processes the RPC

A/B repro on the same server:

| Client | Result |
|---|---|
| Manual curl walking the full OAuth + SSE flow | ✓ end-to-end success, upstream returns valid responses |
| Claude Code CLI (claude mcp add --transport sse <name> <url>) | ✓ "Authentication Successful", tools register correctly |
| claude.ai web (custom connector via Settings → Connectors) | ✗ "Authorization with the MCP server failed" |

Worker tail evidence: during a failed claude.ai web handshake, wrangler tail captured zero requests from claude.ai to the Worker. Not /authorize, not /token, not /sse. The ofid_ reference appears to be generated client-side before any network call to the configured server. Combined with Claude Code CLI working against the same server with the same OAuth client, this strongly suggests the failure is in claude.ai web's connector-validation pre-flight, not in any subsequent token-attachment step.

Suggested next step for triage: a concrete claude.ai/mcp/debug flag that surfaces what the web client is doing pre-network would help server operators distinguish between "server misconfigured" (we keep finding ourselves chasing this) and "client validation failed" (the actual cause).

If your triage team wants additional probes / fresh ofid_ references / tail captures, reach out via DM and I'm happy to share privately.

trannolis · 2 months ago

Update with new evidence: bug also affects Anthropic Cloud routines, not just claude.ai web.

Following up on my comment above: the same broken-connector state propagates from claude.ai web's connector store into /v1/code/triggers (Anthropic Cloud scheduled routines). I built a one-shot diagnostic to isolate whether routines bypass the web client's bug. They don't.

Repro setup:

  • Created an experimental trigger via POST /v1/code/triggers with mcp_connections: [{connector_uuid: "<uuid>", name: "<our-server>", url: "https://<redacted-mcp-server>/sse"}]. Schema discovered by trial — required fields are connector_uuid, name, url.
  • The connector_uuid references the same connector record claude.ai web's UI shows as broken (the one whose smoke test fails with the auth-failed banner from my prior comment).
  • Trigger prompt: minimal — list available tools, call a known tool from the connector if mcp__<our-server>__* is present, otherwise report which tool prefixes ARE available. No fallback, no CLI shell-out — pure connector-path test.
  • Fired via POST /v1/code/triggers/{id}/run.

Result — routine stdout, captured verbatim from claude.ai's routine-history UI:

EXPERIMENT_RESULT=MCP_TOOLS_NOT_AVAILABLE

No tools matching mcp__<our-server>__* are present in this runtime.

Tool prefixes available in this session:
- Agent, Bash, Edit, Read, Write, Skill, ToolSearch, Monitor, NotebookEdit,
  PushNotification, TodoWrite, WebFetch, WebSearch

wrangler tail against the bridge Worker captured zero requests during the 3-minute window after firing the routine. The runtime never made the discovery call, never hit /.well-known/oauth-protected-resource, never tried /sse. Same fingerprint as the claude.ai web smoke test failure.

Diagnosis: mcp_connections in the trigger config is accepted by the API at create-time but silently ignored at runtime when the referenced connector's auth state is in the broken-by-#46140 state. The routine's tool inventory comes back without any mcp__<name>__* entries — as if the connector wasn't attached at all.

Three failure paths, one root cause:

| Surface | Symptom | Worker requests captured |
|---|---|---|
| claude.ai web custom connector smoke test | "Authorization with the MCP server failed" banner | 0 |
| claude.ai web in-session use of the connector | Same banner, can't use tools | 0 |
| /v1/code/triggers routine with mcp_connections | Routine runs but no mcp__* tools loaded | 0 |
| Claude Code CLI (claude mcp add --transport sse) | OAuth succeeds end-to-end, tools register | Many — full OAuth + tool catalog flow |

The Claude Code CLI A/B test is the proof that the bridge Worker is correct. Three failure modes through claude.ai's web-connector path, one success through the CLI's separate auth path.

Operational impact for self-hosted MCP servers: this isn't just a UX bug in the web smoke test. Anyone trying to use a custom OAuth connector with Anthropic Cloud routines hits the same wall — and the routines fail silently (no error in stdout, just missing tools). The only workaround we've found is to bypass claude.ai's connector-store entirely and run the MCP client outside of routines (e.g., via the Claude Code CLI's separate auth path).

Reference IDs from this user's failed handshakes:

  • ofid_e1ef67c549d3582c
  • ofid_afae126355cb4937
  • ofid_2a000847563dfca4

If your triage team wants the trigger ID, prompt, and exact mcp_connections payload for internal repro, reach out via DM and I'm happy to share privately.

Roscat · 2 months ago

+1, still reproducing on 7 May 2026.

Stack: Self-hosted MCP server, Express + @modelcontextprotocol/sdk 1.20.1, Cloudflare named tunnel → localhost.

Symptom matches exactly: Full OAuth flow completes on the wire — discovery, DCR, /authorize (consent approved), /token (200 OK, valid token issued). Cloudflare tunnel logs show all four requests succeed. Then claude.ai reports "Authorization with the MCP server failed" and never makes any subsequent request to the server. No bearer token is ever attached to a /mcp POST. Reproducible across two surfaces, two attempts each (4/4 failures).

Server health verified independently: I wrote a Node script that walks the identical flow claude.ai's connector should walk (discovery → DCR → PKCE → /authorize with decision=approve → /token → authenticated MCP initialize call). All five steps succeed end-to-end. JWT decodes cleanly, aud claim matches the resource parameter exactly, MCP server returns a proper initialize response with capabilities. The server is provably healthy through the very step claude.ai is skipping.

Reference ID: ofid_08a5f97a3516ee98

The only difference between my repro script's request sequence and claude.ai's is what happens after /token returns 200 — the script attaches the bearer token to the next /mcp request, claude.ai discards it. Same server, same domain, same TLS, same tunnel.

This is blocking onboarding for any new self-hosted MCP connector since the ~April 15 Connections UI migration.

alemarcha · 2 months ago

+1 on 2026-05-11. Independent repro from a different stack, with the chat vs Routines asymmetry that hopefully helps narrow the cause.

Stack

Minimal Node + Express + @modelcontextprotocol/sdk 1.18, OAuth 2.1 written from scratch with jose RS256 signing (not HS256), full spec coverage: RFC 9728 PRM, RFC 8414 AS metadata, RFC 7591 DCR, PKCE S256. Single tool ping. Tunnel: ngrok premium reserved domain → localhost:3100, single-origin end-to-end (no apex→www, no CDN, no trailing-slash normalization). Full OAuth + authenticated MCP initialize + tools/call verified end-to-end via curl.

Surface A — claude.ai web chat ✅

Connector registered. First use in chat triggered the browser OAuth flow, completed cleanly. After that, "list the tools of mcp-admin" returned ping, and tools/call ping returned pong + timestamp. Bearer reaches the server on subsequent /mcp calls — verified in server logs.

Surface B — Routines ❌

Routine: trig_01Ldp1giJZzf9mk8ma7N2niZ. Same connector added in "Conectores". Prompt: "Llama a la tool ping del conector mcp-admin y reporta el resultado".

Verbatim LLM output:

Ejecutaré un ping al host mcp-admin.
[Ejecutado: Ping to mcp-admin host]
El comando ping no está disponible en este entorno. Intentaré con una alternativa.
[Ejecutado: Try to reach mcp-admin via curl and DNS lookup]
El host mcp-admin no se puede resolver. (...)

Same MCP_TOOLS_NOT_AVAILABLE pattern @trannolis reported: the LLM fell back to shell ping / curl because the mcp__claude_ai_AdminMCP__* namespace was not present in the runtime. Server-side: zero requests captured during the routine window — same fingerprint as the web smoke-test failures earlier in this thread.

Manually invoking authenticate from within the routine returns:

https://api.anthropic.com/authorize?...&redirect_uri=http%3A%2F%2Flocalhost%3A53546%2Fcallback&...

A localhost:53546 redirect_uri can't possibly complete in a cloud routine. And opening that URL in a browser redirects further to https://api.anthropic.com/mcp/gdrive/google/install?... showing a "Server Turned Down — use drivemcp.googleapis.com/mcp/v1" page, i.e. the connector's client_id appears mapped to the deprecated Google Drive route despite the connector record pointing at our custom single-origin URL.

Net

  • The custom OAuth flow propagates cleanly into the chat session.
  • It does not propagate into the routine runtime, and manually re-authenticating from there resolves to the wrong/deprecated server template.

Happy to share a minimal repro server privately if your triage team wants it.

francismartens318 · 2 months ago

Also bumped into this one but where the bearer token is the refresh token.
Workaround

Since the client consistently sends the opaque refresh_token as Bearer, the server can accept it as a valid credential by storing user identity alongside the session and resolving it on lookup — avoiding any extra IAM API calls per request
Roscat · 2 months ago

Following up on my 7 May comment above (ofid_08a5f97a3516ee98) with new diagnostic evidence from a workaround attempt.

Workaround attempted: host separation to no-auth endpoint

Added a second public hostname palantir-open.deepixels.com to the same Cloudflare tunnel, configured as a no-auth path-based-secret endpoint at /{surface}-{43char-secret}/mcp. OAuth metadata explicitly 404'd on this hostname via host-gating middleware. Verified via curl:

  • GET https://palantir-open.deepixels.com/health200
  • GET https://palantir-open.deepixels.com/.well-known/oauth-authorization-server404 (served by Express — confirms host gating)
  • GET https://palantir.deepixels.com/.well-known/oauth-authorization-server200 (unchanged, Claude Code OAuth connector unaffected)

Reconfigured the failing web chat custom connector to point at https://palantir-open.deepixels.com/c-<secret>/mcp. Result: still fails, but with a different error — "Couldn't reach the MCP server" (ofid_9ed201a7d9ad595a), no longer "Authorization with the MCP server failed".

Server log captured during the failed setup

[OAuth] /authorize called without resource parameter; defaulting to surface=c
{"type":"open_access","ts":"2026-05-08T12:12:06.305Z","surface":"c","ip":"160.79.106.37","method":"POST","path":"/c-<secret>/mcp","ua":"python-httpx/0.28.1"}

The first line means the request reached /authorize on the OAuth-protected hostname (the host-gating middleware would 404 /authorize on the open-access hostname). The second shows the same setup flow's POST to the open-access endpoint succeeded. So claude.ai is reaching both hostnames during a single connector setup, even though only the open-access hostname is in the connector record.

This converges with @alemarcha's finding two comments above: the connector's effective OAuth server is determined by something other than the connector record's URL. In @alemarcha's case it resolved to a deprecated Google Drive route; in mine it resolves to the user's other working OAuth server on the same account (the Claude Code connector at palantir.deepixels.com).

Cluster pattern this week

Three spec-correct independent stacks, three different specific manifestations of what looks like one bug family:

  • Mine / original: OAuth completes, bearer never attaches to /mcp calls
  • @alemarcha: client_id mapped to deprecated Google Drive route despite connector record pointing at custom URL
  • @francismartens318: claude.ai sends refresh_token (not access_token) as Bearer credential

Surface and exact symptom vary, but the underlying breakage is consistent: claude.ai's connector identity / token tracking is decoupled from the connector record's actual configuration in ways that vary per account, per surface, and per session.

Additional Anthropic support reference IDs from this round: ofid_e6ff927ae4504e59, ofid_9ed201a7d9ad595a.

Roscat · 2 months ago

Following up on my 7 May comment above (ofid_08a5f97a3516ee98) with new diagnostic evidence from a workaround attempt.

Workaround attempted: host separation to no-auth endpoint

Added a second public hostname palantir-open.deepixels.com to the same Cloudflare tunnel, configured as a no-auth path-based-secret endpoint at /{surface}-{43char-secret}/mcp. OAuth metadata explicitly 404'd on this hostname via host-gating middleware. Verified via curl:

  • GET https://palantir-open.deepixels.com/health200
  • GET https://palantir-open.deepixels.com/.well-known/oauth-authorization-server404 (served by Express — confirms host gating)
  • GET https://palantir.deepixels.com/.well-known/oauth-authorization-server200 (unchanged, Claude Code OAuth connector unaffected)

Reconfigured the failing web chat custom connector to point at https://palantir-open.deepixels.com/c-<secret>/mcp. Result: still fails, but with a different error — "Couldn't reach the MCP server" (ofid_9ed201a7d9ad595a), no longer "Authorization with the MCP server failed".

Server log captured during the failed setup

[OAuth] /authorize called without resource parameter; defaulting to surface=c
{"type":"open_access","ts":"2026-05-08T12:12:06.305Z","surface":"c","ip":"160.79.106.37","method":"POST","path":"/c-<secret>/mcp","ua":"python-httpx/0.28.1"}

The first line means the request reached /authorize on the OAuth-protected hostname (the host-gating middleware would 404 /authorize on the open-access hostname). The second shows the same setup flow's POST to the open-access endpoint succeeded. So claude.ai is reaching both hostnames during a single connector setup, even though only the open-access hostname is in the connector record.

This converges with @alemarcha's finding two comments above: the connector's effective OAuth server is determined by something other than the connector record's URL. In @alemarcha's case it resolved to a deprecated Google Drive route; in mine it resolves to the user's other working OAuth server on the same account (the Claude Code connector at palantir.deepixels.com).

Cluster pattern this week

Three spec-correct independent stacks, three different specific manifestations of what looks like one bug family:

  • Mine / original: OAuth completes, bearer never attaches to /mcp calls
  • @alemarcha: client_id mapped to deprecated Google Drive route despite connector record pointing at custom URL
  • @francismartens318: claude.ai sends refresh_token (not access_token) as Bearer credential

Surface and exact symptom vary, but the underlying breakage is consistent: claude.ai's connector identity / token tracking is decoupled from the connector record's actual configuration in ways that vary per account, per surface, and per session.

Additional Anthropic support reference IDs from this round: ofid_e6ff927ae4504e59, ofid_9ed201a7d9ad595a.

pedrorocca · 1 month ago

Adding a data point that extends the impact beyond self-hosted servers: we hit the exact same failure pattern using Databricks' official, first-party MCP endpoint — no custom server involved.

Setup:

Observed sequence (from Databricks-side logs + claude.ai behavior):
POST /api/2.0/mcp/sql → 200 (endpoint reachable)
OAuth flow initiates ... connector stays in "Configure" state, never reaches "Connected"
Tools never load in the conversation

The same endpoint works perfectly in Claude Code CLI via .claude/settings.json with "type": "http" + Bearer header. The failure is isolated to the claude.ai web connector flow.

This means the bug blocks not just custom/self-hosted MCPs, but also official vendor-provided MCP integrations. Databricks is a widely-used data platform — their MCP endpoint being unusable from claude.ai web is a significant gap for enterpris users.

Tracking this issue for our team. Hope this additional data point helps prioritize a fix.

COEUS-CORP · 1 month ago

Confirmed we are seeing the same issue. Not great for a critical issue to have been open for over a month...

jtinney · 1 month ago

Confirming this with parallel-client evidence. Three independent OAuth-2.1 MCP clients tested against the same self-hosted MCP server in a single minute today. The server is fully RFC-compliant per the MCP HTTP-transport authorization spec (RFC 9068 §2.1 at+jwt JWT header, RFC 9207 iss-in-callback, RFC 8707 audience binding, RFC 6749 §5.1 cache headers, RFC 7591 DCR with optional fields omitted-not-null, RFC 8252 §8.3 loopback redirects).

Side-by-side access log (sanitized — host replaced with example.invalid)

# 1) Claude Code CLI — WORKS
[CLI-host]  POST /register            → 201   Bun/1.3.x
[CLI-host]  GET  /authorize           → 302   (browser handoff)
[CLI-host]  POST /approve             → 200
[CLI-host]  POST /token               → 200   (JWT issued)
[CLI-host]  POST /mcp-endpoint        → 200   claude-code/2.1.x (cli)   ← uses Bearer
[CLI-host]  POST /mcp-endpoint        → 202
[CLI-host]  POST /mcp-endpoint        → 200   (tools/list returned)

# 2) ChatGPT custom app (web) — WORKS
[OAI-IP]    POST /mcp-endpoint        → 401   probe; receives WWW-Authenticate
[OAI-IP]    GET  /.well-known/oauth-* → 200
[OAI-IP]    POST /register            → 201   Python/aiohttp
[OAI-IP]    POST /token               → 200   (JWT issued)
[OAI-IP]    POST /mcp-endpoint        → 200   openai-mcp/1.0.0 (ChatGPT) ← uses Bearer
[OAI-IP]    POST /mcp-endpoint        → 202
[OAI-IP]    POST /mcp-endpoint        → 200   (tools/list returned)

# 3) claude.ai custom connector (web) — BROKEN, matches this issue
[CAI-IP]    POST /mcp-endpoint        → 401   probe
[CAI-IP]    GET  /.well-known/oauth-* → 200
[CAI-IP]    POST /register            → 201   python-httpx/0.28.1
[CAI-IP]    POST /token               → 200   (JWT issued — confirmed in server logs)
***  no subsequent POST /mcp-endpoint with Bearer  ***
***  connector UI shows "Authorization with the MCP server failed"  ***
***  retry loop: fresh DCR + token every attempt; Bearer is never sent  ***

Server-side cross-checks ruling out a server bug

  1. The same endpoint serves CLI + ChatGPT successfully in the same minute claude.ai web fails. Server has no per-client routing.
  2. JWT is RFC 9068 §2.1 compliant — header {"alg":"HS256","typ":"at+jwt"}, payload includes iss, sub, aud, scope, client_id, token_use, iat, exp; signature verifies with HS256.
  3. Authorization response includes iss query param (RFC 9207). AS metadata advertises authorization_response_iss_parameter_supported: true.
  4. /token response carries Cache-Control: no-store + Pragma: no-cache (RFC 6749 §5.1) and Content-Type: application/json.
  5. PRM (RFC 9728) and AS metadata (RFC 8414) responses complete and validate.
  6. DCR (RFC 7591) accepts and registers cleanly for all three clients — optional fields omitted from response (not null) so strict-typed parsers don't reject.

What claude.ai sends and receives

  • /token request body: {grant_type, code, client_id, code_verifier, redirect_uri, resource} — well-formed.
  • /token response received by claude.ai: HTTP 200, valid JSON {access_token, token_type:"Bearer", expires_in, refresh_token, scope}, RFC 6749 §5.1 cache headers.
  • claude.ai's server IPs (160.79.106.x) make no further request to the MCP endpoint after token issuance.

Diff vs the working clients

ChatGPT and Claude Code CLI both POST the MCP endpoint with Authorization: Bearer <jwt> within ~1 second of receiving the token. claude.ai's server-side proxy obtains the token (server confirms 200 on /token) and stops there. The gap is between obtaining the access token and using it — entirely on claude.ai's side.

Same symptom and pattern as the original report. Posting parallel-client evidence so the diagnosis isn't dependent on a single server implementation.

frankueDE · 1 month ago

Claude Code CLI ALSO fails the same way for me — on Manual CIMD with external IdP.
The OP and several other commenters mention CLI as a known-working workaround.
I tested all three Claude clients today (claude.ai web, Claude Desktop on Windows, Claude Code CLI) against the same MCP server backed by Auth0 with Manual CIMD (DCR disabled).
All three exhibit the identical bearer-attach symptom: OAuth completes cleanly, the token is issued, then POST /mcp arrives without an Authorization header. So the "CLI works" workaround appears to be DCR-specific — folks with strict Manual CIMD + external IdP setups may want to retest before assuming CLI is an out.

tpark · 1 month ago

If anything sits between Claude and your origin (CDN / WAF / firewall / bot or rate-limit protection), check whether it is dropping Claude's egress IPs — that was the root cause in my case, and it presents exactly as "OAuth completes, Bearer never used."

I can confirm Claude's native connector itself works flawlessly — clean A/B: with the block ON it fails identically to the reports here; with it OFF it connects and calls tools with zero issues. So for the subset whose origin never receives the authenticated request, this is an edge/infra filter, not a Claude bug.

General diagnostic (any stack, ~1 min): tail your origin access log while connecting. If your token endpoint returns 200 but the subsequent authenticated MCP request never arrives at your origin, something upstream is dropping it. Claude's MCP tool-call requests come from Anthropic's documented egress range 160.79.104.0/21 (https://platform.claude.com/docs/en/api/ip-addresses) — a DIFFERENT range than the OAuth calls (which came from Azure IPs in my trace), which is why OAuth succeeds while the authenticated request is filtered. Allowlist 160.79.104.0/21 in whatever sits in front of your origin and re-test.

My case — Cloudflare Workers MCP server. wrangler tail:

POST /mcp-register   -> 201
GET  /mcp-authorize  -> 200
POST /mcp-authorize  -> 302
POST /mcp-token      -> 200   (valid token)
<nothing>                     <-- authenticated /mcp request never reaches the Worker

Cause: Security → Bots → "Block AI training bots" = Block on all pages, which drops 160.79.104.0/21 at the edge. Matches Anthropic's "allowlist Claude's outbound IPs through your WAF/firewall" guidance on the closed anthropics/claude-ai-mcp#326 / anthropics/claude-ai-mcp#327.

Cloudflare fix that keeps AI-bot protection (verified on the Free plan) — WAF Custom Rule:

Expression: (ip.src in {160.79.104.0/21} and starts_with(http.request.uri.path, "/mcp"))
Action:     Skip  →  [x] All managed rules  AND  [x] All Super Bot Fight Mode Rules   (BOTH)
Place at:   First

Gotcha: checking only "All managed rules" is not enough — the AI-bot block runs in the bot-fight pipeline, so you must also check "All Super Bot Fight Mode Rules" (works on the Free plan despite the name). Simpler alternative: set "Block AI training bots" to "Do not block".

This won't be everyone's cause here (the "fresh domain" workaround, the Databricks first-party report, and the CLI-also-fails reports point elsewhere), but if your origin never sees the authenticated request, rule this out first.

@Roscat @yjpark-lookpin — you both mentioned Cloudflare (tunnel / Access); worth checking on your zone.

Roscat · 1 month ago

@tpark — your diagnosis nailed it. Three weeks of OAuth debugging when the cause was exactly what you described.

My setup matched yours: Cloudflare zone with "Block AI Bots Scope" set to "Block on all pages". Disabled it, retried the connector, connected first try. Server logs immediately captured three Claude-User requests from 160.79.106.x — the requests that had been silently dropped at the edge for the last three weeks.

Specific data point for others reading this: in my case the blocked UA was Claude-User (not python-httpx or the IP range alone). Cloudflare's AI Crawl Control flagged Claude-User as an AI Crawler, and "Block AI Bots Scope: Block on all pages" enforced it at the edge before requests reached my Cloudflare tunnel. wrangler tail and origin logs showed zero traffic during the blocked period — looked exactly like the original report's symptom.

For anyone with Cloudflare in front of their MCP origin, the 60-second check: Cloudflare dashboard → AI Crawl Control → Security → look for Claude-User Unsuccessful: N where N > 0. That's the smoking gun.

For future readers — worth distinguishing: this means my earlier comments in this thread, while documenting a real Anthropic-side bug class that @alemarcha and @francismartens318 are genuinely hitting, weren't actually my specific cause. Mine was edge-level bot filtering, not the OAuth bearer-attach bug. Two different problems converging on the same symptom string.

Thank you for writing it up — saved me indefinite blocked time and a wrong-tree hunt I'd convinced myself was Anthropic-side.

grocco · 25 days ago

@tpark that was it! Thank you so much!

localden collaborator · 7 days ago

Thank you all for taking the time to report this and for the extensive server-side logging.

We traced the reference IDs from this thread. For the majority (15 of 17), OAuth completes and a valid bearer token is attached; the very first authenticated MCP request then receives a bare HTTP 403 with no WWW-Authenticate header, from the server or an edge layer in front of it. That signature is consistent with the Cloudflare "Block AI Bots" managed rule (or an equivalent bot-management rule) matching our Claude-User user agent, which is carried on the MCP request but not the earlier OAuth legs. The community diagnosis in this thread is correct.

The fix is to allowlist the Claude-User user agent, or the Anthropic connector egress range 160.79.104.0/21, in the bot-management or WAF configuration in front of the MCP endpoint.

Two of the reference IDs failed earlier in the flow, before the token exchange, and do not fit the same pattern; if either of those reporters is still blocked after the allowlist change, please open a fresh issue with a new reference ID and we will trace it separately.

Closing this one out. Please reopen with a fresh reference ID if the 403 persists after allowlisting.