Skip to content

Custom connector fails with "Couldn't reach the MCP server" — zero inbound traffic at edge (ofid_c58995a431270861) #227

Description

@sanchit-jk

Summary

Adding a self-hosted OAuth 2.1 + DCR MCP server as a custom connector fails consistently in both Claude Desktop (v1.3883.0) and claude.ai web. The error shown is:

Couldn't reach the MCP server. You can check the server URL and verify the server is running. If this persists, share this reference with support: ofid_c58995a431270861

Our edge/ingress logs show zero inbound requests from Anthropic on every Connect click — not to /.well-known/oauth-protected-resource, /.well-known/oauth-authorization-server, /oauth/register, /oauth/authorize, nor /mcp. The broker appears to error out inside Anthropic's start step before dispatching any HTTP request to the advertised server. This is the same symptom pattern as #111, #199, and #214.

The same server connects and works end-to-end in Cursor (full DCR + PKCE + auth code + refresh token + tool calls) and in Postman (manual OAuth 2.1 flow), confirming the server and its OAuth layer are fully functional.

Reference IDs (for backend lookup)

  • ofid_c58995a431270861 — latest failed Connect attempt (claude.ai web, ~2026-04-24 20:30 UTC, same result on Desktop).
  • request_id req_011CaNmqAmgXYeGpumG8bDcZ — related Claude Desktop startup 403 (separate scope entitlement issue, included in case it's relevant).
  • Server URL, account UUID, org UUID intentionally omitted from this public issue — happy to share privately with the Anthropic team via support or a DM. The ofid_ above should be enough to look up the failed flow server-side.

Reproduction

  1. claude.ai web → Settings → Connectors → Add custom connector.
  2. Paste the MCP server URL (self-hosted OAuth 2.1 + DCR + PKCE S256 endpoint). Advanced settings blank.
  3. Click Add → connector appears in list.
  4. Click Connect.
  5. Browser never redirects to the server's /oauth/authorize endpoint. Claude shows the "Couldn't reach the MCP server" toast with the ofid_ reference above.
  6. Same steps reproduce identically in Claude Desktop 1.3883.0.

Evidence the server is healthy and spec-compliant

All probes run from the public internet with User-Agent: Claude-User (+https://docs.anthropic.com/claude-code) — the exact UA we believe Anthropic's broker uses. We intentionally tested this to rule out WAF / bot-UA blocking (the resolution in #76 and #64). The server host is redacted as <dev-host> in the samples below.

1. /mcp returns a proper 401 with RFC 9728 WWW-Authenticate

HTTP/2 401
content-type: application/json
www-authenticate: Bearer realm="https://<dev-host>/mcp",
  resource_metadata="https://<dev-host>/.well-known/oauth-protected-resource/mcp",
  error="missing_token"

{"error":"missing_token","error_description":"Authentication required"}

Also passes identically with UAs curl/*, Claude-User, ClaudeBot, and anthropic-ai — no UA-based WAF block.

2. Protected Resource Metadata (RFC 9728)

GET /.well-known/oauth-protected-resource/mcp200:

{
  "resource": "https://<dev-host>/mcp",
  "authorization_servers": ["https://<dev-host>"],
  "scopes_supported": ["mcp:read", "mcp:write"],
  "bearer_methods_supported": ["header"]
}

3. Authorization Server Metadata (RFC 8414)

GET /.well-known/oauth-authorization-server200:

{
  "issuer": "https://<dev-host>",
  "authorization_endpoint": "https://<dev-host>/oauth/authorize",
  "token_endpoint": "https://<dev-host>/oauth/token",
  "registration_endpoint": "https://<dev-host>/oauth/register",
  "scopes_supported": ["mcp:read", "mcp:write"],
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_methods_supported": ["none"],
  "code_challenge_methods_supported": ["S256"]
}

4. Dynamic Client Registration works for both claude.ai and claude.com callbacks

POST /oauth/register with redirect_uris: ["https://claude.ai/api/mcp/auth_callback"] returns 201 with a valid client_id, correct echo of redirect_uris, grant_types: ["authorization_code","refresh_token"], response_types: ["code"], token_endpoint_auth_method: "none".

Same request with redirect_uris: ["https://claude.com/api/mcp/auth_callback"] also returns 201 (we accept both per the sunpeak guide note about dual-domain callbacks).

5. Server-side OAuth flow is end-to-end verified in other clients

  • Cursor: full DCR → authorize → token → authenticated /mcp → tool calls all work.
  • Postman: manual OAuth 2.1 auth-code flow (PKCE, client_credentials_in_body) completes successfully and returns valid JWTs.

What our pod logs show during a failed Connect

With kubectl logs -f on the MCP pod running during a Connect attempt: no inbound requests at all during the attempt window. No /.well-known/*, no /oauth/*, no /mcp. Combined with the WAF tests above, this rules out our edge as the cause — the broker isn't emitting the request.

Hypothesis

Given:

  • Our server passes every OAuth 2.1 / MCP spec requirement (PRM, AS metadata, DCR, WWW-Authenticate with resource_metadata, PKCE S256, authorization_code + refresh_token).
  • Our server works end-to-end in Cursor and Postman.
  • No request ever reaches our edge during a Claude Connect attempt.
  • Multiple UAs including Claude-User pass our WAF cleanly.

...the failure is inside the Anthropic-side broker start step — matching the exact pattern of #111 (fix was a server-side allowlist on the Anthropic backend) and #199 / #214.

Environment

  • Claude Desktop: 1.3883.0 (CCD 2.1.111), macOS 15 (Darwin 25.x), Apple Silicon.
  • claude.ai web: tested on Chrome, reproduces in incognito with fresh cookies.
  • Server: FastAPI + FusionAuth-backed OAuth 2.1 behind AWS ALB + AWS WAF (AWSManagedRulesCommonRuleSet). Strictly follows RFC 7591 DCR, RFC 8414, RFC 9728, and the MCP 2025-06-18 spec.
  • Tested cleanly after full Claude Desktop + claude.ai cache wipe (filesystem, localStorage, cookies, IDB, Redis-side OAuth state all cleared). Post-wipe behaviour is identical.

Ask

@crondinini-ant — could you look up ofid_c58995a431270861 against the broker-side logs? Based on the identical signature to #111 / #214, we suspect the start step is rejecting our server (possibly on a default-scope or URL-validation step) before dispatching. If so, an allowlist exception for our dev host would unblock us while the root cause is investigated. I can share the hostname + account/org UUIDs privately on request. Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions