[BUG] Claude Desktop doesn't connect to Custom MCPs altogether (not with OAuth 2.1 nor with SSE)

Resolved 💬 66 comments Opened Aug 15, 2025 by jakub-nezasa Closed Jun 9, 2026
💡 Likely answer: A maintainer (localden, collaborator) responded on this thread — see the highlighted reply below.

Hi Anthropics Team,

We have developed and deployed an MCP server with OAuth 2.1 authentication for the purposes of our company.

The deployed MCP works fantastically with Claude Code!
claude mcp add --transport http custom_mcp [redacted-url]

It requires the authentication, goes through the authentication process, lists all the tools and use them appropriately. All communication is logged via server logs.

When configured in Clade Desktop as a Connector with the same url, the service is never contacted. When clicking "Connect":

This time, no request reaches the deployed server. No single query, successful or not, is logged by the service. The communication seems to be broken when it reaches Claude servers.

Please investigate why Claude Desktop doesn't issue any requests (authentication or functional) to Custom Connectors.

View original on GitHub ↗

66 Comments

github-actions[bot] · 11 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/3515
  2. https://github.com/anthropics/claude-code/issues/3140
  3. https://github.com/anthropics/claude-code/issues/3273

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

jakub-nezasa · 11 months ago
sanidhya-p · 10 months ago

I am also facing the same issue. Is there any workaround for it?

dnjscksdn98 · 10 months ago

I also faced exactly the same issue

AlexMobiCraft · 10 months ago

I also faced exactly the same issue

anyoung-tableau · 10 months ago

I am also facing this issue. What's working in a variety of other clients (MCP Inspector, VS Code, Cursor, Cloudflare's AI Playground), Claude Desktop appears to not make the followup request to the resource_metadata URL that is provided in the WWW-Authenticate header when no Bearer token is provided on the Authorization header, so the OAuth dance stops prematurely.

dnjscksdn98 · 10 months ago

I solved the issue by allowing anthropic's outbound IP addresses to my WAF firewall

EDjur · 9 months ago

Also facing this exact issue - any updates? Works perfectly in Claude Code 👍

daukadolt · 9 months ago

Glad that I'm not the only one seeing this.

Debugged it through and through. I'll give a stab at Claude Code. Thanks!

EDjur · 9 months ago

Small update. I managed to get it working, albeit extremely flaky. Hard to tell if the flakiness is due to some state in Claude Desktop, or some bug in the app.

But as mentioned before in this thread by @anyoung-tableau, it seems that Claude Desktop doesn't read or care about the WWW-Authenticate metadata like resource_metadata etc.

Instead it seems to just test some various URLs like:

/.well-known/oauth-authorization-server/mcp
/.well-known/oauth-protected-resource/mcp
/.well-known/oauth-authorization-server/oidc

I tried adding redirects for these to my actual locations, but that didn't seem to work (but again, its hard to tell when it super flaky).

What did finally make it work was just proxying those requests directly (example with Fastify):

const createProxyHandler = (targetPath: string) => {
    return async (request: FastifyRequest, reply: FastifyReply) => {
        const resp = await server.inject({
            headers: request.headers,
            method: 'GET',
            url: `${issuer}${targetPath}`
        });
        const data = resp.json();
        return reply.type('application/json').send(data);
    };
};

server.get(path, createProxyHandler(target));

Despite this though, it seems to fail about 70% of the time with no clear reason. Whereas in Claude Code it works flawlessly.

jhiver · 9 months ago

+1, same issue here. here is a summary of the debugging session I had with Claude Code. There is 0 debugging on Claude Desktop which doesn't help. Tried both Streamable HTTP and SSE.

TL;DR: everything works fine in Claude Code but fails miserably in Claude Desktop. Long version below.

Claude Desktop SSE MCP Connection Failure - Debugging Summary

Problem Description

Custom MCP server with OAuth 2.0 SSE transport works perfectly with MCP Inspector but fails silently with Claude Desktop after OAuth completion.

Environment

  • Claude Desktop Version: 0.13.37 (Electron 37.5.1)
  • Server: Custom Rust MCP server using rmcp library
  • Transport: SSE (Server-Sent Events)
  • Authentication: OAuth 2.0 with Dynamic Client Registration (RFC 7591)
  • URL Pattern: https://{uuid}.saramcp.com

What Works ✅

  1. MCP Inspector Connection
  • OAuth discovery: ✅ PASS
  • Client registration: ✅ PASS
  • Authorization flow: ✅ PASS
  • Token exchange: ✅ PASS
  • SSE connection: ✅ PASS
  • initialize request: ✅ PASS
  • tools/list request: ✅ PASS (returns 2 tools)
  1. OAuth Implementation
  • All .well-known endpoints return correct metadata
  • Dynamic Client Registration works (RFC 7591)
  • Authorization code flow completes successfully
  • Access tokens and refresh tokens issued correctly
  • PKCE with S256 working
  1. Server Response Times
  • All OAuth endpoints: < 200ms
  • Metadata endpoints: < 1ms
  • Token generation: < 100ms

What Fails ❌

Claude Desktop completes OAuth but never establishes SSE connection.

Server Logs Evidence

# OAuth flow completes successfully:
2025-10-07 23:08:01 [DEBUG] POST /.oauth/token - 200 OK
2025-10-07 23:08:01 [DEBUG] Access token issued: mcp_token_***

# Expected next: SSE connection with Authorization header
# Actual: NOTHING. Zero requests.

# No GET / with Authorization header
# No initialize messages
# No tools/list requests
# Complete silence after token exchange

Network Tab Evidence (Chrome DevTools)

Using developer_settings.json with DevTools enabled in Claude Desktop:

{"allowDevTools": true}

Observed:

  • ✅ Metadata discovery requests visible
  • ✅ OAuth registration request visible
  • ✅ Token exchange request visible (200 OK)
  • NO SSE connection attempt to custom server
  • NO requests with Authorization header

Filter: Searching for domain in Network tab shows ZERO requests after OAuth completion.

Connector Behavior

  1. Click "Add Custom Connector" with URL https://{uuid}.saramcp.com
  2. OAuth browser window opens
  3. User logs in successfully
  4. Authorization granted
  5. Browser redirects back to claude.ai/new
  6. Connector disappears from settings (not shown as connected or disconnected)
  7. No error messages displayed to user
  8. No connection attempts logged on server

OAuth Metadata Comparison

Working Server (Doxyde.com)

{
  "oauth-authorization-server": "https://doxyde.com/.well-known/oauth-authorization-server",
  "protected-resources": [
    "https://sse.doxyde.com/",
    "https://sse.doxyde.com/message"
  ]
}

Our Server (Same Structure)

{
  "oauth-authorization-server": "https://{uuid}.saramcp.com/.well-known/oauth-authorization-server",
  "protected-resources": [
    "https://{uuid}.saramcp.com/",
    "https://{uuid}.saramcp.com/message"
  ]
}

Both use identical structure. Doxyde works, ours doesn't.

What We Tried (All Failed)

  1. ✅ Added response_modes_supported: ["query"] to auth server metadata
  2. ✅ Added resource field per RFC 9728
  3. ✅ Removed resource field to match working servers
  4. ✅ Tested different OAuth scopes
  5. ✅ Verified CORS headers on all endpoints
  6. ✅ Tested with public/organization/private access levels
  7. ✅ Checked token expiration times (3600s)
  8. ✅ Verified redirect URIs match exactly
  9. ✅ Tested SSE endpoints directly (work perfectly)
  10. ✅ Validated all JSON-RPC 2.0 responses

None of these changed the behavior.

Authorization Server Metadata (Full)

{
  "issuer": "https://{uuid}.saramcp.com",
  "authorization_endpoint": "https://{uuid}.saramcp.com/.oauth/authorize",
  "token_endpoint": "https://{uuid}.saramcp.com/.oauth/token",
  "registration_endpoint": "https://{uuid}.saramcp.com/.oauth/register",
  "scopes_supported": ["mcp:read"],
  "response_types_supported": ["code"],
  "response_modes_supported": ["query"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_methods_supported": ["none"],
  "code_challenge_methods_supported": ["S256", "plain"]
}

SSE Server Implementation

Library: rmcp (Rust MCP SDK) - official implementation
Endpoints:

  • GET / - SSE connection endpoint
  • POST /message - Message endpoint

Verified working with:

  • ✅ MCP Inspector
  • ✅ Direct SSE client testing
  • ✅ cURL with streaming

Hypothesis

The issue appears to be in Claude Desktop's OAuth-to-SSE bridge:

  1. OAuth proxy at claude.ai completes authentication ✅
  2. OAuth proxy receives access token ✅
  3. OAuth proxy should establish SSE connection to custom server
  4. Connection dies here - SSE connection never attempted

The bug is NOT in:

  • Server implementation (works with MCP Inspector)
  • OAuth flow (completes successfully)
  • SSE implementation (works when tested directly)

The bug IS in:

  • Claude Desktop's post-OAuth connection logic
  • Whatever happens after Claude receives the access token
  • The bridge between OAuth completion and SSE establishment

Debug Logs Access

Server logs: Full request/response logging with timestamps
Claude Desktop logs: ~/Library/Logs/Claude/mcp*.log (no errors shown)
DevTools: Network tab shows OAuth but no SSE connection

Reproducibility

100% reproducible across multiple attempts, different servers, different configurations.

Request for Anthropic

Could you please add more verbose logging to Claude Desktop's MCP connector system? Specifically:

  1. Log when OAuth completes successfully
  2. Log when attempting to establish SSE connection
  3. Log the exact URL being used for SSE
  4. Log any errors during SSE connection setup
  5. Show connection status in UI (connecting/failed/connected)

Currently there is ZERO feedback to developers when this fails. The connector just vanishes silently.

Workaround

None known. MCP Inspector works, but Claude Desktop does not. Claude Code (CLI) works fine.

---

Happy to provide more logs, traces, or test any debugging approaches. Server is running at production URL and can be tested anytime.

byosamah · 9 months ago

I also faced exactly the same issue

alfranz · 9 months ago

Same issue here

itspers · 8 months ago

Could be same problem as mine - https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1674
But in my case claude receives token as just just calls mcp without it..

@jhiver why 'Browser redirects back to claude.ai/new'? It should redirect to https://claude.ai/api/mcp/auth_callback, no?

CodeLuca · 8 months ago

Facing same issue

twchad · 8 months ago

My current theory is that Claude and MCP Inspector both are incorrectly executing the HTTP session handshake on HTTP streamable. When I switched both to sse, I was able to connect properly. Claude Desktop is still doing weird things however. The connection will persist in a chat conversation over many hours, but if I start a new chat, the tool calls fail for both the new chat and the existing chat. My server shows a proper session and refresh token exchange, but also a bunch of new auth requests from Claude. Very strange.

twchad · 8 months ago

And MCP calls in the client are just rate limited, so even if you get it to work it stinks. I asked Claude to make calls in quick succession and it only made seven relatively slow calls before breaking.

viniFiedler · 8 months ago

Any workarounds?

twchad · 8 months ago

Not that I've found yet. Works great for a few minutes, then all the tool calls start failing. I don't have this kind of auth problem with any other AI agent or LLM.

sebosamet · 8 months ago

Same issue here.
MCP connection works fine with ChatGPT, Mistral, Dust, but not with Claude.
Worked fine with Claude until October 26.
Auth looks ok but in the browser, at the end of the auth:
https://claude.ai/settings/connectors?&server=189fd97b-ed2c-45fd-92ee-27320c6cc04f&step=end_error
and then authenticate is called with no token.
We desperately tried to get some support from Anthropic for 8 days now, with no answer.

itspers · 8 months ago

This is crazy.. made whole protocol, made it popular, but dont care about own implementation.. give us at least ability to see some error log..

sebosamet · 8 months ago

Got an answer from Anthropic.
The problem was that in the authentication workflow, the initialize method (the one returned in WWW-Authenticate, in my case /oauth-protected-resource) returned 200 instead of 401.
Now /oauth-protected-resource returns the metadata in a 401 answer and it is fine.

I do not see the link between the fix and what I observed (and it used to work with a 200), but it does fix the issue.

twchad · 8 months ago

I can connect over SSE. I am returning a 401, so my issue isn't the same. Claude itself is convinced the issues on the platform are severe. The same tool call in the stretch of a couple minutes will fail or succeed intermittently. Failed calls never make it to the server. If you do get it to work, it quickly hits a weird ~6 tool call rate limit within one conversation. It's really quite bad in comparison to Claude Code, which works well with no problems.

biswapm · 8 months ago
Same issue here. MCP connection works fine with ChatGPT, Mistral, Dust, but not with Claude. Worked fine with Claude until October 26. Auth looks ok but in the browser, at the end of the auth: https://claude.ai/settings/connectors?&server=189fd97b-ed2c-45fd-92ee-27320c6cc04f&step=end_error and then authenticate is called with no token. We desperately tried to get some support from Anthropic for 8 days now, with no answer.

I'm facing a similar issue. Everything was working fine until last week, but it suddenly stopped. I'm now getting a start_error in the URL. I tried reaching out through the Fin bot, but it keeps repeating the same resolution and doesn't seem to understand the problem. how did you reach out them ?
https://claude.ai/settings/connectors?&server=e231fb70-965e-4fc4-b756-607319035894&step=start_error

sebosamet · 8 months ago
> Same issue here. MCP connection works fine with ChatGPT, Mistral, Dust, but not with Claude. Worked fine with Claude until October 26. Auth looks ok but in the browser, at the end of the auth: https://claude.ai/settings/connectors?&server=189fd97b-ed2c-45fd-92ee-27320c6cc04f&step=end_error and then authenticate is called with no token. We desperately tried to get some support from Anthropic for 8 days now, with no answer. I'm facing a similar issue. Everything was working fine until last week, but it suddenly stopped. I'm now getting a start_error in the URL. I tried reaching out through the Fin bot, but it keeps repeating the same resolution and doesn't seem to understand the problem. how did you reach out them ? https://claude.ai/settings/connectors?&server=e231fb70-965e-4fc4-b756-607319035894&step=start_error

We wrote to support@anthropic.com. It took them one week to answer.

twchad · 8 months ago

So. A couple of additional thoughts. I've had more initial connection stability since making sure that WWW-Authenticate headers were set properly. However, client side tool call failures are still intermittent. On SSE, Claude Desktop makes POST calls to the SSE endpoint that it shouldn't. And on httpstreamable, some tool calls just disappear. No server log for them and they fail client side.

anyoung-tableau · 7 months ago

I was able to resolve the issue I was having by doing 2 things:

  1. Add client_secret_post as an advertised auth method. Previously I was only advertising client_secret_basic (even though it was supported by the token endpoint) but Claude uses client_secret_post and posts client credentials on the token request body, not in the Authorization header.
  2. I had a bug where the WWW-Authenticate header always used http for the resource_metadata URL. Every other client I've used seems to "upgrade" it to https for me but Claude didn't and I hadn't noticed that until now. :)
gplhegde · 7 months ago

If you are providing Dynamic Client Registration and sending empty or null values in the registration response, such as logo_url: null, then it will silently fail. You should be omitting all the fields that are not applicable or not there.

Infact, it does not even send the resource parameter in the authorization request as per their own spec - https://modelcontextprotocol.io/specification/draft/basic/authorization#resource-parameter-implementation

Jorgevillada · 7 months ago

I have fixed the Dynamic Client Registration doing this things: (it now connects correctly).

  1. WWW-Authenticate Header
  2. .well-known/oauth-protected-resource returns http status code=401 and header www-authenticate:

Bearer realm="mcp", resource_metadata="https://mymcphost.com/.well-known/oauth-protected-resource"

  1. client_secret_post in token_endpoint_auth_methods_supported
  2. in registration_endpoint response must have this info.
{
    "client_id": "...",
    "client_secret": "...",
    "redirect_uris": ["https://claude.ai/api/mcp/auth_callback"],
    "token_endpoint_auth_method": "client_secret_post",  // ← same client_secret_post
    "grant_types": ["authorization_code", "password"], // ← password grant
    "registration_client_uri": "https://...",  // ← always a HTTPS
    "authorization_endpoint": "https://.../auth",  // ← always https
    "token_endpoint": "https://.../token",  // ← always https
    "jwks_uri": "https://.../certs",  // ← always https
    "issuer": "https://..."  // ← always https
  }

this is a claude request sent toregistration_endpoint

{
  "redirect_uris": [
    "https://claude.ai/api/mcp/auth_callback"
  ],
  "token_endpoint_auth_method": "client_secret_post",
  "grant_types": [
    "authorization_code",
    "refresh_token"
  ],
  "response_types": [
    "code"
  ],
  "scope": "service_account roles openid email offline_access phone basic acr groups mcp:read microprofile-jwt profile mcp:execute mcp:write address web-origins",
  "client_name": "Claude"
}
  1. in .well-known/oauth-authorization-server i put
{
...
   "mcp_extensions":{
      "client_id":"client-id",
      "discovery_service":{
         "keycloak_backend":"<url>",
         "note":"Dynamic client registration intermediary on HTTP Streaming service",
         "registration_endpoint":"<endpoint>",
         "registration_service":"<url_registration>"
      },
      "http_streaming_auth_required":true,
      "http_streaming_endpoint":"<mcp_url>",
      "http_streaming_session_timeout":600,
      "supported_transports":[
         "http_streaming"
      ],
      "version":"2025-06-18"
   }
...
}
Nihilentropy-117 · 7 months ago

`

Define your static credentials here (or pull from env vars for security)

You will provide these values to Claude.ai

STATIC_CLIENT_ID = os.getenv("OAUTH_CLIENT_ID", "mcp-obsidian-client")
STATIC_CLIENT_SECRET = os.getenv("OAUTH_CLIENT_SECRET", "super-secret-obsidian-key-change-this")

-----------------------------

OAuth storage

We pre-populate the allowed client immediately

oauth_clients: Dict[str, dict] = {
STATIC_CLIENT_ID: {
"client_id": STATIC_CLIENT_ID,
"client_secret": STATIC_CLIENT_SECRET,
# You might need to adjust this based on where Claude is redirecting
"redirect_uris": ["https://claude.ai/oauth/callback", "http://localhost:8080/callback"],
"client_name": "Claude"
}
}
oauth_codes: Dict[str, dict] = {}
oauth_tokens: Dict[str, dict] = {}

@app.post("/register")
async def register_client(request: Request):
"""
Dynamic Registration is disabled/modified to return the static credentials.
This ensures only your pre-defined ID/Secret works.
"""
body = await request.json()

# We ignore the request to create new credentials and return our static ones
return JSONResponse({
"client_id": STATIC_CLIENT_ID,
"client_secret": STATIC_CLIENT_SECRET,
"client_id_issued_at": 1234567890,
"redirect_uris": body.get("redirect_uris", [])
})

@app.get("/authorize")
async def authorize(
request: Request,
response_type: str = None,
client_id: str = None,
redirect_uri: str = None,
state: str = None,
code_challenge: str = None,
code_challenge_method: str = None
):
"""OAuth Authorization Endpoint"""
# Check if the requested Client ID matches our static one
if client_id != STATIC_CLIENT_ID:
return HTMLResponse(f"<h1>Invalid client: {client_id}</h1>", status_code=400)

# Auto-approve (skip user consent for simplicity)
code = secrets.token_urlsafe(32)
oauth_codes[code] = {
"client_id": client_id,
"redirect_uri": redirect_uri,
"code_challenge": code_challenge,
"code_challenge_method": code_challenge_method
}

logger.info(f"Issued authorization code for client {client_id}")

# Redirect back to Claude with the code
separator = "&" if "?" in redirect_uri else "?"
redirect_url = f"{redirect_uri}{separator}code={code}"
if state:
redirect_url += f"&state={state}"

return RedirectResponse(redirect_url)

@app.post("/token")
async def token_endpoint(
request: Request,
grant_type: str = Form(None),
code: str = Form(None),
redirect_uri: str = Form(None),
client_id: str = Form(None),
client_secret: str = Form(None),
code_verifier: str = Form(None)
):
"""OAuth Token Endpoint"""
if grant_type != "authorization_code":
return JSONResponse({"error": "unsupported_grant_type"}, status_code=400)

if code not in oauth_codes:
return JSONResponse({"error": "invalid_grant"}, status_code=400)

code_data = oauth_codes[code]

# 1. Validate Client ID
if code_data["client_id"] != client_id:
return JSONResponse({"error": "invalid_client_id"}, status_code=400)

# 2. Validate Client Secret (ADDED THIS CHECK)
# We check against our stored static client
stored_client = oauth_clients.get(client_id)
if not stored_client or stored_client["client_secret"] != client_secret:
logger.warning(f"Failed secret check for client {client_id}")
return JSONResponse({"error": "invalid_client_secret"}, status_code=401)

# Generate access token
access_token = secrets.token_urlsafe(32)
oauth_tokens[access_token] = {
"client_id": client_id,
"scope": "mcp"
}

# Clean up used code
del oauth_codes[code]

logger.info(f"Issued access token for client {client_id}")

return JSONResponse({
"access_token": access_token,
"token_type": "Bearer",
"expires_in": 3600
})`

This Works

Ken-Gilb · 7 months ago

My MCP server broke some time after October. For those looking for a fix I was able to correct it by making sure my WWW-Authenticate header had the resource_metadata value set to https. My server is behind a reverse proxy and was returning http instead of https. This apparently used to work (and still works with MCP Inspector) but no longer works with claude.

FIX: ensure resource_metadata="https://your-server.com/.well-known/oauth-protected-resource"

bortolatto · 7 months ago
My MCP server broke some time after October. For those looking for a fix I was able to correct it by making sure my WWW-Authenticate header had the resource_metadata value set to https. My server is behind a reverse proxy and was returning http instead of https. This apparently used to work (and still works with MCP Inspector) but no longer works with claude. FIX: ensure resource_metadata="https://your-server.com/.well-known/oauth-protected-resource"

I'm experiencing the same issue. Would you mind sharing what your .well-known/oauth-protected-resource endpoint returns?
In my case, the response looks like this (with sensitive data removed):

{
  "resource": "https://my-server/requisicao/mcp",
  "authorization_servers": [
    "https://my-idp-provider/auth/realms/my-realm"
  ],
  "jwks_uri": "https://my-idp-provider/auth/realms/my-realm/protocol/openid-connect/certs",
  "scopes_supported": [
    "openid",
    "profile",
    "email",
    "claudeai"
  ],
  "bearer_methods_supported": [
    "header",
    "body"
  ]
}

Logs from my server:

10.103.0.121 - - [05/Dec/2025:19:49:01 +0000] "POST /my-server/mcp HTTP/1.1" 401 218 "-" "Claude-User" METHOD: "POST" URI: "/my-server/mcp" QUERY_STRING: "-" ARGS: "-" FULL_URI: "/my-server/mcp-" HOST: "my-server" X-TENANT: "-" CONTENT-TYPE: "application/json" UPSTREAM_STATUS: "401" UPSTREAM_TIME: "0.002"

[RESPONSE] 10.103.0.121 [05/Dec/2025:19:49:01 +0000] "POST /my-server/mcp HTTP/1.1" STATUS=401 RESP_TIME=0.002 BODY_SIZE=218 HEADERS={
  Content-Type:"application/json; charset=utf-8"
  Content-Length:"218"
  WWW-Authenticate:"Bearer resource_metadata=https://my-server/.well-known/oauth-protected-resource"
  CORS-Origin:"*"
  CORS-Methods:"GET, POST, PUT, DELETE, OPTIONS"
  CORS-Headers:"Content-Type, Authorization, Mcp-Session-Id, Last-Event-Id, mcp-protocol-version, Cache-Control, Accept, X-Tenant"
  CORS-Expose:"Mcp-Session-Id, Mcp-Session-Id"
  CORS-Creds:"true, true"
  MCP-Session:"-"
  Location:"-"
  Cache-Control:"-"
}
matija2209 · 7 months ago

I ran into most of these same issues while implementing OAuth for my MCP server. Ended up writing down everything I learned: [OAuth for MCP Server: Complete Guide to Protecting Claude](https://www.buildwithmatija.com/blog/oauth-mcp-server-claude)

A few things that caught me:

  • client_secret_post needs to be in token_endpoint_auth_methods_supported (Claude Web uses this specifically)
  • Don't return null values in the registration response - omit fields instead of setting them to null
  • resource_metadata in WWW-Authenticate must be https:// even behind reverse proxies
  • The approval flow needs GET redirects, not POST (mcp-remote's callback server rejects POST)
  • Vercel's firewall can block .well-known endpoints - you need to add exceptions

The guide has full Next.js code examples and walks through the complete OAuth 2.1 setup. Hope it saves someone the debugging time.

boogey100 · 7 months ago

We're having exactly the same problem. These connectors are the only reason we have standard users subscribed in our team plan. If it isn't fixed, we'll unsubscribe them.

boogey100 · 7 months ago
I ran into most of these same issues while implementing OAuth for my MCP server. Ended up writing down everything I learned: [OAuth for MCP Server: Complete Guide to Protecting Claude](https://www.buildwithmatija.com/blog/oauth-mcp-server-claude) A few things that caught me: client_secret_post needs to be in token_endpoint_auth_methods_supported (Claude Web uses this specifically) Don't return null values in the registration response - omit fields instead of setting them to null resource_metadata in WWW-Authenticate must be https:// even behind reverse proxies The approval flow needs GET redirects, not POST (mcp-remote's callback server rejects POST) * Vercel's firewall can block .well-known endpoints - you need to add exceptions The guide has full Next.js code examples and walks through the complete OAuth 2.1 setup. Hope it saves someone the debugging time.

I've tried walking through this but haven't had any luck. It authenticates fine but doesn't make any calls once it's authenticated. Will add some details when I get a chance.

matija2209 · 7 months ago
> I ran into most of these same issues while implementing OAuth for my MCP server. Ended up writing down everything I learned: [OAuth for MCP Server: Complete Guide to Protecting Claude](https://www.buildwithmatija.com/blog/oauth-mcp-server-claude) > A few things that caught me: > > client_secret_post needs to be in token_endpoint_auth_methods_supported (Claude Web uses this specifically) > Don't return null values in the registration response - omit fields instead of setting them to null > resource_metadata in WWW-Authenticate must be https:// even behind reverse proxies > The approval flow needs GET redirects, not POST (mcp-remote's callback server rejects POST) > * Vercel's firewall can block .well-known endpoints - you need to add exceptions > > The guide has full Next.js code examples and walks through the complete OAuth 2.1 setup. Hope it saves someone the debugging time. I've tried walking through this but haven't had any luck. It authenticates fine but doesn't make any calls once it's authenticated. Will add some details when I get a chance.

Looking at the logs was a saver for me. Are the /mcp/see POST requests coming through (Mine were 401). Another one is the firewall

boogey100 · 7 months ago
> > I ran into most of these same issues while implementing OAuth for my MCP server. Ended up writing down everything I learned: [OAuth for MCP Server: Complete Guide to Protecting Claude](https://www.buildwithmatija.com/blog/oauth-mcp-server-claude) > > A few things that caught me: > > > > client_secret_post needs to be in token_endpoint_auth_methods_supported (Claude Web uses this specifically) > > Don't return null values in the registration response - omit fields instead of setting them to null > > resource_metadata in WWW-Authenticate must be https:// even behind reverse proxies > > The approval flow needs GET redirects, not POST (mcp-remote's callback server rejects POST) > > * Vercel's firewall can block .well-known endpoints - you need to add exceptions > > > > The guide has full Next.js code examples and walks through the complete OAuth 2.1 setup. Hope it saves someone the debugging time. > > > I've tried walking through this but haven't had any luck. It authenticates fine but doesn't make any calls once it's authenticated. Will add some details when I get a chance. Looking at the logs was a saver for me. Are the /mcp/see POST requests coming through (Mine were 401). Another one is the firewall

| # | Check | Status | Details |
|-----|---------------------------------------|--------|-------------------------------------------------------------------------------------------------|
| 1 | Auth Server Metadata | ✅ | issuer, authorization_endpoint, token_endpoint, registration_endpoint all present with https:// |
| 2 | token_endpoint_auth_methods_supported | ✅ | Includes client_secret_post, client_secret_basic, none |
| 3 | code_challenge_methods_supported | ✅ | Includes S256 |
| 4 | grant_types_supported | ✅ | Includes authorization_code, refresh_token |
| 5 | Protected Resource returns 200 | ✅ | HTTP 200 (not 401) |
| 6 | resource field uses https:// | ✅ | https://mcp.example.com and https://mcp.example.com/sse |
| 7 | Dynamic Client Registration | ✅ | Returns 201 with client_id, client_secret, correct fields |
| 8 | No null values in registration | ✅ | 0 null values found |
| 9 | WWW-Authenticate header | ✅ | resource_metadata uses https:// URL |
| 10 | CORS on OAuth metadata | ✅ | access-control-allow-origin: * |
| 11 | CORS on Protected Resource | ✅ | Full CORS headers present |
| 12 | OPTIONS preflight | ✅ | Returns 204 with proper CORS for https://claude.ai |
| 13 | Health check | ✅ | Server responding |

What Works ✅

Claude Code CLI - Works perfectly:

claude mcp add code-search --transport http https://mcp.example.com
  • OAuth flow completes
  • Tools are listed and functional
  • All requests logged on the server

Manual curl testing - Works perfectly:

# OAuth metadata discovery
curl https://mcp.example.com/.well-known/oauth-authorization-server
# ✅ Returns valid metadata

# Dynamic Client Registration
curl -X POST https://mcp.example.com/register -H "Content-Type: application/json" \
  -d '{"redirect_uris":["https://example.com/callback"],"client_name":"test"}'
# ✅ Returns client_id and client_secret

# MCP Initialize (post-auth)
curl -X POST https://mcp.example.com/ -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}'
# ✅ Returns session ID and server capabilities

MCP Inspector - Works perfectly with both transport modes.

What Fails ❌

Claude.ai Web Connector:

  1. Add custom connector with server URL https://mcp.example.com or https://mcp.example.com/sse
  2. OAuth flow initiates correctly
  3. Browser shows consent page, I click "Authorize"
  4. Authorization code issued, redirect happens
  5. Nothing reaches my server after this point
  6. Claude.ai connector shows "Disconnected"

Server Logs During Claude.ai Attempt

[2024-12-08T19:42:15Z] GET /.well-known/oauth-authorization-server - Origin: https://claude.ai
[2024-12-08T19:42:16Z] POST /register - Client registration request
[2024-12-08T19:42:16Z] Registered client: abc123...
[2024-12-08T19:42:20Z] GET /oauth/authorize - Authorization request for client: abc123...
[2024-12-08T19:42:25Z] GET /oauth/authorize/approve - Authorization approval for client: abc123...
[2024-12-08T19:42:25Z] Authorization code issued: def456...
[2024-12-08T19:42:26Z] POST /oauth/token - Token exchange
[2024-12-08T19:42:26Z] Token issued for client: abc123...
# ^^^ OAuth completes successfully ^^^
# --- NOTHING AFTER THIS POINT ---
# No MCP initialize request
# No POST to /, /mcp, or /sse

The server receives the token exchange request and successfully issues a token, but Claude.ai never proceeds to establish the MCP connection.

Server Implementation Details

  • Protocol: MCP 2025-03-26 (Streamable HTTP)
  • Auth: OAuth 2.1 with Dynamic Client Registration (RFC 7591), PKCE (S256)
  • Endpoints: /, /mcp, /sse (all support Streamable HTTP)
  • SDK: @modelcontextprotocol/sdk latest
  • Tunnel: Cloudflare Tunnel (valid SSL, no certificate issues)

I've implemented all required OAuth metadata endpoints:

  • /.well-known/oauth-authorization-server
  • /.well-known/oauth-protected-resource
  • /register (Dynamic Client Registration)
  • /oauth/authorize + /oauth/authorize/approve
  • /oauth/token (authorization_code + refresh_token grants)

Conclusion

The OAuth flow is completing correctly (verified by server logs showing successful token issuance). The breakdown occurs after token exchange - Claude.ai's infrastructure isn't proceeding to send the MCP initialize request. This matches what others are reporting: the requests never leave Claude's side.

arnaldo-delisio · 7 months ago

Potential Workaround: Manual OAuth Credentials

If you're experiencing connection issues with OAuth 2.1, try using manual client credentials instead of automatic discovery:

  1. When adding your custom MCP server in Claude Desktop
  2. Click "Advanced settings"
  3. Manually enter your OAuth Client ID and OAuth Client Secret

Why This May Help

Several users (including myself on Claude.ai web/mobile) have found that manual credential entry bypasses OAuth initialization issues that occur during automatic discovery.

OAuth Providers That Support This

Worth testing if you're stuck with authentication failures.

boogey100 · 6 months ago

I finally got it going!

Root Cause

Cloudflare Tunnel buffers SSE (Server-Sent Events) responses, preventing real-time streaming required by the MCP Streamable HTTP transport.

The connection flow would succeed up to:

  1. ✅ Client registration (POST /register)
  2. ✅ OAuth authorization (GET /oauth/authorize)
  3. ✅ Token exchange (POST /oauth/token)
  4. ✅ MCP initialize (POST / with initialize)
  5. ✅ SSE stream establishment (GET / with Accept: text/event-stream)
  6. ✅ Notifications initialized (POST / with notifications/initialized)
  7. ❌ tools/list never sent - Claude Web/Desktop doesn't proceed

The SSE GET request returns HTTP 200, but Cloudflare buffers the event stream data instead of streaming it in real-time. Claude Web/Desktop waits for SSE events that never arrive, then times out.

Evidence

  • Server logs showed SSE stream "established" but no subsequent tools/list request
  • Same server worked perfectly via ngrok (no SSE buffering)
  • Cloudflare's disableChunkedEncoding: true setting didn't help
  • This is a https://github.com/cloudflare/cloudflared/issues/1449 - GET-based SSE is buffered while POST requests stream correctly

Solution

Use ngrok instead of Cloudflare Tunnel:
ngrok http <mcp-server-port>

ngrok streams SSE responses correctly, and Claude Web/Desktop connects successfully.

Other Findings

  1. Non-standard ports (e.g., :8443) don't work with Claude Web/Desktop - it appears to only support standard HTTPS port 443
  2. The MCP SDK's StreamableHTTPServerTransport requires clients to accept text/event-stream - this is enforced in the SDK itself
  3. OAuth metadata must include registration_endpoint for dynamic client registration to work
matteoscurati · 6 months ago

Additional Data Point: Cloudflare Workers (not Tunnel) - Same Issue

I wanted to contribute another data point that might help narrow down this issue.

Our Setup

We're running an OAuth 2.1-protected MCP server on Cloudflare Workers (https://api.8004.dev) using:

  • Hono.js framework
  • Full OAuth 2.1 implementation with Dynamic Client Registration (RFC 7591)
  • SSE endpoint at /sse, JSON-RPC at /mcp
  • PKCE support for Claude Desktop authentication

Important distinction: We're using Cloudflare Workers, not Cloudflare Tunnel. This means the SSE buffering issue mentioned by @boogey100 in cloudflare/cloudflared#1449 does not apply to our infrastructure. Workers streams SSE natively without buffering.

What Works ✅

  • Claude Code CLI: Works perfectly with claude mcp add --transport http
  • Direct API calls: OAuth flow completes successfully (we see token exchange in logs)
  • mcp-remote bridge: Works flawlessly with Claude Desktop using:

``json
{
"mcpServers": {
"8004-agents": {
"command": "npx",
"args": ["mcp-remote", "https://api.8004.dev/sse"]
}
}
}
``

What Doesn't Work ❌

Claude.ai web custom connectors fail after OAuth completes:

  1. OAuth flow succeeds (we receive and validate the token)
  2. Claude.ai sends GET /sse with valid Bearer token
  3. We respond with proper SSE stream
  4. Connection is CANCELED by the client after ~1-2 seconds regardless of our response
  5. Status shows "Disconnected"

What We've Tried (all unsuccessful)

  • ✗ Different SSE formats (comments only, events, JSON-RPC notifications)
  • ✗ Proactive initResponse sending vs. only event: endpoint
  • ✗ JSON response instead of SSE stream
  • ✗ Various keepalive intervals (5s, 15s, 30s)
  • ✗ Session persistence in Cloudflare KV
  • ✗ Multiple protocol versions (2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25)

Key Insight

Since our infrastructure is Cloudflare Workers (not Tunnel), we can rule out the SSE buffering/flush issues entirely. Our SSE streams work perfectly with:

  • mcp-remote (npx package)
  • Claude Code CLI
  • Direct browser connections
  • curl with SSE streaming

The fact that the same SSE endpoint works with Claude Code CLI but fails with Claude.ai/Claude Desktop suggests the issue is client-side in how these clients handle OAuth-protected SSE connections.

Current Workaround

Using mcp-remote as a bridge successfully bypasses the OAuth issue:

{
  "mcpServers": {
    "8004-agents": {
      "command": "npx",
      "args": ["mcp-remote", "https://api.8004.dev/sse"]
    }
  }
}

This works because mcp-remote handles the OAuth flow correctly and maintains the SSE connection, then bridges it to Claude Desktop via stdio.

Hypothesis

Given that:

  1. The OAuth flow completes successfully (tokens are issued and validated)
  2. The SSE connection is established (we see the GET request with valid auth)
  3. The connection is terminated by the client after 1-2 seconds
  4. The same endpoint works perfectly with other clients

This points to a client-side timeout or connection handling issue in Claude.ai/Claude Desktop when dealing with OAuth-protected SSE endpoints, rather than an infrastructure or SSE format problem.

Would it be possible to get more verbose client-side logging to see why the connection is being canceled?

---

Server implementation: api.8004.dev (open source, Cloudflare Workers + Hono.js)
Live endpoints:

ehudsn · 6 months ago
I finally got it going! Root Cause Cloudflare Tunnel buffers SSE (Server-Sent Events) responses, preventing real-time streaming required by the MCP Streamable HTTP transport. The connection flow would succeed up to: 1. ✅ Client registration (POST /register) 2. ✅ OAuth authorization (GET /oauth/authorize) 3. ✅ Token exchange (POST /oauth/token) 4. ✅ MCP initialize (POST / with initialize) 5. ✅ SSE stream establishment (GET / with Accept: text/event-stream) 6. ✅ Notifications initialized (POST / with notifications/initialized) 7. ❌ tools/list never sent - Claude Web/Desktop doesn't proceed The SSE GET request returns HTTP 200, but Cloudflare buffers the event stream data instead of streaming it in real-time. Claude Web/Desktop waits for SSE events that never arrive, then times out. Evidence Server logs showed SSE stream "established" but no subsequent tools/list request Same server worked perfectly via ngrok (no SSE buffering) Cloudflare's disableChunkedEncoding: true setting didn't help This is a 🐛 SSE over GET is not streamed in real-time on Quick Tunnel — all data is flushed only after the server closes the connection cloudflare/cloudflared#1449 - GET-based SSE is buffered while POST requests stream correctly Solution Use ngrok instead of Cloudflare Tunnel: ngrok http ngrok streams SSE responses correctly, and Claude Web/Desktop connects successfully. Other Findings 1. Non-standard ports (e.g., :8443) don't work with Claude Web/Desktop - it appears to only support standard HTTPS port 443 2. The MCP SDK's StreamableHTTPServerTransport requires clients to accept text/event-stream - this is enforced in the SDK itself 3. OAuth metadata must include registration_endpoint for dynamic client registration to work

We don't use tunnels but we do use Cloudflare, and this comment got me thinking, so I went in and turned off the proxy in Cloudflare, and sure enough, it works if the proxy is turned off.

That's 4 hours of my life I'll never get back, but this post right here saved me the rest of the night at least.

andrewbuckingham · 6 months ago
I finally got it going! [...snip] Solution Use ngrok instead of Cloudflare Tunnel: ngrok http ngrok streams SSE responses correctly, and Claude Web/Desktop connects successfully. Other Findings 1. Non-standard ports (e.g., :8443) don't work with Claude Web/Desktop - it appears to only support standard HTTPS port 443 2. The MCP SDK's StreamableHTTPServerTransport requires clients to accept text/event-stream - this is enforced in the SDK itself 3. OAuth metadata must include registration_endpoint for dynamic client registration to work

Truly superb work, thank you. I had exactly this problem, and been all around the houses modifying the ModelContextProtocol.CSharkSdk thinking it must be a deficiency in that implementation. Turns out it was CloudFlare proxy the whole time. Amazing job @boogey100 !

jsbattig · 6 months ago

We got our remote MCP server working properly. Using just the remote MCP URL, and also adding the advanced ClientID/Secret option (that work like implicit creds).
The trick was to use standard port 443 for the server (and all redirects). What doesn't work at all is if you use a port other than 443... that's it.

matija2209 · 6 months ago

I had to rebuild mpc-handler from scratch. I got streamed HTTP working which is an upgrade from now legacy SSE. It works in Claude, ChatGPT, ...

thesobercoder · 6 months ago
> I finally got it going! > Root Cause > Cloudflare Tunnel buffers SSE (Server-Sent Events) responses, preventing real-time streaming required by the MCP Streamable HTTP transport. > The connection flow would succeed up to: > > 1. ✅ Client registration (POST /register) > 2. ✅ OAuth authorization (GET /oauth/authorize) > 3. ✅ Token exchange (POST /oauth/token) > 4. ✅ MCP initialize (POST / with initialize) > 5. ✅ SSE stream establishment (GET / with Accept: text/event-stream) > 6. ✅ Notifications initialized (POST / with notifications/initialized) > 7. ❌ tools/list never sent - Claude Web/Desktop doesn't proceed > > The SSE GET request returns HTTP 200, but Cloudflare buffers the event stream data instead of streaming it in real-time. Claude Web/Desktop waits for SSE events that never arrive, then times out. > Evidence > > Server logs showed SSE stream "established" but no subsequent tools/list request > Same server worked perfectly via ngrok (no SSE buffering) > Cloudflare's disableChunkedEncoding: true setting didn't help > This is a 🐛 SSE over GET is not streamed in real-time on Quick Tunnel — all data is flushed only after the server closes the connection cloudflare/cloudflared#1449 - GET-based SSE is buffered while POST requests stream correctly > > Solution > Use ngrok instead of Cloudflare Tunnel: ngrok http > ngrok streams SSE responses correctly, and Claude Web/Desktop connects successfully. > Other Findings > > 1. Non-standard ports (e.g., :8443) don't work with Claude Web/Desktop - it appears to only support standard HTTPS port 443 > 2. The MCP SDK's StreamableHTTPServerTransport requires clients to accept text/event-stream - this is enforced in the SDK itself > 3. OAuth metadata must include registration_endpoint for dynamic client registration to work We don't use tunnels but we do use Cloudflare, and this comment got me thinking, so I went in and turned off the proxy in Cloudflare, and sure enough, it works if the proxy is turned off. That's 4 hours of my life I'll never get back, but this post right here saved me the rest of the night at least.

You saved at least 24 hours for me. Although I'm a little worried about getting DDOSed without orange cloud turned on.

github-actions[bot] · 5 months ago

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

coliver-yext · 5 months ago

Adding another data point here with the official Granola MCP server (https://mcp.granola.ai/mcp), which uses Streamable HTTP transport with OAuth.

Symptoms (identical to OP):

  • Added Granola as a custom connector in Claude Desktop settings — it appears in the list as "CUSTOM"
  • Clicking Configure shows "Tool permissions: Choose when Claude is allowed to use these tools" but zero tools listed
  • No OAuth prompt is ever triggered
  • The connector cannot be toggled on in chat — toggle does nothing
  • No error message is displayed anywhere — fails completely silently

Works in Claude Code CLI:

$ claude mcp add --transport http granola https://mcp.granola.ai/mcp
$ claude mcp list
granola: https://mcp.granola.ai/mcp (HTTP) - ✓ Connected

Inconsistent behavior across users:
A colleague on the same Granola workspace was able to connect the same Granola MCP server in Cowork mode successfully — it completed OAuth, exposed tools, and retrieved meeting data. Same connector URL, same Granola plan. So this doesn't appear to be a universal transport or protocol issue — it's intermittent or user/session-specific.

Resolution: Disconnecting and reconnecting the connector in Desktop settings fixed it. OAuth triggered on the second attempt, tools appeared, and the connector is now functional in Cowork. No changes to the server or account — just a disconnect/reconnect cycle.

This suggests the initial connection attempt gets into a bad state silently, and the UI has no mechanism to surface the failure or retry. The workaround is simple, but the problem is that a user would never know to try it — from the UI it looks like the connector just doesn't work.

For comparison: Atlassian's MCP connector (https://mcp.atlassian.com/v1/sse, SSE transport) connected on the first attempt without issue on the same machine.

Environment: macOS, Claude Desktop (latest), Cowork mode

rexplx · 5 months ago

Same issue with Nia MCP on Claude.ai (Feb 2026)

Experiencing the same connection failure when adding Nia's remote MCP server (https://apigcp.trynia.ai/mcp) as a custom connector on Claude.ai & Claude Desktop

Error:

"There was an error connecting to the MCP server. Please check your server URL and make sure your server handles auth correctly. If this persists, share this reference with support: a3f5497cb3ce3f7e"

Key details:

  • Nia uses OAuth authentication
  • The same Nia MCP server works perfectly in Claude Code (plugin) and Claude Desktop (local stdio transport)
  • Only fails when connecting via Claude.ai web custom connector
  • OAuth flow never initiates — error appears immediately on clicking "Connect"
  • Error reference: a3f5497cb3ce3f7e

Environment: Claude.ai Max plan, macOS

justcodebruh · 4 months ago

bumping this bug. This is forcing all MCPs to go through the Claude marketplace as opposed to allowing for custom MCP use.

snboyle16 · 4 months ago

Experiencing this issue as well, unable to connect to Braintrust MCP because of this, is there an ETA on when this will be fixed??

barrettpyke · 4 months ago

Experiencing this issue trying to connect to the custom Braintrust MCP at https://api.braintrust.dev/mcp. The first issue took the user to the auth page in browser but displayed the below error message after the flow was complete. The second issue did not ever make it to the browser and showed the same error.

Error Reference: 72231b3347ba4a24

The same MCP server works with Claude Code.

There was an error connecting to the MCP server. Please check your server URL and make sure your server handles auth correctly.

richardARPANET · 4 months ago

Also seeing this. Get it fixed ASAP

zzynic · 4 months ago

Same issue. Can't connect to n8n.

shahedsj · 4 months ago

is this still an isssue

drei023 · 4 months ago

Very much so.. all remote-hosted MCPs (Cloudflare Workers, for example) are unable to connect, so anyone using iOS/iPad dev environments or agents instead of desktop is down. Hoping for a fix 𝘴𝘰𝘰𝘯..

For reference, see also issue #11814:
https://github.com/anthropics/claude-code/issues/11814

drei023 · 4 months ago

I have a solution! (Tested)

Turns out it’s not a “bug” on Anthropic’s end, but rather specific requirements regarding WAF rules, 401 authentication, and DCR (also make sure you’re not blocking bots and crawlers). If remote hosting your MCP server on CloudFlare as Worker, you’ll also need to create a KV namespace and Bind to it. Here’s the entire procedure:

Prerequisites (one-time per worker)

  1. Worker created in Cloudflare dashboard with the worker.js code pasted in
  2. Custom subdomain mapped (e.g. wp.tantricawakening.net)
  3. BEARER_TOKEN secret set in Worker settings
  4. KV namespace created, named MCP_DATA, bound to the worker as variable MCP_DATA
  5. WAF Custom Rule: User-Agent contains “Claude” - skip all security checks

worker.js requirements (the v4 pattern)
∙ GET /.well-known/oauth-authorization-server - includes registration_endpoint
∙ GET /.well-known/oauth-protected-resource
∙ POST /register - Dynamic Client Registration, stores in KV
∙ GET /authorize - generates one-time code, stores in KV, redirects back
∙ POST /token - exchanges code for BEARER_TOKEN
∙ POST /mcp - returns 401 + WWW-Authenticate when unauthenticated
∙ All above must use env.MCP_DATA for KV ops
Connecting in Claude.ai

  1. Settings - Connectors - Add custom connector
  2. URL: https://[subdomain].tantricawakening.net/mcp
  3. Click Add - click Connect - complete OAuth flow
  4. Done. Configure button optional (use to toggle individual tools).

If you’re still running into errors upon trying to Connect, type your exact error code into Gemini, it knows them all and will offer suggestions. Gemini seems to know Claude better than Claude.. haha.

ridafkih · 4 months ago

I faced two issues building the MCP server for https://github.com/ridafkih/keeper.sh with better-auth. Putting this here in case anyone else runs into any issues.

  1. Claude Desktop will request every scope listed in the support scopes, sanity check request them as a third-party would, and ensure they're in clientRegistrationAllowedScopes.
  2. Claude Desktop sends token_endpoint_auth_method: "client_secret_post" in the registration request, which better-auth rejects without being already authenticated, but better-auth doesn't downgrade this to 'none' and instead rejects the request outright with a 401. You need to downgrade it manually.

If all your /.well-known/... endpoints are configured correctly after that, then you should be good to go. Worked for me once this was done.

tspython · 3 months ago

Please fix, if developers are not able to connect claude reliably to developer tools this will make developers move off the claude platform.

shahedsj · 3 months ago

I been waiting for this for last one month Sent from my iPhoneOn Mar 29, 2026, at 8:44 PM, Tushar Shrivastav @.***> wrote:tspython left a comment (anthropics/claude-code#5826)
Please fix, if developers are not able to connect claude reliably to developer tools this will make developers move off the claude platform.

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you commented.Message ID: @.***>

loning · 3 months ago

For those stuck on OAuth/SSE connections with custom MCP servers — NyxID (https://github.com/ChronoAIProject/NyxID) can act as a local proxy that handles the auth layer separately from the MCP transport.

Instead of debugging the OAuth 2.1 handshake between Claude Desktop and your MCP server, you point Claude at NyxID's endpoint. NyxID handles credential injection and token refresh at the proxy layer, forwarding authenticated requests to your actual MCP server.

This decouples the auth problem from the transport problem — if SSE works but OAuth doesn't (or vice versa), NyxID lets you solve them independently.

Self-hosted and open source: https://github.com/ChronoAIProject/NyxID
Hosted version in closed beta — DM me for an invite code.

EthosUnlimited · 2 months ago

We're hitting the same issue with a WordPress-based MCP server and Cowork connectors (not Claude Code CLI — that path works fine for us too).
Our setup: OAuth 2.1 with PKCE, DCR, RFC 9728 protected resource metadata, RFC 8414 AS metadata, RFC 9207 iss parameter, public client support, Streamable HTTP transport. Server is on WP Engine staging.
What we can confirm from our side:

Full E2E test passes manually: DCR → PKCE → code exchange → bearer token → 17 tools returned. The OAuth server is correct.
All endpoints reachable from cloud infrastructure (verified via curl from multiple IPs).
All .well-known metadata documents return correct JSON with valid registration_endpoint, token_endpoint, code_challenge_methods_supported, etc.

What our audit log shows:
We have server-side audit logging on every OAuth event. When Cowork attempts to connect:

Without pre-registered credentials: Cowork's server IP hits our MCP endpoint, gets the expected 401 with WWW-Authenticate: Bearer resource_metadata="...". Then nothing. Zero metadata discovery requests. Zero DCR requests. Zero authorize requests. Immediate step=start_error in the Cowork callback URL.
With pre-registered credentials (client_id + client_secret in advanced settings): The flow gets further. Consent screen appears, user clicks Allow, auth code is issued, browser reaches claude.ai/api/mcp/auth_callback?code=XXX&state=YYY&iss=ZZZ. Then Cowork's backend never calls our token endpoint. Auth code stays used=0. Callback URL shows step=end_error&error_code=mcp_token_exchange_failed.

We can distinguish Cowork's server IP from our test traffic in the audit log. Cowork's IP produces only oauth.fail 401 events (the initial probe). Every DCR registration, token exchange, and successful auth in the log is from our manual testing.
Cowork flow IDs: ofid_27429fd45ba0de2c, ofid_3b8cd7caacc4eab9, ofid_a799fc9f773051a2, ofid_67d6f2f63977f127, ofid_756e0939199e36ab
What we ruled out: Safe Browsing, wp_safe_redirect, code TTL, missing iss parameter, public client auth method mismatch, short alias redirects losing POST body, WP Engine WAF, stale client state. We shipped 7 patch versions (v1.0.7 through v1.0.13) systematically eliminating server-side hypotheses. The server is not the problem.
This connector was previously working. The regression appeared without changes on our server side.AskOpus 4.6

EthosUnlimited · 2 months ago

GitHub Comment for anthropics/claude-code#5826

Copy/paste the content below the line into a comment on https://github.com/anthropics/claude-code/issues/5826*

---

We're hitting the same issue with a WordPress-based MCP server and Cowork connectors (not Claude Code CLI — that path works fine for us too).

Our setup: OAuth 2.1 with PKCE, DCR, RFC 9728 protected resource metadata, RFC 8414 AS metadata, RFC 9207 iss parameter, public client support, Streamable HTTP transport. Server is on WP Engine staging.

What we can confirm from our side:

  • Full E2E test passes manually: DCR → PKCE → code exchange → bearer token → 17 tools returned. The OAuth server is correct.
  • All endpoints reachable from cloud infrastructure (verified via curl from multiple IPs).
  • All .well-known metadata documents return correct JSON with valid registration_endpoint, token_endpoint, code_challenge_methods_supported, etc.

We applied every fix mentioned in this thread (v1.0.14) — none resolved it:

Based on the comments here, we shipped a patch that includes ALL of the following:

  • WWW-Authenticate added to Access-Control-Expose-Headers on the MCP endpoint's 401 response
  • Full CORS headers (Access-Control-Allow-Origin: *, Allow-Methods, Allow-Headers) on /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server (these were served via WordPress parse_request, bypassing REST API CORS entirely)
  • OPTIONS preflight handling for both well-known endpoints
  • Path-specific well-known endpoints (/.well-known/oauth-protected-resource/mcp, etc.) per the 2025-11-25 spec
  • MCP-Protocol-Version and Mcp-Session-Id added to Access-Control-Allow-Headers
  • resource_metadata URL forced to HTTPS (guards against reverse-proxy HTTP leak)
  • Both claude.ai and claude.com callback URLs accepted per Anthropic's docs
  • Cache-Control: private, no-store, no-cache, must-revalidate, max-age=0 on well-known responses + Set-Cookie to bust WP Engine's edge cache (verified: x-cacheable: NO:Private)
  • No null values in DCR response (confirmed)

After deploying v1.0.14 with all of the above, the Cowork connector fails identically.

What our audit log shows:

We have server-side audit logging on every OAuth event. When Cowork attempts to connect:

  • Without pre-registered credentials: Cowork's server IP hits our MCP endpoint, gets the expected 401 with WWW-Authenticate: Bearer resource_metadata="...". Then nothing. Zero metadata discovery requests. Zero DCR requests. Zero authorize requests. Immediate step=start_error in the Cowork callback URL.
  • With pre-registered credentials (client_id + client_secret in advanced settings): The flow gets further. Consent screen appears, user clicks Allow, auth code is issued, browser reaches claude.ai/api/mcp/auth_callback?code=XXX&state=YYY&iss=ZZZ. Then Cowork's backend never calls our token endpoint. Auth code stays used=0. Callback URL shows step=end_error&error_code=mcp_token_exchange_failed.

We can distinguish Cowork's server IP from our test traffic in the audit log. Cowork's IP produces only oauth.fail 401 events (the initial probe). Every DCR registration, token exchange, and successful auth in the log is from our manual testing.

Cowork flow IDs: ofid_8dc382cbf72b9ab3, ofid_27429fd45ba0de2c, ofid_3b8cd7caacc4eab9, ofid_a799fc9f773051a2, ofid_67d6f2f63977f127, ofid_756e0939199e36ab

What we ruled out: Safe Browsing, wp_safe_redirect, code TTL, missing iss parameter, public client auth method mismatch, short alias redirects losing POST body, WP Engine WAF, stale client state, CORS on well-known endpoints, CORS on WWW-Authenticate, edge caching, path-specific well-known 404s, HTTP resource_metadata URL, missing MCP-Protocol-Version header allowance. We shipped 8 patch versions (v1.0.7 through v1.0.14) systematically eliminating server-side hypotheses. The server is not the problem.

This connector was previously working. The regression appeared without changes on our server side.

Anthropic support ticket filed — conversation ID 215474060758866, same evidence included.

HamadaSalhab · 1 month ago

Still seeing Desktop-only failures with a custom OAuth 2.1 / Streamable HTTP MCP server (Memory Store, memory.store) as of May 2026. Same shape: works perfectly in Claude Code CLI (claude mcp add --transport http ...) and on Claude.ai web; fails intermittently in Claude Desktop.

Two recurring failure modes on Desktop:

  1. The connector disappears from the "+" menu in the chat composer. Opening the Connectors settings screen shows the connector listed but with "This connector has no tools available."
  2. Disconnecting and reconnecting the connector restores the tools list on the settings screen and makes the connector reappear in the "+" menu — but the first tools/call then fails with Unable to reach Memory Store.

The key signal: during the failed tools/call, zero traffic reaches our server — no auth attempt, no request, nothing in access logs. The initialize and tools/list handshake at reconnect time does arrive normally. This points squarely at Desktop's client-side dispatch layer, not transport, OAuth, or our backend (which the CLI and web client exercise without issue against the same URL).

Probably the same class as #22299 (closed COMPLETED, but our reproduction is on macOS not Windows and against an OAuth 2.1 / Streamable HTTP remote server, not a stdio filesystem server — the Feb fix may have only addressed the Windows/stdio path) and #23736 (closed COMPLETED, also Streamable HTTP, also Desktop-only).

Happy to provide Desktop logs, server-side request traces, or a test account if helpful.

barrettpyke · 1 month ago

Still occurring for Braintrust MCP

Experiencing this issue trying to connect to the custom Braintrust MCP at https://api.braintrust.dev/mcp. The first issue took the user to the auth page in browser but displayed the below error message after the flow was complete. The second issue did not ever make it to the browser and showed the same error. Error Reference: 72231b3347ba4a24 The same MCP server works with Claude Code. There was an error connecting to the MCP server. Please check your server URL and make sure your server handles auth correctly.

Issue is still occurring as of 5/28/26

New error reference: ofid_a41b6f8184f6d761

localden collaborator · 1 month ago

We traced the seven ofid_ reference IDs from this thread. They break into two distinct problems, and neither matches the original August 2025 report exactly.

ofid_756e0939…, ofid_67d6f2f6…, ofid_3b8cd7ca…, ofid_a799fc9f…, ofid_27429fd4…, ofid_8dc382cb… (one reporter, stgfirstformat.wpenginepowered.com, Apr 24-25): the connector reached your server. Discovery and the authorize step succeeded; the failure is at your /token endpoint, which returned an error for the authorization code your server had just issued. Three of the six attempts also got a 401 on the pre-authorize probe. Our logs show two successful authentications against this server earlier on Apr 23, then 22 consecutive failures starting that evening, which points to a server-side configuration change around that time. Things to check on your side: your /token handler's logs for these flows, whether the auth-code store or signing key rotated on Apr 23, and whether your WP staging environment was redeployed.

ofid_a41b6f81… (Braintrust, May 28): the dynamic client registration response from api.braintrust.dev/oauth/register returns client_secret_expires_at as a floating-point number. RFC 7591 specifies this field as seconds since epoch, an integer, and our parser rejects non-integer values. Returning an integer there will unblock the flow.

Original report (Aug 2025, Desktop opens a New Conversation page and the server is never contacted): there's no ofid_ reference for this one in the thread, so we can't trace it. If you can reproduce it now, the error card will show a reference ID; share that and we'll trace it.

If you're seeing a connection failure that isn't one of the cases above, please open a new issue with your ofid_ reference rather than adding to this thread, so each report can be traced on its own. This repo tracks Claude Code (CLI) issues; for Claude Desktop or claude.ai MCP connector failures, file at anthropics/claude-ai-mcp instead.