Skip to content

Commit 1710b14

Browse files
authored
Merge pull request #181 from floran-putter/main
Add configurable HTTP header forwarding
2 parents fc684b3 + 1794ec9 commit 1710b14

File tree

2 files changed

+73
-6
lines changed

2 files changed

+73
-6
lines changed

fastapi_mcp/server.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,15 @@ def __init__(
113113
Optional[AuthConfig],
114114
Doc("Configuration for MCP authentication"),
115115
] = None,
116+
headers: Annotated[
117+
Optional[List[str]],
118+
Doc(
119+
"""
120+
List of HTTP header names to forward from the incoming MCP request into each tool invocation.
121+
Only headers in this allowlist will be forwarded. Defaults to ['authorization'].
122+
"""
123+
),
124+
] = None,
116125
):
117126
# Validate operation and tag filtering options
118127
if include_operations is not None and exclude_operations is not None:
@@ -147,6 +156,8 @@ def __init__(
147156
timeout=10.0,
148157
)
149158

159+
self._forward_headers = {h.lower() for h in (headers or ["Authorization"])}
160+
150161
self.setup_server()
151162

152163
def setup_server(self) -> None:
@@ -404,11 +415,12 @@ async def _execute_api_tool(
404415
raise ValueError(f"Parameter name is None for parameter: {param}")
405416
headers[param_name] = arguments.pop(param_name)
406417

418+
# Forward headers that are in the allowlist
407419
if http_request_info and http_request_info.headers:
408-
if "Authorization" in http_request_info.headers:
409-
headers["Authorization"] = http_request_info.headers["Authorization"]
410-
elif "authorization" in http_request_info.headers:
411-
headers["Authorization"] = http_request_info.headers["authorization"]
420+
for name, value in http_request_info.headers.items():
421+
# case-insensitive check for allowed headers
422+
if name.lower() in self._forward_headers:
423+
headers[name] = value
412424

413425
body = arguments if arguments else None
414426

tests/test_mcp_simple_app.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ def fastapi_mcp(simple_fastapi_app: FastAPI) -> FastApiMCP:
2222
return mcp
2323

2424

25+
@pytest.fixture
26+
def fastapi_mcp_with_custom_header(simple_fastapi_app: FastAPI) -> FastApiMCP:
27+
mcp = FastApiMCP(
28+
simple_fastapi_app,
29+
name="Test MCP Server with custom header",
30+
description="Test description",
31+
headers=["X-Custom-Header"],
32+
)
33+
mcp.mount()
34+
return mcp
35+
36+
2537
@pytest.fixture
2638
def lowlevel_server_simple_app(fastapi_mcp: FastApiMCP) -> Server:
2739
return fastapi_mcp.server
@@ -311,5 +323,48 @@ async def test_headers_passthrough_to_tool_handler(fastapi_mcp: FastApiMCP):
311323

312324
if mock_request.called:
313325
headers_arg = mock_request.call_args[0][4] # headers are the 5th argument
314-
assert "Authorization" in headers_arg
315-
assert headers_arg["Authorization"] == "Bearer token456"
326+
assert "authorization" in headers_arg
327+
assert headers_arg["authorization"] == "Bearer token456"
328+
329+
330+
@pytest.mark.asyncio
331+
async def test_custom_header_passthrough_to_tool_handler(fastapi_mcp_with_custom_header: FastApiMCP):
332+
from unittest.mock import patch, MagicMock
333+
from fastapi_mcp.types import HTTPRequestInfo
334+
335+
# Test with custom header "X-Custom-Header"
336+
with patch.object(fastapi_mcp_with_custom_header, "_request") as mock_request:
337+
mock_response = MagicMock()
338+
mock_response.status_code = 200
339+
mock_response.text = '{"result": "success"}'
340+
mock_response.json.return_value = {"result": "success"}
341+
mock_request.return_value = mock_response
342+
343+
http_request_info = HTTPRequestInfo(
344+
method="POST",
345+
path="/test",
346+
headers={"X-Custom-Header": "MyValue123"},
347+
cookies={},
348+
query_params={},
349+
body=None,
350+
)
351+
352+
try:
353+
# Call the _execute_api_tool method directly
354+
# We don't care if it succeeds, just that _request gets the right headers
355+
await fastapi_mcp_with_custom_header._execute_api_tool(
356+
client=fastapi_mcp_with_custom_header._http_client,
357+
tool_name="get_item",
358+
arguments={"item_id": 1},
359+
operation_map=fastapi_mcp_with_custom_header.operation_map,
360+
http_request_info=http_request_info,
361+
)
362+
except Exception:
363+
pass
364+
365+
assert mock_request.called, "The _request method was not called"
366+
367+
if mock_request.called:
368+
headers_arg = mock_request.call_args[0][4] # headers are the 5th argument
369+
assert "X-Custom-Header" in headers_arg
370+
assert headers_arg["X-Custom-Header"] == "MyValue123"

0 commit comments

Comments
 (0)