quickoauth
← Guides

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 state parameter must round-trip unmodified.
  • The token response must be application/json with access_token, token_type: "Bearer", and expires_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