anthropics / anthropics/claude-ai-mcp
Connector shows "authorization not found" after a successful OAuth connect — duplicate approve submission + "Reconnect" reuses a consumed authorization URL (server-side log evidence; sibling of #132)
- Lenguaje dominante
- Sin datos de lenguaje
- Estrellas
- 471
- Forks
- 76
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Descripción
## Summary
We run a remote MCP server (`mcp.melt.ly`) behind an OAuth 2.1 one-click connect flow (Supabase OAuth Server as the identity provider, dynamic client registration). The Claude connector (claude.ai web and Claude Desktop) intermittently shows users an "authorization not found" error page at the end of the connect flow — **even though the token exchange already completed and the connection works**.
We pulled our IdP's auth-server logs for five consecutive manual connect attempts on 2026-07-06 and can now separate this into **two distinct client-side mechanisms**, with the full server-side timeline below. This looks like the server-side evidence that #134 was closed as "needs-more-info" for lacking, and it matches what the reporter of the still-open #132 describes (Supabase-auth MCP server, OAuth succeeds, tools work, Claude UI still reports failure).
## Environment
- Claude connector on claude.ai web and Claude Desktop (custom connector)
- Remote MCP server: `mcp.melt.ly` (streamable HTTP)
- OAuth 2.1 with PKCE + dynamic client registration; Supabase OAuth Server as IdP
- Server-side flow independently verified correct (clean attempts complete register → authorize → consent → approve → token exchange with no errors)
## Server-side log timeline (2026-07-06, times UTC)
| Window | Attempt | What the auth server saw |
|---|---|---|
| 15:36:58–15:39:39 | Fresh-account connect | DCR register `201` → one authorize `302` → login → consent details GET ×3 → **approve `200` (15:39:37) → token exchange `200` (15:39:38)** → *second* approve POST at 15:39:39 → `404 authorization not found` → error page shown to user |
| 15:40:35–15:40:39 | Retry | New DCR client + new authorization → approve `200` → token `200`. Clean. |
| 16:24:17 | **"Reconnect" on existing connector entry** | Consent page loaded with the **45-minute-old, already-consumed** authorization_id → details `404` → error page. **No new register or authorize call at all.** |
| 16:27:26–16:27:49 | Remove + re-add | New client + new authorization → approve `200` → token `200`. Clean. |
| 16:29:15–16:29:24 | Remove + re-add | Approve `200` (16:29:20) → **token `200` (16:29:21)** → second decision submission at 16:29:24 → `404` → error page. Connection had already succeeded 3s earlier. |
| 16:36:08–16:36:18 | Remove + re-add | Clean. |
## Mechanism 1: failure UI despite successful token exchange
In both "failing" runs above, `POST /oauth/token` returned `200` **1–3 seconds before** the request that produced the error page. The connection was fully functional in all five attempts (functional failure rate 0/5); the only failure was the UI. A second approve submission for the **same authorization_id** arrives 2–4s after the first succeeded; since authorization requests are single-use and purged at consent/code-redemption, the late submission can only 404, and the connector surfaces that as a failed connect.
We haven't yet proven where the duplicate submission originates (user double-click vs. a second tab/popup opened by the connector vs. back-navigation resubmit) — we are instrumenting our consent endpoint with per-request `user-agent` / `sec-fetch-*` / `referer` capture and can share that data here. Whatever the source, the UI reporting failure after its own successful token exchange seems wrong.
## Mechanism 2: "Reconnect" reuses the consumed authorization URL (deterministic)
Clicking **Reconnect** on an existing connector entry loaded our consent page with the original, long-consumed authorization_id — with **no new `/oauth/clients/register` or `/oauth/authorize` call preceding it**. The connector appears to re-open a cached end-of-flow consent URL instead of starting a fresh authorization flow. Against a spec-compliant single-use authorization endpoint this can only ever 404, and it does not self-heal: the user is stuck until they fully remove and re-add the connector.
## Ruled out
An earlier theory on our side — overlapping/racing authorization requests from the connector — is refuted by these logs: every attempt created exactly one DCR client and one authorization request. No sibling flows ever existed.
## Related observation: DCR client accumulation
Each connect attempt registers a brand-new OAuth client that is never reused and never cleaned up (11 "Claude" clients accumulated on our IdP across 3 days of testing). Not user-visible, but consistent with the connector not persisting or reusing client registrations.
## Suggested fixes
1. Treat a completed token exchange as the source of truth for connect success; don't let a late/duplicate consent submission flip the UI to "failed".
2. Make **Reconnect** start a fresh authorization flow (or at least fall back to one when the cached URL's authorization is no longer valid) instead of re-opening the consumed consent URL.
3. Consider reusing the registered client instead of minting a new DCR client per attempt.
## References
- #132 (open) — same symptom, Supabase-auth MCP server
- #134 (closed, needs-more-info) — this report contains the server-side timeline that was missing there
Happy to provide more server-side data — we now durably log every consent render and decision outcome with request-header signatures.
Guía de contribución
No hay ninguna guía de contribución indexada para este repositorio
Evaluación
Este issue todavía no se ha evaluado.