[Bug] MCP Server Authorization Header Not Recognized, Falls Back to OAuth
Status Fixed / completed
Reported on v2.1.74
Maintainer reply None cached
Workaround ✓ Mentioned in thread ↓
Activity 11 comments · opened Mar 13, 2026 · closed Apr 22, 2026
Bug Description
mcp does not work anymore for servers that use Authorization header instead of OAuth, it refuses to obey an Authorization header now and tries to use oauth.
This worked yesterday just fine.
{
"mcpServers": {
"myserver": {
"type": "http",
"url": "http://some.internal.mcp.server.com/mcp/message",
"headers": {
"Authorization": "Bearer REDACTED"
}
}
}
}
Environment Info
- Platform: darwin
- Terminal: iTerm.app
- Version: 2.1.74
- Feedback ID: 2b50b5dc-afb3-45de-b385-4263dd8cd4a7
Errors
[{"error":"Error: NON-FATAL: Lock acquisition failed for /Users/kevin/.local/share/claude/versions/2.1.74 (expected in multi-process scenarios)\n at kvT (/$bunfs/root/src/entrypoints/cli.js:2644:2025)\n at Cbq (/$bunfs/root/src/entrypoints/cli.js:2644:1169)\n at processTicksAndRejections (native:7:39)","timestamp":"2026-03-13T01:03:24.698Z"},{"error":"Error: ripgrep exited with code null\n at <anonymous> (/$bunfs/root/src/entrypoints/cli.js:100:18244)\n at emit (node:events:98:22)\n at #maybeClose (node:child_process:766:16)\n at #handleOnExit (node:child_process:520:72)\n at processTicksAndRejections (native:7:39)","timestamp":"2026-03-13T01:04:08.866Z"},{"error":"RipgrepTimeoutError: Ripgrep search timed out after 20 seconds. The search may have matched files but did not complete in time. Try searching a more specific path or pattern.\n at $ (/$bunfs/root/src/entrypoints/cli.js:102:242)\n at <anonymous> (/$bunfs/root/src/entrypoints/cli.js:102:456)\n at <anonymous> (/$bunfs/root/src/entrypoints/cli.js:100:18315)\n at emit (node:events:98:22)\n at #maybeClose (node:child_process:766:16)\n at #handleOnExit (node:child_process:520:72)\n at processTicksAndRejections (native:7:39)","timestamp":"2026-03-13T01:04:08.867Z"}]
11 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
It's not a duplicate, this worked fine one day ago
Same bug, confirmed from the server side. Custom Streamable HTTP MCP server (Python, uvicorn, ASGI) using the official
mcpSDK'sStreamableHTTPServerTransport. Configured with"type": "http"and bearer token viaAuthorizationheader.The sequence:
GET /.well-known/oauth-authorization-server404(no OAuth implementation)/authorize,/token,/register), enters the OAuth flowPOST /mcpPOST /mcpworks fine when called directly:The server returns
401only on failed auth. On a valid request, it returns200. The MCP spec says the client should tryPOST /mcpfirst and only enter OAuth on401. A404on the well-known endpoint should not trigger it.@0xhmn yep! this bug is maddening
Hmm, so it is now working after updating the Java MCP SDK from 0.16.0 to 0.17.1. It appears that claude code started sending a "form" field in the elicitation object during initialization:
{"capabilities": {"elicitation": {"form": {}}}}The MCP Java SDK 0.16.0 had the Elicitation record with zero fields — it only accepted {"elicitation": {}}. Jackson strict deserialization rejected the unknown form field, which got wrapped in a JSON-RPC error response.
@0xhmn not sure if this may be your issue or not!
OK found a workaround:
Claude Code's HTTP transport omits the
Accept: text/event-stream, application/jsonheader when connecting to Streamable HTTP servers. My server rejected the request with406 Not Acceptable(the MCP Streamable HTTP spec requires clients to accept both content types). Claude Code read that 406 as an auth failure and dropped into the OAuth flow:Fix: Force the
Acceptheader in.mcp.json:With this change now I am getting 200:
After this change
/mcpshows the server connected.I tried forcing the Accept header in a mcp server config in claude_desktop_config.json and it doesn't work. Upon start, the desktop app says the config is invalid.
Another workaround: mcp-stdio bridges stdio ↔ Streamable HTTP, so Claude Code never attempts OAuth discovery or touches HTTP headers directly.
This sidesteps both issues discussed here:
Accept: application/json, text/event-streamby defaultMCP_BEARER_TOKENenv var is sent asAuthorization: Bearer ...on every requestWorks with
pip install mcp-stdio,uvx mcp-stdio, orbrew install shigechika/tap/mcp-stdio._Claude Code help me write up a bug report. I found this one, so I'll add all the details here._
Title
StreamableHTTPClientTransportdoes not applyrequestInitheaders to MCP requests — custom Authorization headers are ignored in interactive sessionsDescription
When configuring an HTTP MCP server in
~/.claude.jsonwith customheaders(e.g., Bearer token auth), the headers are sent correctly byclaude mcp listbut are never sent by interactive Claude Code sessions. This makes it impossible to connect to HTTP MCP servers that use API key / Bearer token authentication (as opposed to OAuth).Root Cause
The
StreamableHTTPClientTransportclass in the MCP TypeScript SDK creates two fetch wrappers:this._fetch— the raw fetch function (no custom headers)this._fetchWithInit— fetch wrapped withrequestInitheaders viacreateFetchWithInit()The transport uses
this._fetchfor all actual MCP requests (GET SSE probe, POST messages, DELETE):But it only passes
this._fetchWithInitto the OAuth auth flow:This means custom
requestInitheaders (includingAuthorization) are never included in actual MCP requests — only in OAuth discovery/registration requests.Interaction with authProvider
Claude Code provides a built-in
authProviderfor all HTTP MCP transports (to support OAuth servers like Gmail/Calendar). When anauthProvideris present:_commonHeaders()checksthis._authProvider.tokens()— no tokens on first request, so no Authorization header is setauthProviderexists and status is 401)claude mcp listworks because it uses a separate code path that sends the custom headers directly (confirmed via server-side logging — the health check POST includes the Bearer token with UAclaude-code/2.1.104 (cli), while the interactive session sends OAuth discovery requests with UABun/1.3.12and no Authorization header).Steps to Reproduce
Authorizationheader)~/.claude.json:``
json
``{
"mcpServers": {
"my-server": {
"type": "http",
"url": "https://api.example.com/mcp",
"headers": {
"Authorization": "Bearer my_api_key"
}
}
}
}
claude mcp list— shows "Connected"claudesession — shows "needs authentication"Expected Behavior
Custom
headersfrom the MCP config should be included in all HTTP requests to the MCP server, not just health checks.Suggested Fix (SDK)
In
StreamableHTTPClientTransport, usethis._fetchWithInitinstead ofthis._fetchfor all MCP requests:Or, ensure
_commonHeaders()merges headers fromrequestInitwhen no OAuth tokens are available.Workaround
Pass the API key as a URL query parameter instead of a header:
The server must extract the key from the query string. This is not ideal for production (URLs are logged by proxies, CDNs, and load balancers) but is the only workaround until the SDK is fixed.
Environment
Evidence
Server-side logs showing the difference:
_I am building an MCP server with API Token authentication, which is how I found this bug. I used the MCP Builder Skill from Anthropic and still couldn't get my server to work with Claude Code. I fed one of the workarounds above to Claude Code, and it made the following change to the MCP server code, which seems to give me a fix without putting the API Key in the URL._
---
The fix: Return 405 on unauthenticated GET to /mcp. The SDK skips SSE, sends POST with the configured Authorization header, auth succeeds. Standard header-based auth, no keys in URLs.
⏺ The fix: return 405 on any GET to /mcp that doesn't have an Authorization header.
In src/mcp/index.ts, before the auth middleware:
Why it works: The SDK sends custom headers from the client config on POST but not on the initial GET (SSE probe). Without this fix, the unauthenticated GET hits the auth middleware → 401 → triggers OAuth. The SDK specifically treats 405 as "server doesn't support SSE GET" and gracefully skips to POST, which does include the configured Authorization header.
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.