MCP: headless (--print) sessions emit spurious "requires authentication" for OAuth connectors with valid cached tokens

Open 💬 0 comments Opened Jul 14, 2026 by matclothier

Summary

In non-interactive / headless sessions (--print, CLAUDE_CODE_ENTRYPOINT=sdk-cli), the harness injects a system-reminder telling the model that claude.ai OAuth connectors "require authentication before their tools can be used" — even when those connectors have valid cached tokens and every tool call against them succeeds. The advisory appears to be gated on session interactivity, not on actual token state, so it fires as a false positive.

It also re-fires repeatedly: because connector tools are deferred and loaded on demand via tool-search, each newly loaded batch re-triggers the check and re-injects the notice.

The injected text instructs the model to "Tell the user that these servers need to be authorized … and that the capability is unavailable until they do." So the model dutifully reports working connectors as broken and avoids/refuses tasks that would in fact succeed.

Environment

  • Claude Code 2.1.206
  • Headless SDK mode: launched with --print --input-format stream-json --output-format stream-json --permission-mode auto, CLAUDE_CODE_ENTRYPOINT=sdk-cli, no TTY
  • ~20 claude.ai-account-scoped (Scope: claude.ai config) remote OAuth MCP connectors, deferred-loaded
  • macOS

Steps to reproduce

  1. Run Claude Code headless (--print, SDK entrypoint) with a set of claude.ai-scoped OAuth connectors configured and already authorized (valid cached tokens).
  2. Start a session and let deferred connector tools load (e.g. via a tool-search that pulls in a connector batch).
  3. Observe the injected system-reminder.

Actual behavior

A system-reminder is injected listing effectively all remote connectors as requiring authentication, e.g.:

The following MCP servers require authentication before their tools can be used:
Connector_A
Connector_B
...  (≈20 servers)
This session is non-interactive, so Claude cannot run the OAuth flow here.
Tell the user that these servers need to be authorized ... the capability is
unavailable until they do.

At the same moment, claude mcp list reports every one of those servers as ✔ Connected, and any tool call against them succeeds normally (tokens refresh silently over HTTP — no browser flow needed). Reconnecting one genuinely-expired connector and re-checking confirms the rest were never unauthenticated — the notice still lists the whole fleet.

Expected behavior

The "requires authentication" advisory should be gated on actual token state (token present and refreshable) — the same path claude mcp list uses to report ✔ Connected — not on whether the session could run an interactive OAuth flow. A connector with a valid/refreshable token should not be reported as needing authentication just because the session is non-interactive.

If a genuinely-expired connector (dead/revoked refresh token) is detected, list only that connector, not the entire fleet.

Impact

Because the injected reminder explicitly instructs the model to tell the user the capability is unavailable, the agent repeatedly and incorrectly reports healthy connectors as broken, and can decline or route around tasks that would have worked. In a headless/automation context this is hard for the user to see, and erodes trust in every capability report. The only reliable workaround today is to instruct the agent to ignore the advisory entirely and probe by calling the tool — which defeats the purpose of the advisory.

Suggested fix

  • Verify real credential state (cached access/refresh token validity) before emitting the advisory, rather than inferring "needs auth" from session interactivity.
  • Only emit for connectors that actually fail a live check; never blanket the whole set.
  • Optionally, distinguish "would need interactive re-auth (refresh token dead)" from "token present, will refresh silently" so headless sessions only warn on the former.

View original on GitHub ↗