quickoauth
← All guides

Debugging "unable to connect": a failure map

Where MCP OAuth connections actually break, ordered by how often they happen, with the test that isolates each one.

Updated

On this page

“Unable to connect” is one error message covering at least seven distinct failures. Work down this list; it’s ordered by frequency, and each entry has a test that gives you a definite answer.

1. Hostname not publicly resolvable

The most common failure for self-hosted servers, and invisible in your logs because no request ever arrives.

  • Tailscale Funnel (*.ts.net), split-horizon DNS, and LAN-only names all fail the connector’s DNS pre-flight even when the URL opens fine in your own browser.
  • Test: dig +short yourdomain.com @1.1.1.1; no answer means no connector.
  • Fix: a real domain with a public record. A $10 domain pointed at the same box beats any tunnel hack.

2. Certificate not from a public CA

Self-signed certs and internal CAs fail the TLS pre-flight.

  • Test: curl -sI https://yourdomain.com/mcp -o /dev/null -w '%{http_code}\n' from a machine outside your network, no -k flag.
  • Fix: Caddy or a Cloudflare-proxied record; both give you a publicly trusted cert with zero maintenance.

3. Wrong response to the unauthenticated probe

claude.ai’s first MCP call carries no token on purpose. Your server must return 401 with a WWW-Authenticate header naming the resource metadata URL, or for a no-auth server just answer the request.

  • Test: curl -si -X POST https://yourdomain.com/mcp | head -5
  • A 403, a redirect to a login page, or an HTML error page all kill discovery.

4. Metadata documents missing or inconsistent

  • /.well-known/oauth-protected-resource resource field must cover the MCP URL: the exact endpoint URL or its origin both pass; anything that doesn’t prefix-match it fails.
  • /.well-known/oauth-authorization-server must list S256 under code_challenge_methods_supported and advertise a registration_endpoint.
  • Test: curl both and read them. Field-by-field expectations are in the connector checklist.

5. Dynamic client registration rejected

claude.ai POSTs to your registration_endpoint and needs a client_id back. Providers with DCR disabled (Auth0 default) or absent return 403 or 404 here.

  • Test: the /register curl in the checklist.
  • Fix: a proxy that answers /register itself, the Auth0 proxy guide shows the pattern.

6. Token audience mismatch

The browser flow completes, consent succeeds, and then every tool call gets 401. Almost always: your server validates aud against a value the token doesn’t carry, because the /authorize redirect didn’t pass audience (or RFC 8707 resource) to the provider.

  • Test: decode the access token at the server (jwt.io or jq -R 'split(".")[1] | @base64d | fromjson') and compare aud with what your validator expects.

7. Streamable HTTP transport mismatches

Older servers speaking only SSE, or servers that reject Accept: application/json, text/event-stream, connect and then fail on the initialize round-trip.

  • Test:
    curl -s https://yourdomain.com/mcp -X POST \
      -H 'content-type: application/json' \
      -H 'accept: application/json, text/event-stream' \
      -H 'authorization: Bearer YOUR_TOKEN' \
      -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
    You want an initialize result, JSON or SSE-framed.

Still stuck after all seven? The MCP Inspector (npx @modelcontextprotocol/inspector) runs the same handshake with a full request log on screen, the fastest way to see which step diverges.

Next steps

Once the diverging step is fixed, re-run the four-curl self-test in the connector checklist before touching the connector dialog again; it’s faster than another round trip through claude.ai. If the failure traced back to your authorization server rather than your MCP server, the provider walkthroughs show a known-good configuration to diff against: Auth0, Stytch, Supabase, Hydra. To hand the diagnosis to an agent, copy the debug prompt from the prompt library.

Put it into practice

Diagnose a failing claude.ai connector

A systematic run of the seven checks that isolate where an MCP OAuth connection breaks.

Preview prompt
My remote MCP server at https://MY_DOMAIN/mcp shows "unable to connect" when I add it as a claude.ai custom connector. Run these checks in order, report the result of each, and stop at the first failure with a diagnosis and fix:

1. DNS: dig +short MY_DOMAIN @1.1.1.1, which must return a public A/AAAA/CNAME answer. (Tailscale *.ts.net names and LAN-only DNS fail claude.ai's pre-flight even if the URL works in my browser.)
2. TLS: curl -sI https://MY_DOMAIN/mcp without -k; the certificate must chain to a public CA.
3. Unauthenticated probe: curl -si -X POST https://MY_DOMAIN/mcp | head -5; expect 401 with a WWW-Authenticate header naming the resource metadata URL (or a valid MCP response if the server is intentionally no-auth). 403, 404, redirects, or HTML mean discovery dies here.
4. Protected resource metadata: curl https://MY_DOMAIN/.well-known/oauth-protected-resource; the "resource" field must exactly equal https://MY_DOMAIN/mcp or its origin https://MY_DOMAIN (a prefix of the MCP URL); anything else fails.
5. Authorization server metadata: curl https://MY_DOMAIN/.well-known/oauth-authorization-server; must include authorization_endpoint, token_endpoint, registration_endpoint, and "S256" in code_challenge_methods_supported.
6. DCR: POST to the registration_endpoint with {"redirect_uris":["https://claude.ai/api/mcp/auth_callback"],"client_name":"probe"}; expect a JSON body containing client_id.
7. Handshake: POST an MCP initialize request to /mcp with accept: application/json, text/event-stream (use a token from the flow if auth is on); expect an initialize result, JSON or SSE-framed.

For whichever step fails, explain what claude.ai's discovery expected at that step, what my server returned instead, and the minimal change to fix it. If all seven pass, run npx @modelcontextprotocol/inspector against the endpoint and compare its handshake log with the checks above.

Use this site from your agent

Add this URL as a connector in Claude, ChatGPT, or Grok. No sign-in required.

https://quickoauth.com/mcp
Explore more guides →

Search prompts & guides

24 results