Skip to content

Python: [Bug]: MCPStreamableHTTPTool re-injects header_provider headers on cross-origin redirects (latent until #4808 / mcp-sdk#2298 ship) #5820

@matte1782

Description

@matte1782

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)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions