Custom OAuth MCP connector: token exchange never fires in Cowork (sandbox can't reach localhost loopback redirect); identical server works in claude.ai web Chat
Summary
A custom remote MCP server with a fully spec-compliant OAuth 2.1 flow connects successfully in claude.ai web Chat and Claude Code, but fails in Cowork. In Cowork, OAuth discovery, dynamic client registration, and the /authorize step all succeed and a valid authorization code is minted — but POST /oauth/token never reaches the server, and Claude reports "the MCP server disconnected before I could complete the handshake; the complete_authentication tool isn't available anymore."
Likely the same class of bug as #52565 and #23736 (custom OAuth + DCR MCP connectors work in Code/web but fail in Desktop/Cowork). Adding net-new server-side instrumentation below: the token exchange demonstrably never fires, and the root cause appears to be the localhost loopback redirect being unreachable from Cowork's cloud sandbox.
Environment
- Claude Cowork (Claude Desktop), current build as of 2026-05-27, macOS
- Remote MCP server, HTTP+SSE transport, OAuth-gated MCP endpoint
- OAuth 2.1: authorization-code + PKCE (S256), RFC 7591 dynamic client registration, RFC 9728 protected-resource metadata,
WWW-Authenticate: Bearer resource_metadata=...on the 401. All verified returning 200.
What works vs. what fails
| Surface | Result |
|---|---|
| claude.ai web Chat (custom connector) | ✅ Connects — server-brokered callback (https://claude.ai/api/mcp/auth_callback), token exchange completes, tools load |
| Cowork (same server, same OAuth config) | ❌ Fails — uses http://localhost:<ephemeral-port>/callback, asks the user to paste the redirected URL back, then "disconnected before handshake" |
Server-side evidence (Cowork attempt)
Access logs across every Cowork attempt show:
GET /.well-known/oauth-protected-resource/mcp/sse -> 200
GET /.well-known/oauth-protected-resource -> 200
GET /.well-known/oauth-authorization-server -> 200
POST /oauth/register -> 201
GET /oauth/authorize?...&resource=... -> 303 (valid code minted to localhost callback)
POST /oauth/token -> (never arrives)
The token endpoint is never hit. The MCP/discovery requests arrive from Anthropic cloud IPs, while the /authorize request comes from the end user's home IP — so the localhost:<port>/callback redirect lands in the user's browser and bridges nothing back to the sandboxed MCP client. Cowork then falls back to the documented manual "paste the callback URL" flow (#29507), but the connector session is torn down before the pasted code can be exchanged.
Repro steps
- Stand up a remote MCP server with OAuth 2.1 (PKCE + dynamic client registration + RFC 9728 protected-resource metadata).
- Add it as a custom connector in claude.ai web Chat -> connects fine.
- Add the same server in Cowork -> discovery/register/authorize succeed, but the handshake never completes; no
POST /oauth/tokenreaches the server.
Expected
Cowork should complete the OAuth flow for remote custom connectors using a server-brokered callback (as web Chat does), not a localhost loopback the sandbox cannot receive.
Related
- #52565 (open) — Cowork custom connector OAuth, tools never load
- #23736 (closed as duplicate) — same server works in Code, fails in Desktop/Cowork
- #29507 — documents the localhost-redirect -> manual-paste fallback this degrades into
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗