Skip to content

Support per-run headers for MCP server calls #1969

@Padi2312

Description

@Padi2312

Summary

Allow specifying HTTP headers for the MCP server for each run invocation, not only at initialization time. In multi-user or multi-tenant environments it’s often necessary to attach user-specific or session-specific headers (e.g., auth token, user-id, tenant-id) for each agent run.

Problem

Currently, the SDK supports configuring the MCP server client (URL, transport, static headers) at initialization. But if you need different headers per run (for example, different user tokens or metadata when the same agent is shared among users), there’s no direct hook to override or supply headers with each Runner.run() or Runner.run_streamed() invocation.

Proposed behaviour

  • Extend the Runner run() and run_streamed() methods to accept a new parameter (for example mcp_headers: dict[str, str] | None) that allows specifying HTTP headers for the MCP server just for that run.

  • Example usage:

    result = await Runner.run(
        starting_agent=agent,
        input="Do something",
        run_config=run_config,
        mcp_headers={"Authorization": f"Bearer {user_token}", "X-User-Id": user_id}
    )
  • The SDK should merge default headers (set at MCP server/client init) and the per-run headers, with per-run headers taking precedence.

  • For streaming:

    result = await Runner.run_streamed(
        starting_agent=agent,
        input="Another task",
        mcp_headers={"X-Tenant-Id": tenant_id}
    )

Benefits

  • Supports multi-user / multi-tenant scenarios where many users share an agent or MCP connection but need per-request credentials or context headers.
  • Avoids needing to recreate the MCP client for each user or each run just to change headers.
  • Simplifies agent orchestration when per-run metadata (e.g., user-id, request id) must be sent to the MCP server for authorization, analytics, or routing.

Backward compatibility & notes

  • If mcp_headers is not provided, behaviour remains unchanged (use init‐level headers).
  • The new parameter is optional, so existing code continues working without change.
  • Performance impact is minimal — just header injection per request.

Suggested API addition

class Runner:
    @classmethod
    async def run(
        # existing parameters...
        mcp_headers: dict[str, str] | None = None,
    ) -> RunResult:
        ...

And similarly for run_streamed(...).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions