/api/mcp. It implements:
- RFC 7591 — Dynamic Client Registration
- RFC 7636 — PKCE (required, S256 only)
- RFC 8414 — Authorization Server Metadata
- RFC 9728 — Protected Resource Metadata
Discovery
/.well-known/oauth-protected-resource currently advertises the public resource scope mcp. Pro authorization-code grants return the internal scope value mcp_pro; legacy API-key grants and client_credentials return mcp.
Endpoints
POST /api/oauth/register
Dynamic Client Registration. Returns a client_id (public clients, no secret).
Request:
https://claude.ai/api/mcp/auth_callbackhttps://claude.com/api/mcp/auth_callbackhttp://localhost:<port>/http://127.0.0.1:<port>— any port
redirect_uris per registration; more returns 400 invalid_request.
Rate limit: 5 registrations / 60 s / IP.
Client TTL: 90 days sliding (every successful token exchange refreshes).
GET /api/oauth/authorize
Starts the OAuth flow. Renders a consent page that redirects to Clerk for sign-in, then issues an authorization code bound to the caller’s entitlement. The Pro sign-in leg of the consent flow is served by the sibling GET /oauth/authorize-pro (HTML; not called directly by clients). It admits Pro subscribers and confirmed free accounts; a provider-confirmed lapse is reclassified onto the free-account path, so authorization continues with a restricted, allowance-metered token. Retryable verification failures return 503, while genuinely insufficient states such as an expired or disabled paid row without a confirmed lapse render the Pro-required page.
Required query params:
response_type=codeclient_id— from DCRredirect_uri— must match the one registeredcode_challenge— PKCE S256code_challenge_method=S256state— opaquescope(optional)
GETDEL on exchange).
POST /api/oauth/token
Exchanges an authorization code for an access token, or refreshes an existing token.
Grant type: authorization_code:
refresh_token:
client_credentials (operator-issued enterprise keys only):
client_secret against the deployment’s operator key allowlist and returns a bearer token with scope: "mcp" and the standard 3600 s TTL. Not available for dashboard wm_… keys — those are sent directly as X-WorldMonitor-Key instead.
Rate limit: 10 token requests / minute. The limiter is keyed by client_secret hash for client_credentials, by client_id when present (authorization_code and refresh_token), and falls back to caller IP only when neither identifier is available. All three grant types fail open when the limiter is unconfigured or throws; the response then carries X-RateLimit-Mode: degraded (listed in Access-Control-Expose-Headers) so operators and cross-origin clients can tell that traffic apart from healthy limiter grants. A Redis storage outage still fails token persistence closed.
Token TTLs:
- Access token: 1 hour
- Refresh token: 7 days
Cache-Control: no-store, Pragma: no-cache.
Using tokens
Pass the access token on every MCP request:free-account path; an expired or disabled paid entitlement without that confirmed lapse, or another genuinely insufficient non-free state, is denied on the next request.
Error responses
Per RFC 6749 §5.2:invalid_request, invalid_client, invalid_grant, unsupported_grant_type, invalid_scope.