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.
generic · updated 2026-08-22
“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-kflag. - 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-resourceresourcefield 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-servermust listS256undercode_challenge_methods_supportedand advertise aregistration_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
/registercurl in the checklist. - Fix: a proxy that answers
/registeritself — 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.ioorjq -R 'split(".")[1] | @base64d | fromjson') and compareaudwith 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:
You want ancurl -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"}}}'initializeresult, 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.
Metadata
- Commit
- 650e1b8
- Browser
- —
- Current Time
- —
- Dimensions
- —
- Source
- guides
- Last Updated
- 2026-08-22