[FEATURE] http MCP server: allow OAuth fallback when a configured Authorization header is empty
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
For a type: http MCP server, Claude Code disables its OAuth flow whenever headers.Authorization is present in the config — regardless of the header's value. This makes it impossible to ship a single, fixed MCP config that uses a bearer token when one is available and falls back to interactive OAuth when it isn't.
This blocks a real use case: plugins install a fixed mcpServers config that end users cannot edit, and one committed entry needs to serve both audiences:
- Remote / SSH / headless (where the OAuth loopback callback can't complete): authenticate with a bearer API token from an env var.
- Local / desktop (no token set): fall back to the interactive OAuth loopback.
The idiomatic attempt uses an env var with an empty default:
{
"mcpServers": {
"example": {
"type": "http",
"url": "https://mcp.example.com/mcp",
"headers": { "Authorization": "Bearer ${MCP_API_TOKEN:-}" }
}
}
}
Intent: var set → Bearer <token> (token auth); var unset → Bearer (empty) → OAuth fallback.
Current behavior: with MCP_API_TOKEN set, token auth works. With it unset, the header expands to Bearer (empty token), the server returns 401, and Claude Code reports:
Failed to reconnect to example: Server rejected the configured Authorization
header (HTTP 401). Check that the token is valid for this MCP endpoint —
OAuth fallback is disabled when headers.Authorization is set.
So an empty bearer is treated as "auth configured" (OAuth disabled) rather than "no credential" (OAuth enabled). This matches the docs ("…or remove the header to use the OAuth flow") — but removing the header isn't possible for a fixed, plugin-installed config.
SO THE CRUX:
removing the header isn't possible for a fixed, plugin-installed config
Reproduction:
- Configure a
type: httpserver whose endpoint requires auth and advertises OAuth (RFC 9728WWW-Authenticate). - Set
"headers": { "Authorization": "Bearer ${SOME_VAR:-}" }withSOME_VARunset → connect →401, no OAuth fallback. - Set
SOME_VARto a valid token → token auth works.
Proposed Solution
When the resolved Authorization header value is empty or whitespace-only — including after ${VAR:-} expansion to an empty string — Claude Code should treat the header as absent and run the standard OAuth discovery/flow on 401, exactly as if no header were configured.
This makes the empty-default env-var pattern "just work": a present token → header auth; an absent token → OAuth. It requires no new config surface and is backwards-compatible (a non-empty header behaves exactly as today).
Alternative Solutions
- Explicit per-server opt-in for header-then-OAuth fallback. A flag that tries the configured header first and falls back to OAuth on
401:
``json``
{ "type": "http", "url": "…", "headers": { "Authorization": "Bearer ${VAR:-}" }, "authFallback": "oauth" }
More explicit, but adds config surface.
- Honor
headersHelperfor plugin http servers (#41690). A dynamic header script could omit the header entirely when no token exists, which would re-enable OAuth — but it's currently silently ignored for plugin http servers.
- Allow plugins to mark a header as optional (e.g. a
"${VAR}"-only value is dropped when unset, rather than failing to parse or sending an empty value). Narrower than #1.
- Status quo + server-side or
mcp-remoteworkarounds — rejected as unworkable:
- Server-side handling is impossible: the client sends an empty bearer and never attempts OAuth, so the server cannot rescue the no-token case.
- Conditional omission of the header key: no config syntax exists for it.
- Shipping a second
npx mcp-remoteplugin copy for the OAuth audience: two install paths, confusing for users.
Related
- #51581 —
${VAR}substitution not applied to.mcp.jsonHTTP header values - #41690 —
headersHelpersilently ignored for plugin http servers - #59467 — HTTP MCP client ignores configured
Authorizationheader when server also advertises OAuth
Priority
Critical - Blocking my work
Feature Category
MCP server integration
Use Case Example
Claude Cowork plugins REQUIRE the use of type: "http" for the MCP config:
{
"mcpServers": {
"arc-papers": {
"type": "http",
"url": "https://XXX.us-west1.run.app/mcp"
}
}
}
HOWEVER, that setup DOES NOT WORK with Google OAuth when running Claude Code remotely, since the OAuth callback tries to use the user's local machine.
This then REQUIRES 2 COPIES OF EVERY PLUGIN:
- One for Claude Cowork (and Claude Code local)
"type": "http", with Google OAuth- One for Claude Code (remote)
- Either
mcp-remotewith Google OAuth, or"type": "http"with bearer authentication (or other alternative to OAuth)
Additional Context
_No response_
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗