anthropics / anthropics/claude-ai-mcp
Custom connector: code delivered to auth_callback via compliant 302→GET, but Claude never POSTs to the token endpoint
- Ngôn ngữ chính
- Không có dữ liệu ngôn ngữ
- Star
- 471
- Fork
- 76
- Chỉ số merge pull request
- Không có pull request nào được merge trong 30 ngày
Mô tả
### MCP server URL
https://www.swatgenx.com/mcp
### Where does the issue occur?
When auth finishes and redirects back to Claude.ai
### Transport used
streamable-http
### Client registration type
Dynamic Client Registration (DCR)
### SSE only: Does your server return the expected status code for POST requests?
Not applicable (my server does not use SSE)
### When did you last reproduce this?
2026-07-24 02:43–02:44 CEST (~8 consecutive attempts). Also reproduced 2026-07-24 02:22 and 02:31 CEST, and 2026-07-23 22:12 CEST.
### Browser and OS
Safari 26.3.1 and Chrome, both on macOS 15 (Intel). Reproduced with and without iCloud Private Relay, from claude.ai web and from the desktop app, with the browser signed into claude.ai.
### Describe the issue
The full OAuth 2.1 flow succeeds up to and including delivery of the authorization code to `https://claude.ai/api/mcp/auth_callback`, but Claude's backend **never sends `POST /token`** — not once across ~12 attempts over two days. The connector remains "You started connecting to SWATGenX but didn't finish." No error toast with an error ID was shown; pressing Approve just appears to do nothing.
Flow as observed in our server logs for every attempt:
1. Claude probes `GET /mcp` → we return `401` with `WWW-Authenticate: Bearer … resource_metadata="https://www.swatgenx.com/.well-known/oauth-protected-resource/mcp"`.
2. Claude fetches RFC 9728 + RFC 8414 metadata (all `200`), then registers via DCR (`201`).
3. The user's browser opens our `/api/oauth/authorize` (PKCE S256, `redirect_uri=https://claude.ai/api/mcp/auth_callback`, `state` present), gets the consent page (`200`), and the Approve POST returns **`302 Found`** with `Location: https://claude.ai/api/mcp/auth_callback?code=…&state=…` (state echoed verbatim).
4. The browser follows the redirect to claude.ai. **After that: silence.** No `POST /token` from Anthropic egress (`160.79.104.0/21`) or any other address ever reaches us. The issued code sits unredeemed in Redis until its 10-minute TTL expires.
What we ruled out on our side:
- **Not the 307 redirect bug (#250):** our consent response is `302` (now `303`), verified as received through our CDN, not just in code. We also probed the callback directly: `GET https://claude.ai/api/mcp/auth_callback` → `307` (processed), `POST` → `405` — and browsers convert our `302`/`303` to GET, so the callback receives a GET.
- **Server flow provably works end-to-end:** we replayed Claude's exact sequence (DCR → authorize → consent approve → follow the 302 → `POST /token` with `grant_type=authorization_code` + `code_verifier` → authenticated `POST /mcp` initialize) against the public URL from an external client. Every step passes; the token endpoint returns a valid access token and the MCP initialize succeeds with it.
- **DCR response is complete:** echoes `token_endpoint_auth_method: "none"`, `grant_types`, `response_types`, `redirect_uris`, `client_id_issued_at` (RFC 7591).
- **Metadata consistency:** AS metadata advertises `code_challenge_methods_supported: ["S256"]`, `token_endpoint_auth_methods_supported: ["none"]`; protected-resource metadata `resource` exactly matches the server URL as entered.
- Token endpoint accepts `application/x-www-form-urlencoded`, responds in well under 1 s, and is reachable from datacenter IPs (verified externally).
One observation that may help you debug: **each connect attempt performs DCR twice, ~1 second apart, from two different Anthropic egress IPs** (e.g. `160.79.106.35` then `160.79.106.36`), creating two distinct client_ids. The authorize URL opened in the user's browser always carries the newer client_id. If the pending-authorization state on Claude's side is keyed per server and the second registration/attempt overwrites the first, the approved callback might no longer match any pending state — which would explain the silent drop before token exchange.
This looks closely related to #506 and #620 (callback succeeds, token endpoint never called).
### Issue details
```shell
No pop-up error or error ID was shown in Claude.ai. The connector card shows
"You started connecting to SWATGenX but didn't finish." with a Connect button.
```
### Expected behavior
After the browser delivers `code` + `state` to `https://claude.ai/api/mcp/auth_callback`, Claude's backend exchanges the code at our `token_endpoint` (`https://www.swatgenx.com/api/oauth/token`) and completes the connection — or, if it rejects the callback, surfaces an error ID so the failure is diagnosable.
### Logs from your server
Sanitized access log for one representative attempt (2026-07-24, times CEST; codes/states truncated). Anthropic backend = `160.79.106.x`, user browser = residential IP:
```shell
02:43:55 160.79.106.35 POST /api/oauth/register 201 # DCR (1st)
02:43:55 160.79.106.35 POST /api/oauth/register 201 # DCR (2nd, ~0.9 s later, new client_id)
02:43:56 GET /api/oauth/authorize?response_type=code
&client_id=Wm0tUx4U…&redirect_uri=https%3A%2F%2Fclaude.ai%2Fapi%2Fmcp%2Fauth_callback
&code_challenge=…&code_challenge_method=S256&state=…&scope=read 200 # consent page
02:43:57 POST /api/oauth/authorize?… 302 # approve → Location: https://claude.ai/api/mcp/auth_callback?code=…&state=…
--- nothing further: no POST /api/oauth/token from any address, ever ---
```
The same signature repeats for every attempt on 2026-07-23 22:12 and 2026-07-24 02:22 / 02:31 / 02:40–02:44 CEST.
Hướng dẫn đóng góp
Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này
Đánh giá
Issue này chưa được đánh giá.