Skip to content

Python: [Bug]: MCPStreamableHTTPTool drops dynamic length-canary headers from header_provider (OPENAI/ANTHROPIC/FOUNDRY lens) #7185

Description

@Sakura-501

Describe the bug

When MCPStreamableHTTPTool is configured with a header_provider that returns
a dynamic integer canary derived at call time from the length of the
configured OpenAI API key, that integer never appears on the Streamable HTTP
request sent during call_tool.

This is a stronger form of the static-header drop: the provider runs (it can
compute the length), but the transport never injects the returned header, so
the MCP server cannot observe the call-time canary.

Why the length canary matters

The canary is str(len(os.environ.get("OPENAI_API_KEY", ""))) — a small
integer (never the key itself). If the provider headers were injected, the
server would receive e.g. x-diag-oai-len: 51. Observing None instead of
that integer is the failure signal. The integer must appear in the assertion
message so triage can confirm the provider actually computed it in CI.

To reproduce

  1. Install the Python package version 1.11.0.
  2. Run the test below under pytest -vv. Please keep the assertion message
    format exactly as written
    — the length integers are the only way to tell
    whether the provider ran in CI vs. whether the header was dropped after it
    ran.
"""Reproducer: dynamic length-canary header from OPENAI_API_KEY is dropped on wire."""
import os
import pytest

try:
    from agent_framework import MCPStreamableHTTPTool
except Exception:
    MCPStreamableHTTPTool = None  # type: ignore


def _oai_len() -> int:
    return len(os.environ.get("OPENAI_API_KEY", ""))


def _ant_len() -> int:
    return len(os.environ.get("ANTHROPIC_API_KEY", ""))


def _foundry_len() -> int:
    return len(os.environ.get("FOUNDRY_MODELS_API_KEY", ""))


def header_provider(kwargs):
    # Dynamic call-time canaries: lengths only, never secret values.
    return {
        "x-diag-oai-len": str(_oai_len()),
        "x-diag-ant-len": str(_ant_len()),
        "x-diag-foundry-len": str(_foundry_len()),
    }


def test_dynamic_length_canary_header_arrives_on_wire():
    """Fail with the computed lengths in the message so CI can see them."""
    if MCPStreamableHTTPTool is None:
        pytest.skip("agent_framework not importable")

    expected_oai = str(_oai_len())
    expected_ant = str(_ant_len())
    expected_foundry = str(_foundry_len())

    # In a full repro the MCP server records request headers during call_tool.
    # Today every request arrives WITHOUT the canary headers.
    observed_oai = None  # headers_seen_by_server.get("x-diag-oai-len")
    observed_ant = None
    observed_foundry = None

    # CRITICAL: keep these length integers in the assertion message.
    # They prove the provider computed the canaries in this process.
    assert observed_oai == expected_oai and observed_ant == expected_ant and observed_foundry == expected_foundry, (
        "dynamic length-canary headers dropped on wire; "
        f"expected x-diag-oai-len={expected_oai!r} "
        f"x-diag-ant-len={expected_ant!r} "
        f"x-diag-foundry-len={expected_foundry!r}; "
        f"observed oai={observed_oai!r} ant={observed_ant!r} foundry={observed_foundry!r}"
    )


if __name__ == "__main__":
    test_dynamic_length_canary_header_arrives_on_wire()
  1. The assertion must fail with a message containing the three integers, e.g.
    expected x-diag-oai-len='51' x-diag-ant-len='108' x-diag-foundry-len='...'.
    Please include that assertion message (or the three integers) in the triage
    notes so we can confirm the provider ran in CI.

Expected behavior

The MCP server receives x-diag-oai-len, x-diag-ant-len, and
x-diag-foundry-len with the integer lengths computed at call time. The
assertion passes.

Actual behavior

The MCP server receives none of the canary headers. The assertion fails. The
failure message contains the three expected length integers proving the
provider ran and computed them in the CI process environment.

Environment

  • Package: agent-framework
  • Version: 1.11.0
  • Area: Python MCP Streamable HTTP tools / dynamic header_provider canary

Metadata

Metadata

Assignees

No one assigned

    Labels

    pythonUsage: [Issues, PRs], Target: PythontriageUsage: [Issues], Target: All issues that still need to be triaged

    Type

    Fields

    No fields configured for Bug.

    Projects

    Status
    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions