mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Add header_provider to Streamable HTTP MCP servers (#4849)
* Python: Add header_provider to MCPStreamableHTTPTool (#4808) Add a header_provider callback parameter to MCPStreamableHTTPTool that enables injecting dynamic per-request HTTP headers from runtime kwargs (originating from FunctionInvocationContext.kwargs set in agent middleware). The implementation uses contextvars and httpx event hooks to ensure headers are task-local and safe for concurrent tool calls: - header_provider receives the runtime kwargs dict and returns headers - call_tool sets a ContextVar before delegating to MCPTool.call_tool - An httpx request event hook reads from the ContextVar and injects headers Example usage: mcp_tool = MCPStreamableHTTPTool( name="web-api", url="https://api.example.com/mcp", header_provider=lambda kwargs: { "X-Auth-Token": kwargs.get("auth_token", ""), }, ) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback for #4808: Python: [Bug]: Unable to pass AgentContext to MCPStreamableHTTPTool * Add test for header_provider via FunctionTool.invoke with FunctionInvocationContext Addresses PR review comment: exercises the full pipeline from FunctionInvocationContext.kwargs through FunctionTool.invoke to MCPStreamableHTTPTool.call_tool and header_provider, rather than testing call_tool in isolation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Address review feedback for #4808: review comment fixes * Fix streamable MCP transport defaults Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix Azure AI test client mocks Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix MCP runtime kwarg regressions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Stabilize MCP tool runtime kwargs Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use context kwargs in MCP wrappers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * updated mcp samples * fix link --------- Co-authored-by: Copilot <copilot@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
7c2dae8855
commit
9c57680f00
@@ -11,7 +11,7 @@ The Model Context Protocol (MCP) is an open standard for connecting AI agents to
|
||||
| Sample | File | Description |
|
||||
|--------|------|-------------|
|
||||
| **Agent as MCP Server** | [`agent_as_mcp_server.py`](agent_as_mcp_server.py) | Shows how to expose an Agent Framework agent as an MCP server that other AI applications can connect to |
|
||||
| **API Key Authentication** | [`mcp_api_key_auth.py`](mcp_api_key_auth.py) | Demonstrates API key authentication with MCP servers |
|
||||
| **API Key Authentication** | [`mcp_api_key_auth.py`](mcp_api_key_auth.py) | Demonstrates API key authentication with MCP servers using `header_provider`, runtime invocation kwargs, and a command-line API key argument |
|
||||
| **GitHub Integration with PAT** | [`mcp_github_pat.py`](mcp_github_pat.py) | Demonstrates connecting to GitHub's MCP server using Personal Access Token (PAT) authentication |
|
||||
|
||||
## Prerequisites
|
||||
@@ -19,5 +19,7 @@ The Model Context Protocol (MCP) is an open standard for connecting AI agents to
|
||||
- `OPENAI_API_KEY` environment variable
|
||||
- `OPENAI_RESPONSES_MODEL` environment variable
|
||||
|
||||
Run `mcp_api_key_auth.py` with the MCP API key as the first command-line argument.
|
||||
|
||||
For `mcp_github_pat.py`:
|
||||
- `GITHUB_PAT` - Your GitHub Personal Access Token (create at https://github.com/settings/tokens)
|
||||
|
||||
@@ -4,7 +4,7 @@ from typing import Annotated, Any
|
||||
|
||||
import anyio
|
||||
from agent_framework import Agent, tool
|
||||
from agent_framework.openai import OpenAIResponsesClient
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
@@ -57,7 +57,7 @@ async def run() -> None:
|
||||
# Define an agent
|
||||
# Agent's name and description provide better context for AI model
|
||||
agent = Agent(
|
||||
client=OpenAIResponsesClient(),
|
||||
client=OpenAIChatClient(),
|
||||
name="RestaurantAgent",
|
||||
description="Answer questions about the menu.",
|
||||
tools=[get_specials, get_item_price],
|
||||
|
||||
@@ -1,20 +1,31 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
|
||||
from agent_framework import Agent, MCPStreamableHTTPTool
|
||||
from agent_framework.openai import OpenAIResponsesClient
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
from dotenv import load_dotenv
|
||||
from httpx import AsyncClient
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
MCP Authentication Example
|
||||
MCP API Key Authentication Example
|
||||
|
||||
This example demonstrates how to authenticate with MCP servers using API key headers.
|
||||
This sample demonstrates the runtime ``header_provider`` pattern for
|
||||
``MCPStreamableHTTPTool``. The MCP tool derives authentication headers from
|
||||
``function_invocation_kwargs`` passed to ``Agent.run(...)`` so the API key stays
|
||||
in runtime context instead of being baked into a shared ``httpx.AsyncClient``.
|
||||
|
||||
Replace the ``url`` parameter in the ``MCPStreamableHTTPTool`` with your authenticated server URL and
|
||||
run the sample with your API key as a command-line argument:
|
||||
python mcp_api_key_auth.py <your_api_key>
|
||||
|
||||
The ``header_provider`` here is just a simple lambda, but it can be a more complex function that retrieves and
|
||||
formats headers as needed, allowing for flexible authentication schemes.
|
||||
For more complex scenarios, you could implement token refresh logic or support multiple authentication methods
|
||||
within the header provider function.
|
||||
|
||||
For more authentication examples including OAuth 2.0 flows, see:
|
||||
- https://github.com/modelcontextprotocol/python-sdk/tree/main/examples/clients/simple-auth-client
|
||||
@@ -22,44 +33,28 @@ For more authentication examples including OAuth 2.0 flows, see:
|
||||
"""
|
||||
|
||||
|
||||
async def api_key_auth_example() -> None:
|
||||
"""Example of using API key authentication with MCP server."""
|
||||
# Configuration
|
||||
mcp_server_url = os.getenv("MCP_SERVER_URL", "your-mcp-server-url")
|
||||
api_key = os.getenv("MCP_API_KEY")
|
||||
async def api_key_auth_example(api_key: str) -> None:
|
||||
"""Run an agent against an MCP server using runtime-provided API key headers."""
|
||||
|
||||
# Create authentication headers
|
||||
# Common patterns:
|
||||
# - Bearer token: "Authorization": f"Bearer {api_key}"
|
||||
# - API key header: "X-API-Key": api_key
|
||||
# - Custom header: "Authorization": f"ApiKey {api_key}"
|
||||
auth_headers = {
|
||||
"Authorization": f"Bearer {api_key}",
|
||||
}
|
||||
|
||||
# Create HTTP client with authentication headers
|
||||
http_client = AsyncClient(headers=auth_headers)
|
||||
|
||||
# Create MCP tool with the configured HTTP client
|
||||
async with (
|
||||
MCPStreamableHTTPTool(
|
||||
async with Agent(
|
||||
client=OpenAIChatClient(),
|
||||
name="Agent",
|
||||
instructions="You are a helpful assistant. Use your MCP tool when answering the user's question.",
|
||||
tools=MCPStreamableHTTPTool(
|
||||
name="MCP tool",
|
||||
description="MCP tool description",
|
||||
url=mcp_server_url,
|
||||
http_client=http_client, # Pass HTTP client with authentication headers
|
||||
) as mcp_tool,
|
||||
Agent(
|
||||
client=OpenAIResponsesClient(),
|
||||
name="Agent",
|
||||
instructions="You are a helpful assistant.",
|
||||
tools=mcp_tool,
|
||||
) as agent,
|
||||
):
|
||||
query = "What tools are available to you?"
|
||||
description="MCP tool description.",
|
||||
url="<your authenticated server url>",
|
||||
header_provider=lambda kwargs: {"Authorization": f"Bearer {kwargs['mcp_api_key']}"},
|
||||
),
|
||||
) as agent:
|
||||
query = "Use your MCP tool to tell me what tools are available to you."
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
result = await agent.run(
|
||||
query,
|
||||
function_invocation_kwargs={"mcp_api_key": api_key},
|
||||
)
|
||||
print(f"Agent: {result.text}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(api_key_auth_example())
|
||||
asyncio.run(api_key_auth_example(sys.argv[1]))
|
||||
|
||||
@@ -4,7 +4,7 @@ import asyncio
|
||||
import os
|
||||
|
||||
from agent_framework import Agent
|
||||
from agent_framework.openai import OpenAIResponsesClient
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
from dotenv import load_dotenv
|
||||
|
||||
"""
|
||||
@@ -45,7 +45,7 @@ async def github_mcp_example() -> None:
|
||||
# 4. Create agent with the GitHub MCP tool using instance method
|
||||
# The MCP tool manages the connection to the MCP server and makes its tools available
|
||||
# Set approval_mode="never_require" to allow the MCP tool to execute without approval
|
||||
client = OpenAIResponsesClient()
|
||||
client = OpenAIChatClient()
|
||||
github_mcp_tool = client.get_mcp_tool(
|
||||
name="GitHub",
|
||||
url="https://api.githubcopilot.com/mcp/",
|
||||
|
||||
Reference in New Issue
Block a user