Skip to content

Commit f61d1cc

Browse files
authored
Merge pull request #620 from jlowin/headers
Ensure behavior-affecting headers are excluded when forwarding proxies/openapi
2 parents c4d941f + 586072f commit f61d1cc

File tree

3 files changed

+40
-11
lines changed

3 files changed

+40
-11
lines changed

docs/patterns/http-requests.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ async def safe_header_info() -> dict:
7777
}
7878
```
7979

80-
By default, `get_http_headers()` excludes problematic headers like `content-length`. To include all headers, use `get_http_headers(include_all=True)`.
80+
By default, `get_http_headers()` excludes problematic headers like `host` and `content-length`. To include all headers, use `get_http_headers(include_all=True)`.
8181

8282
## Important Notes
8383

src/fastmcp/server/dependencies.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,23 @@ def get_http_headers(include_all: bool = False) -> dict[str, str]:
4848
if include_all:
4949
exclude_headers = set()
5050
else:
51-
exclude_headers = {"content-length"}
52-
53-
# ensure all lowercase!
54-
# (just in case)
55-
exclude_headers = {h.lower() for h in exclude_headers}
56-
51+
exclude_headers = {
52+
"host",
53+
"content-length",
54+
"connection",
55+
"transfer-encoding",
56+
"upgrade",
57+
"te",
58+
"keep-alive",
59+
"expect",
60+
# Proxy-related headers
61+
"proxy-authenticate",
62+
"proxy-authorization",
63+
"proxy-connection",
64+
}
65+
# (just in case)
66+
if not all(h.lower() == h for h in exclude_headers):
67+
raise ValueError("Excluded headers must be lowercase")
5768
headers = {}
5869

5970
try:

tests/client/test_openapi.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ def post_headers(request: Request):
2828
return request.headers
2929

3030
mcp = FastMCP.from_fastapi(
31-
app, httpx_client_kwargs={"headers": {"X-SERVER": "test-abc"}}
31+
app,
32+
httpx_client_kwargs={"headers": {"x-server-header": "test-abc"}},
3233
)
3334

3435
return mcp
@@ -172,13 +173,30 @@ async def test_client_headers_shttp_tool(self, shttp_server: str):
172173
async def test_client_overrides_server_headers(self, shttp_server: str):
173174
async with Client(
174175
transport=StreamableHttpTransport(
175-
shttp_server, headers={"X-SERVER": "test-client"}
176+
shttp_server, headers={"x-server-header": "test-client"}
176177
)
177178
) as client:
178179
result = await client.read_resource("resource://get_headers_headers_get")
179180
assert isinstance(result[0], TextResourceContents)
180181
headers = json.loads(result[0].text)
181-
assert headers["x-server"] == "test-client"
182+
assert headers["x-server-header"] == "test-client"
183+
184+
async def test_client_with_excluded_header_is_ignored(self, sse_server: str):
185+
async with Client(
186+
transport=SSETransport(
187+
sse_server,
188+
headers={
189+
"x-server-header": "test-client",
190+
"host": "1.2.3.4",
191+
"not-host": "1.2.3.4",
192+
},
193+
)
194+
) as client:
195+
result = await client.read_resource("resource://get_headers_headers_get")
196+
assert isinstance(result[0], TextResourceContents)
197+
headers = json.loads(result[0].text)
198+
assert headers["not-host"] == "1.2.3.4"
199+
assert headers["host"] == "fastapi"
182200

183201
async def test_client_headers_proxy(self, proxy_server: str):
184202
"""
@@ -188,4 +206,4 @@ async def test_client_headers_proxy(self, proxy_server: str):
188206
result = await client.read_resource("resource://get_headers_headers_get")
189207
assert isinstance(result[0], TextResourceContents)
190208
headers = json.loads(result[0].text)
191-
assert headers["x-server"] == "test-abc"
209+
assert headers["x-server-header"] == "test-abc"

0 commit comments

Comments
 (0)