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.

Next steps

All four curls green? Then:

  1. Add the connector in claude.ai under Customize → Connectors and complete the login — you should hit your consent screen and see the tools list populate.
  2. Make the same server work for ChatGPT and Grok with one server, three connectors.
  3. If anything is red or the dialog spins, work through debugging connector failures in order.

Still choosing an authorization server? Each has a walkthrough: Auth0, Stytch, Supabase, or self-hosted Hydra.

Metadata

Commit
8d09f70
Browser
Current Time
Dimensions
Source
guides
Last Updated
2026-08-22