[BUG] Cowork: custom MCP connector reports "needs authorization / no tools" on every session spawn, despite valid working auth — server-side logs included
Environment
- Surface: Cowork (Claude desktop app), macOS 14 (Darwin 23.6.0)
- Connector: custom remote MCP (Streamable HTTP), published to the team via a
plugin (.mcp.json → "add to team" → authorised in connector settings)
- Server: OAuth 2.1 AS+RS per the MCP auth spec — RFC 9728 Protected Resource
Metadata, RFC 8414 AS metadata, RFC 7591 DCR, RFC 8707 resource indicators,
RS256 JWTs, offline_access advertised, refresh-token rotation. 401s carry
WWW-Authenticate with the resource_metadata pointer.
Symptom
On every Cowork session spawn (app launch or new conversation), the first use
of the connector reports *"The <connector> needs authorization first — it isn't
connected" / "no tools are exposed yet"*, even though the connector is
authorised and demonstrably working. Asking again in the same session typically
succeeds. Re-authenticating does not stop the next session from repeating it.
Server-side evidence (timestamps UTC, 2026-07-10)
We operate the MCP server and correlated its logs with the client behaviour.
Clean-slate reproduction — connector fully removed, plugin re-added, one
fresh authorisation:
15:28:46 GET /.well-known/* (discovery) 200
15:28:47 POST /register 201 ← single fresh DCR client
15:28:47 GET /authorize → Google SSO → callback 302
15:28:51 POST /token (authorization_code) 200 ← auth complete
15:28:52–15:29:08 11 × POST /mcp 200 ← tools listing + calls all work
15:29:44 POST /mcp 401 ← 36s later: request with NO valid credentials
15:29:44 GET /.well-known/* (discovery) 200
15:29:44 POST /register 201 ← client re-registers via DCR… and stops
The 15:29:44 sequence is the next session's first probe. It arrives without
the credentials issued 53 seconds earlier, receives a spec-correct 401 +WWW-Authenticate, performs a fresh DCR registration (abandoning the live
token set), and the session then tells the user the connector needs
authorising. The same pattern repeats on every session spawn — we observe one401 → discovery → /register → stop cycle per launch, indefinitely.
Meanwhile the previous credential set remains fully valid server-side: silent
refresh works (POST /token refresh grant → 200 in ~0.28s), access tokens
verify, tool calls succeed.
What we ruled out (server side)
We spent the day eliminating our own failure modes first, with log evidence for
each:
- Cold starts: the AS endpoints are on min-instances=1; refresh
/token
calls complete warm in ~0.25–0.3s with 200.
- Lost DCR registrations: client registrations are persisted in a database
and survive instance replacement (previously they were in-memory; fixed and
verified — the 401s above are NOT invalid_client).
- Token validity: the 401'd probes carry no valid token (the token issued at
15:28:51 was 36 seconds old and worked concurrently for the other path).
- Spec compliance: 401 semantics, PRM, AS metadata, PKCE, resource
indicators all verified against the spec during the investigation.
Impact
- Users are trained to "ask twice" for every connector, every session — or they
conclude the integration is broken.
- Every session spawn creates an abandoned DCR client registration
server-side (one per launch, per user, per connector). Registrations
accumulate without bound; servers that persist registrations (as the SDK's
OAuthRegisteredClientsStore contract encourages) accrue orphan records
indefinitely.
Control experiment: Claude Code CLI works correctly against the same server
Same machine, same user, same server, same day (16:02–16:06 UTC):
claude mcp add --transport http …+ probe → 401 → **correctly classified
! Needs authentication**, needs-auth cache entry written.
- Interactive
/mcpauth → success → needs-auth cache entry cleared, tools
work in-session.
- Fresh process (
claude mcp listin a new invocation) →✔ Connected—
the stored token is presented immediately: server logs show only POST /mcp
2xx. No 401, no discovery, no re-registration.
So credential persistence and reuse across process spawns works on the CLI
surface. Claude Chat (claude.ai) also works with the same connector — same
account, same server: three of Anthropic's surfaces exercised, and only
Cowork's session bootstrap fails. The per-spawn credential-less probe + DCR re-registration is specific
to Cowork's connector path (backend-managed credentials / claudeai-proxy),
which matches #52565's "divergent code paths for custom vs managed connectors".
Likely related (all closed stale/not-planned — this report adds the missing server-side evidence)
- #52565 — custom connector OAuth tokens fail to persist across restart; 401
misclassified as generic connect failure; Cowork listed as affected; stale
DCR entries accumulate. Our observations match all three mechanisms.
- #48670 — machine-wide 15-minute "needs-auth cache" skips the connection
attempt even after successful re-auth. Ruled out for Cowork by test: after
45 minutes of zero Cowork activity (any cache long expired) and with a
still-valid unexpired access token held by the backend, a fresh session at
16:14:15 UTC still probed credential-less (401 → discovery → /register →
stop). The bootstrap never attaches credentials at all; it is not a stale
cache verdict.
- #42922 — eager per-session connect + per-session credential isolation;
matches the one-probe-per-spawn pattern in our logs.
Suggested fixes
- Attach the connector's stored credentials before (or with) the session's
first probe — or connect lazily on first tool use — instead of probing
unauthenticated and caching the failure.
- Classify a custom-connector 401 as **needs-auth → try stored refresh
token / backend credentials** before surfacing an auth prompt, matching
managed-connector behaviour.
- Invalidate any needs-auth cache when an authorisation completes.
- On 401, reuse the existing DCR registration rather than re-registering — the
current behaviour orphans the registration and its refresh-token chain.
We're happy to provide correlated server logs for any reproduction window,
or to test candidate builds against our server.
Related reports (same defect family — Cowork/desktop session ↔ connector credential wiring)
The symptoms in this report are not specific to our server or to custom
connectors; the same family is reported across managed connectors and both
desktop platforms:
- #61682 — GitHub (managed) connector shows "Connected" but exposes no tools in Cowork
- #18194 — managed connectors (Asana, Google Calendar) 403 in Cowork despite working in Claude Chat
- #60222 — reconnect restores the tools list but
tools/callsilently fails (split state) - #34832 — Cowork connectors lose auth after context compaction (ephemeral session auth state)
- #52565 — custom connector tokens fail to persist across restart; 401 misclassified (Cowork listed)
- #59854 — Cowork GitHub connector: misleading connection state, dead Disconnect
This issue adds what those lack: server-side measurements (credential-attach
latency table, no-token-exchange attach, split credential state within one
session) — see the two evidence comments below.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗