Summary
When an MCP server is created with FastMCP.from_openapi() and served over Streamable HTTP, the get_http_headers() call in OpenAPITool.run() forwards the incoming MCP transport's Authorization header to the downstream API, overwriting the API key configured on the httpx client.
This means the downstream API receives the MCP client's auth token instead of the intended API key, causing auth failures (503/401/403).
Root cause
In fastmcp/server/providers/openapi/components.py lines 173-175:
mcp_headers = get_http_headers()
if mcp_headers:
request.headers.update(mcp_headers)
get_http_headers() extracts headers from the current MCP HTTP request context and forwards them. The authorization header is not in the exclude_headers set in dependencies.py, and .update() overwrites any existing headers — including the Authorization header that was already set from the httpx client's configured headers.
Minimal reproduction
# server.py
import json, os, httpx
from fastmcp import FastMCP
# Any OpenAPI spec will do
spec = json.loads(open("openapi.json").read())
backend = FastMCP.from_openapi(
openapi_spec=spec,
client=httpx.AsyncClient(
base_url="https://api.example.com",
headers={"Authorization": f"Bearer {os.environ['MY_API_KEY']}"},
),
name="My API",
)
backend.run(transport="streamable-http")
When a client connects over Streamable HTTP with its own auth header (e.g. Claude Code, or any MCP client authenticating with the server), that auth header gets forwarded to api.example.com, replacing MY_API_KEY.
This does not happen when:
- The server runs over stdio (no HTTP request context,
get_http_headers() returns {})
- You call
backend.call_tool() directly in-process without an MCP HTTP context
Suggested fix
Add "authorization" to the exclude_headers set in get_http_headers():
exclude_headers = {
"host",
"content-length",
"content-type",
"connection",
"transfer-encoding",
"upgrade",
"te",
"keep-alive",
"expect",
"accept",
"authorization", # <-- add this
# ...
}
Or alternatively, don't forward MCP transport headers to OpenAPI downstream requests by default.
Environment
- fastmcp 3.0.0
- httpx 0.28.1
- Python 3.13
Summary
When an MCP server is created with
FastMCP.from_openapi()and served over Streamable HTTP, theget_http_headers()call inOpenAPITool.run()forwards the incoming MCP transport'sAuthorizationheader to the downstream API, overwriting the API key configured on the httpx client.This means the downstream API receives the MCP client's auth token instead of the intended API key, causing auth failures (503/401/403).
Root cause
In
fastmcp/server/providers/openapi/components.pylines 173-175:get_http_headers()extracts headers from the current MCP HTTP request context and forwards them. Theauthorizationheader is not in theexclude_headersset independencies.py, and.update()overwrites any existing headers — including theAuthorizationheader that was already set from the httpx client's configured headers.Minimal reproduction
When a client connects over Streamable HTTP with its own auth header (e.g. Claude Code, or any MCP client authenticating with the server), that auth header gets forwarded to
api.example.com, replacingMY_API_KEY.This does not happen when:
get_http_headers()returns{})backend.call_tool()directly in-process without an MCP HTTP contextSuggested fix
Add
"authorization"to theexclude_headersset inget_http_headers():Or alternatively, don't forward MCP transport headers to OpenAPI downstream requests by default.
Environment