anthropics / anthropics/claude-ai-mcp

Custom connector: authorization code issued, POST /token never sent — server-side proof the exchange is never attempted (CIMD and DCR, ofid_51d5492cc50ff37a)

Ouverte
#956 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub
Langage dominant
Aucune donnée de langage
Étoiles
471
Forks
76
Métriques de merge des PR
Aucune PR mergée en 30 j

Description

### Summary

A custom connector completes the OAuth flow through consent, my authorization server issues a code and redirects to `https://claude.ai/api/mcp/auth_callback?code=…&state=…`, and **claude.ai never sends `POST /token`**. Not a rejected exchange — no token request arrives at all.

I can show this from the authorization server's side, because the codes are rows in my database and the RPC calls are in my query logs. The same server, the same code path, the same CIMD mechanism, completed successfully for another client 25 minutes later.

This looks like #506 and #215; filing separately because I have server-side evidence those reports could only infer.

### Evidence

Authorization codes minted, from my `oauth_codes` table (UTC, 2026-08-29). `used` flips to true only inside the exchange function, after every check passes:

| created | client_id | used |
|---|---|---|
| 20:05:31 | `https://claude.ai/oauth/mcp-oauth-client-metadata` | **false** |
| 20:07:52 | `https://claude.ai/oauth/mcp-oauth-client-metadata` | **false** |
| 20:20:11 | `https://claude.ai/oauth/mcp-oauth-client-metadata` | **false** |
| 20:45:52 | `https://chatgpt.com/oauth//client.json` | **true** — access token issued at 20:45:54 |

Database request log for one of the Claude attempts, complete and in order:

```
20:20:11.076 POST /rest/v1/rpc/oauth_upsert_cimd_client 204 ← client metadata document fetched + validated
20:20:11.210 POST /rest/v1/rpc/oauth_create_code 200 ← consent approved, code minted
(nothing further) ← no exchange, ever
```

The ChatGPT attempt at 20:45 produced the same first two calls **plus** `oauth_exchange_code` → 200, through the same endpoint and the same handler. So the exchange path works; it is simply never invoked by claude.ai.

Browser landed on:

```
https://claude.ai/settings/connectors?mcp_auth_source=settings_connect&server=&step=end_error&flow_id=ofid_51d5492cc50ff37a&error_code=mcp_token_exchange_failed
```

### Flow references

`ofid_51d5492cc50ff37a` (token exchange failed, after consent)
`ofid_0bafdfd6d2f1947d`, `ofid_b5b356b7aa1ba132` (earlier attempts, failed before reaching consent with "Couldn't reach")

### What I ruled out on my side

Verified against production from three vantage points — my machine, three DNS resolvers, and Anthropic's own fetcher, which retrieves my discovery documents successfully:

- `GET /mcp` and `POST /mcp` without credentials → `401` + `WWW-Authenticate: Bearer resource_metadata="…"` (RFC 9728); `405` + `Allow: POST, OPTIONS` with a token
- `/.well-known/oauth-protected-resource`, `…/oauth-protected-resource/mcp`, `…/oauth-authorization-server` → `200`, RFC 8414 / 9728 shaped, `client_id_metadata_document_supported: true`
- DCR → `201`; `/authorize` → `303`; `/token` → clean `400 invalid_grant` on a bad code
- CORS: `Access-Control-Allow-Origin: *`, methods `GET, POST, OPTIONS`, `Access-Control-Expose-Headers: WWW-Authenticate`, preflight `200`
- Response times 0.46–0.86s, including a function left idle 15 minutes; `initialize` is 0.7s and touches no database
- TLS chain complete and valid; DNS consistent across three resolvers; both apex A records serve identically
- `/token` accepts a request that omits `client_id` and `redirect_uri` (OAuth 2.1 for a public client using PKCE), verifying either when sent

Tried both **CIMD** (default) and **DCR** — identical outcome. Tried a **second, unrelated hostname** for the same server, on a different DNS zone with a different certificate and a different ingress path — identical outcome.

### Environment

- claude.ai custom connector, added via Settings → Connectors, Streamable HTTP, Authentication "Always required"
- Server: SvelteKit on serverless, OAuth 2.1 + PKCE (S256), authorization-code grant, public client, `token_endpoint_auth_method: none`
- The same server, unchanged, completed OAuth with ChatGPT (CIMD), Grok (DCR) and Google (DCR) on the same day

### What would help

Anything from the claude.ai side about what happens between receiving `code` + `state` at `/api/mcp/auth_callback` and the token request that never leaves — whether the callback is discarded before the exchange is attempted, and on what condition. The flow references above should resolve to these exact attempts.

Happy to share the endpoint privately, or run any probe against it that would be useful.

Guide de contribution

Aucun guide de contribution indexé pour ce dépôt

Évaluation

Cette issue n'a pas encore été évaluée.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.