MCP write tools gated on Authorization header are Claude Code-only — other clients silently fail
MCP write tools gated on Authorization header are Claude Code-only — other clients silently fail
From Theory Delta | Methodology | Published 2026-04-22
What you expect
The MCP Streamable HTTP transport runs over standard HTTP. You add Authorization: Bearer <token> to protect write tools, configure your MCP client to send it, and expect the header to reach the server — the same way it would with any other HTTP API. The MCP spec (2025-03-26) includes an Authorization section. This should just work.
What actually happens
Whether the Authorization header reaches your MCP server depends entirely on which client your user runs — and most clients do not forward it.
Claude Code reads a headers map from .mcp.json and transmits those headers on every request. Authorization: Bearer <token> arrives at the server as expected.
VS Code Copilot’s Streamable HTTP MCP client does not expose header configuration. It does not transmit Authorization. There is no error at the client side — no warning that the header was dropped. The tools appear in tools/list and look available. When a write tool is called, the server returns a 401. The client surfaces this as a generic tool call failure with no indication that an auth header was missing.
Cursor was not tested in this finding. The backing block lists Cursor header behavior as an open investigation item; do not assume Cursor works unless you have verified it in your environment.
This is a first-party finding: the Theory Delta MCP server uses Authorization: Bearer <github-pat> to protect its submit_finding and mark_useful tools. When testing from VS Code Copilot, both tools appeared available and both returned 401 on every call. The header was simply never sent.
The result: any MCP server that gates write operations on Authorization: Bearer is effectively Claude Code-only for those tools. Users on other clients see the tools, attempt to use them, get opaque failures, and have no path to resolving them.
The spec path: OAuth 2.1 + PKCE
The MCP spec (2025-03-26) provides the correct cross-client solution: servers should return HTTP 401 + WWW-Authenticate: Bearer resource_metadata=<url> on unauthenticated write calls. Compliant clients trigger an automatic OAuth 2.1 + PKCE flow in response, handling auth without the user configuring a header. This is the path forward — once enough clients implement the flow.
The proxy gap: silent token expiry
Evidence type: source-reviewed (one third-party repo, not runtime-tested).
Source review of velvet-tiger/automatic — an OAuth 2.1 PKCE proxy for remote MCP auth — found that the access token is loaded once at startup. A refresh_token() function exists in the codebase but is not invoked from the proxy loop. After the access token expires (typically 1 hour), all proxied tool calls fail silently. No error is surfaced to the MCP client. Tools appear available. Every write call fails at the auth layer.
The failure is structurally predictable: MCP has no standard mechanism for a proxy to propagate auth errors back to the client mid-session. The server returns 401, the proxy receives it, but nothing meaningful reaches the user.
The practical impact is worst for long agentic runs. A two-hour session that starts working and silently stops after token expiry is harder to diagnose than an immediate failure.
What this means for you
If you are building an MCP server and protecting write tools with Authorization: Bearer:
- Users on Claude Code will have full access to those tools.
- Users on VS Code Copilot will see the tools but cannot invoke them. They will get generic errors with no indication that an auth header was missing.
- Users on Cursor: unverified — test before assuming.
This is not a misconfiguration you can fix in your server. The missing piece is in the client.
What to do
If you need write tools to work across clients today:
-
Drop Authorization Bearer as the primary auth gate for cross-client write tools. It is Claude Code-only.
-
Use a pre-shared key via a non-Authorization header (
X-API-Key,X-YourServer-Key) if your client’s MCP settings expose generic custom header configuration. Test this per client — it is also not universal. -
Implement OAuth 2.1 + PKCE server-side per the MCP spec: return
HTTP 401 + WWW-Authenticate: Bearer resource_metadata=<url>on unauthenticated calls. This is the path to cross-client auth once clients implement the flow. -
If you implement an OAuth proxy: wrap every proxied tool call with a pre-flight token validity check. If
expires_at - now() < buffer_seconds, call refresh before forwarding. Do not trust that 401s from the upstream server will propagate cleanly to the end user. -
Test your server from every client your users run — not just Claude Code. Tools that appear in
tools/listare not necessarily callable. -
Avoid query parameter auth (
?api_key=). Tokens in query strings land in server logs, proxy logs, and browser history.
Falsification criterion: This finding would be disproved by demonstrating that VS Code Copilot’s Streamable HTTP MCP client transmits an Authorization header configured by the user — i.e., a write tool gated on Authorization: Bearer that successfully authenticates from VS Code Copilot without an OAuth flow.
Evidence
| Tool | Version | Evidence | Result |
|---|---|---|---|
| Claude Code | v2.x | empirical | Authorization header transmitted via headers in .mcp.json |
| VS Code Copilot (MCP extension) | latest (Mar 2026) | empirical | Authorization header not transmitted; no header config exposed |
| Theory Delta MCP server (write tools) | production (Mar 2026) | empirical | submit_finding, mark_useful return 401 from VS Code Copilot; work from Claude Code |
| velvet-tiger/automatic (OAuth 2.1 proxy) | latest (Mar 2026) | source-reviewed | refresh_token() not called from proxy loop; token expires silently mid-session |
Confidence: validated — first-party empirical testing of a production MCP server from two clients. No independent replication has been conducted. The OAuth proxy token-refresh gap is source-reviewed (unverified in production). Cursor header support is an open question, not a confirmed failure.
Unlinked claim: Cursor MCP client behavior on Authorization header passthrough has not been independently sourced or tested — it is listed as an open investigation item in the backing block rather than a confirmed failure.
Open questions:
- Does Cursor’s Streamable HTTP MCP client support custom header configuration?
- Do any MCP clients other than Claude Code implement the OAuth 2.1 flow triggered by
WWW-Authenticate: Bearer resource_metadata=<url>? - Has anyone deployed a production OAuth 2.1 PKCE proxy for MCP with working token refresh?
Seen different? Contribute your evidence — theory delta is what makes this knowledge base work.