Summary
I think MCPStreamableHTTPTool leaks header_provider headers (including bearer tokens) on cross-origin redirects, because the _inject_headers event hook re-applies them after httpx has already stripped them. The bug is latent on the current PyPI mcp release (the _mcp_call_headers ContextVar doesn't propagate to the MCP SDK's post_writer task, per #4808), but it becomes reachable as soon as the fix in modelcontextprotocol/python-sdk#2298 (merged into mcp main on 2026-03-31) reaches a PyPI release. I'd suggest patching this in agent-framework before that fix ships, not after.
How I found this
Reading python/packages/core/agent_framework/_mcp.py end-to-end and noticing the hook is unconditional while httpx.AsyncClient was constructed with follow_redirects=True.
Where
python/packages/core/agent_framework/_mcp.py, lines 1547-1564. The auto-client uses follow_redirects=True, and _inject_headers re-applies every key from _mcp_call_headers on every outbound request with no origin check. The first-party sample samples/02-agents/mcp/mcp_api_key_auth.py documents this as the recommended way to forward bearer tokens.
This is the same class of bug as CVE-2018-1000007 (curl), CVE-2024-37891 (urllib3), CVE-2023-32681 (requests). httpx itself strips Authorization on cross-origin redirects via _redirect_headers; the hook reverses that protection.
Reproduction
I wrote a short script that mirrors the _mcp.py:1547-1564 block, sets the contextvar in the same task (sidestepping #4808), and points the client at a local stub that returns 302 Location: http://attacker.local/.... With the hook installed, the bearer is re-injected on the redirect hop. Without the hook, httpx alone does not leak. I also have a docker-free three-process harness (malicious MCP stub, attacker capture, framework client) with CSPRNG sentinels if that's useful for triage.
Suggested fix
In _inject_headers, compare (scheme, host, port) of request.url against self.url and skip injection when they differ (matches httpx _same_origin). I have a patch and a regression test against a real httpx redirect flow (not a mocked session.call_tool) ready to PR if you want it.
Versions
agent-framework-core 1.3.0, mcp 1.27.1, httpx 0.28.1, Python 3.13.
If you'd prefer to route this through secure@microsoft.com / MSRC given the credential-leak shape, happy to do that; otherwise I'll send the PR against main.
— Matteo Panzeri (matte1782)
Summary
I think
MCPStreamableHTTPToolleaksheader_providerheaders (including bearer tokens) on cross-origin redirects, because the_inject_headersevent hook re-applies them after httpx has already stripped them. The bug is latent on the current PyPImcprelease (the_mcp_call_headersContextVar doesn't propagate to the MCP SDK'spost_writertask, per #4808), but it becomes reachable as soon as the fix in modelcontextprotocol/python-sdk#2298 (merged intomcpmain on 2026-03-31) reaches a PyPI release. I'd suggest patching this in agent-framework before that fix ships, not after.How I found this
Reading
python/packages/core/agent_framework/_mcp.pyend-to-end and noticing the hook is unconditional whilehttpx.AsyncClientwas constructed withfollow_redirects=True.Where
python/packages/core/agent_framework/_mcp.py, lines 1547-1564. The auto-client usesfollow_redirects=True, and_inject_headersre-applies every key from_mcp_call_headerson every outbound request with no origin check. The first-party samplesamples/02-agents/mcp/mcp_api_key_auth.pydocuments this as the recommended way to forward bearer tokens.This is the same class of bug as CVE-2018-1000007 (curl), CVE-2024-37891 (urllib3), CVE-2023-32681 (requests). httpx itself strips
Authorizationon cross-origin redirects via_redirect_headers; the hook reverses that protection.Reproduction
I wrote a short script that mirrors the
_mcp.py:1547-1564block, sets the contextvar in the same task (sidestepping #4808), and points the client at a local stub that returns302 Location: http://attacker.local/.... With the hook installed, the bearer is re-injected on the redirect hop. Without the hook, httpx alone does not leak. I also have a docker-free three-process harness (malicious MCP stub, attacker capture, framework client) with CSPRNG sentinels if that's useful for triage.Suggested fix
In
_inject_headers, compare(scheme, host, port)ofrequest.urlagainstself.urland skip injection when they differ (matches httpx_same_origin). I have a patch and a regression test against a real httpx redirect flow (not a mockedsession.call_tool) ready to PR if you want it.Versions
agent-framework-core1.3.0,mcp1.27.1,httpx0.28.1, Python 3.13.If you'd prefer to route this through
secure@microsoft.com/ MSRC given the credential-leak shape, happy to do that; otherwise I'll send the PR againstmain.— Matteo Panzeri (matte1782)