The endpoints claude.ai actually probes before connecting
A verified checklist of every discovery request claude.ai makes when you add a remote MCP server with OAuth, and what each response must contain.
claude · updated 2026-08-22
When you add a custom connector, claude.ai runs a discovery sequence before it ever shows your consent screen. If any step fails, you get a generic “unable to connect” — with no server-side log line, because some checks never reach your server. This checklist is the sequence, verified against a live connector.
0. DNS and TLS pre-flight
Before any HTTP request, the connector backend resolves your hostname and validates the certificate chain.
- The hostname must resolve on public DNS. Tailscale Funnel (
*.ts.net) hostnames fail this pre-flight even though they are reachable from a browser — the connector is rejected before a single request is made. - Use a real domain with a public A/AAAA or CNAME record and a certificate from a public CA (Let’s Encrypt via Caddy or Cloudflare-managed certs both work).
1. POST /mcp without a token → 401 with WWW-Authenticate
claude.ai first calls your MCP endpoint unauthenticated. A protected server
must answer 401 with a WWW-Authenticate header pointing at your protected
resource metadata:
WWW-Authenticate: Bearer resource_metadata="https://yourdomain.com/.well-known/oauth-protected-resource"
If you return 403, 404, or a 200 with an error body, discovery stops
here. (A server that needs no auth should just answer the MCP request —
claude.ai supports no-auth connectors too.)
2. GET /.well-known/oauth-protected-resource
Protected resource metadata (RFC 9728). Minimum viable body:
{
"resource": "https://yourdomain.com/mcp",
"authorization_servers": ["https://yourdomain.com"]
}
resource must cover the URL the client is calling: the exact endpoint URL
works, and so does its origin — a live passing deployment serves
"resource": "https://yourdomain.com" for an endpoint at /mcp. What fails
is a value that doesn’t prefix-match the MCP URL at all.
3. GET /.well-known/oauth-authorization-server
Authorization server metadata (RFC 8414), fetched from each entry in
authorization_servers. Required fields in practice:
{
"issuer": "https://yourdomain.com",
"authorization_endpoint": "https://yourdomain.com/authorize",
"token_endpoint": "https://yourdomain.com/token",
"registration_endpoint": "https://yourdomain.com/register",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"code_challenge_methods_supported": ["S256"]
}
code_challenge_methods_supported must include S256 — the client uses PKCE
unconditionally.
4. POST /register — dynamic client registration
claude.ai registers itself as an OAuth client (RFC 7591). It sends its
redirect_uris and expects a client_id back. If you use a provider that
does not support open DCR (Auth0 requires enabling it, some providers lack it
entirely), put a thin proxy in front that handles /register itself — see
the Auth0 proxy guide.
Redirect URIs to allow: https://claude.ai/api/mcp/auth_callback and
https://claude.com/api/mcp/auth_callback.
5. Browser flow: /authorize → callback → POST /token
Standard authorization-code + PKCE exchange. Two failure points worth checking:
- The
stateparameter must round-trip unmodified. - The token response must be
application/jsonwithaccess_token,token_type: "Bearer", andexpires_in.
6. POST /mcp with the token
The original MCP initialize request, now with Authorization: Bearer ….
Your server validates the token (issuer, audience, expiry) and answers the
MCP handshake. From here on it’s plain MCP over Streamable HTTP.
Quick self-test
# 1. unauthenticated call returns 401 + WWW-Authenticate
curl -si https://yourdomain.com/mcp -X POST | head -5
# 2-3. metadata endpoints return JSON
curl -s https://yourdomain.com/.well-known/oauth-protected-resource | jq .
curl -s https://yourdomain.com/.well-known/oauth-authorization-server | jq .
# 4. DCR accepts a registration
curl -s https://yourdomain.com/register -X POST \
-H 'content-type: application/json' \
-d '{"redirect_uris":["https://claude.ai/api/mcp/auth_callback"],"client_name":"test"}' | jq .
All four green and the connector dialog will get you to a consent screen.
Metadata
- Commit
- 650e1b8
- Browser
- —
- Current Time
- —
- Dimensions
- —
- Source
- guides
- Last Updated
- 2026-08-22