Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 37 additions & 28 deletions examples/mcp/streamablehttp_custom_client_example/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
# Custom HTTP Client Factory Example
# Custom HTTP Configuration Example

This example demonstrates how to use the new `httpx_client_factory` parameter in `MCPServerStreamableHttp` to configure custom HTTP client behavior for MCP StreamableHTTP connections.
This example demonstrates how to configure custom HTTP client behaviour for
`MCPServerStreamableHttp` connections when using MCP Python SDK v2.

> **Note (mcp SDK v2):** The `httpx_client_factory` parameter has been removed.
> The `MCPServerStreamableHttp` transport now uses `httpx2` internally.
> To customise HTTP behaviour, use the built-in params shown below.

## Features Demonstrated

- **Custom SSL Configuration**: Configure SSL certificates and verification settings
- **Custom Headers**: Add custom headers to all HTTP requests
- **Custom Timeouts**: Set custom timeout values for requests
- **Proxy Configuration**: Configure HTTP proxy settings
- **Custom Retry Logic**: Set up custom retry behavior (through httpx configuration)
- **Custom Authentication**: Pass an `httpx2.Auth` instance via the `auth` param

## Running the Example

Expand All @@ -22,42 +25,48 @@ This example demonstrates how to use the new `httpx_client_factory` parameter in

## Code Examples

### Basic Custom Client
### Custom Headers and Timeout (recommended)

```python
import httpx
from agents.mcp import MCPServerStreamableHttp

def create_custom_http_client() -> httpx.AsyncClient:
return httpx.AsyncClient(
verify=False, # Disable SSL verification for testing
timeout=httpx.Timeout(60.0, read=120.0),
headers={"X-Custom-Client": "my-app"},
)

async with MCPServerStreamableHttp(
name="Custom Client Server",
name="Custom Config Server",
params={
"url": "http://localhost:<port>/mcp",
"httpx_client_factory": create_custom_http_client,
"headers": {
"X-Custom-Client": "my-app",
"User-Agent": "MyApp/1.0",
},
"timeout": 60.0, # connect timeout in seconds
"sse_read_timeout": 120.0, # SSE read timeout in seconds
},
) as server:
# Use the server...
```

## Use Cases
### Basic Authentication

- **Corporate Networks**: Configure proxy settings for corporate environments
- **SSL/TLS Requirements**: Use custom SSL certificates for secure connections
- **Custom Authentication**: Add custom headers for API authentication
- **Network Optimization**: Configure timeouts and connection pooling
- **Debugging**: Disable SSL verification for development environments
```python
import httpx2
from agents.mcp import MCPServerStreamableHttp

async with MCPServerStreamableHttp(
name="Auth Server",
params={
"url": "http://localhost:<port>/mcp",
"auth": httpx2.BasicAuth(username="user", password="secret"),
},
) as server:
# Use the server...
```

## Benefits
## Use Cases

- **Flexibility**: Configure HTTP client behavior to match your network requirements
- **Security**: Use custom SSL certificates and authentication methods
- **Performance**: Optimize timeouts and connection settings for your use case
- **Compatibility**: Work with corporate proxies and network restrictions
- **Corporate Networks**: Add proxy-bypass headers or authentication
- **Custom Authentication**: Use `httpx2.Auth` subclasses for OAuth token refresh
- **Network Optimization**: Set timeouts appropriate for your environment
- **Debugging**: Inspect headers via the `headers` param

This example will auto-pick a free localhost port unless you set `STREAMABLE_HTTP_PORT`; use `STREAMABLE_HTTP_HOST` to change the bind address.
This example will auto-pick a free localhost port unless you set `STREAMABLE_HTTP_PORT`;
use `STREAMABLE_HTTP_HOST` to change the bind address.
75 changes: 24 additions & 51 deletions examples/mcp/streamablehttp_custom_client_example/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""Example demonstrating custom httpx_client_factory for MCPServerStreamableHttp.
"""Example demonstrating custom HTTP configuration for MCPServerStreamableHttp.

This example shows how to configure custom HTTP client behavior for MCP StreamableHTTP
connections, including SSL certificates, proxy settings, and custom timeouts.
With MCP Python SDK v2, the underlying transport uses httpx2 and the
``httpx_client_factory`` parameter is no longer supported. To customise HTTP
behaviour pass the ``headers``, ``timeout``, ``sse_read_timeout``, or ``auth``
(``httpx2.Auth``) keys in ``MCPServerStreamableHttpParams``.
"""

import asyncio
Expand All @@ -12,7 +14,7 @@
import time
from typing import Any, cast

import httpx
import httpx2 # noqa: F401 — available for the auth example in main()

from agents import Agent, Runner, gen_trace_id, trace
from agents.mcp import MCPServer, MCPServerStreamableHttp
Expand All @@ -36,93 +38,64 @@ def _choose_port() -> int:
STREAMABLE_HTTP_URL = f"http://{STREAMABLE_HTTP_HOST}:{STREAMABLE_HTTP_PORT}/mcp"


def create_custom_http_client(
headers: dict[str, str] | None = None,
timeout: httpx.Timeout | None = None,
auth: httpx.Auth | None = None,
) -> httpx.AsyncClient:
"""Create a custom HTTP client with specific configurations.

This function demonstrates how to configure:
- Custom SSL verification settings
- Custom timeouts
- Custom headers
- Proxy settings (commented out)
"""
if headers is None:
headers = {
"X-Custom-Client": "agents-mcp-example",
"User-Agent": "OpenAI-Agents-MCP/1.0",
}
if timeout is None:
timeout = httpx.Timeout(60.0, read=120.0)
if auth is None:
auth = None
return httpx.AsyncClient(
# Disable SSL verification for testing (not recommended for production)
verify=False,
# Set custom timeout
timeout=httpx.Timeout(60.0, read=120.0),
# Add custom headers that will be sent with every request
headers=headers,
)


async def run_with_custom_client(mcp_server: MCPServer):
"""Run the agent with a custom HTTP client configuration."""
async def run_with_server(mcp_server: MCPServer):
agent = Agent(
name="Assistant",
instructions="Use the tools to answer the questions.",
mcp_servers=[mcp_server],
model_settings=ModelSettings(tool_choice="required"),
)

# Use the `add` tool to add two numbers
message = "Add these numbers: 7 and 22."
print(f"Running: {message}")
result = await Runner.run(starting_agent=agent, input=message)
print(result.final_output)


async def main():
"""Main function demonstrating different HTTP client configurations."""
"""Demonstrate custom HTTP configuration for StreamableHTTP (mcp SDK v2)."""

print("=== Example: StreamableHTTP with custom headers and timeout ===")

print("=== Example: Custom HTTP Client with SSL disabled and custom headers ===")
# Use ``headers``, ``timeout``, ``sse_read_timeout``, and ``auth``
# (``httpx2.Auth`` instance) to customise the underlying httpx2 client.
# The ``httpx_client_factory`` parameter was removed in mcp SDK v2.
async with MCPServerStreamableHttp(
name="Streamable HTTP with Custom Client",
name="Streamable HTTP – custom config",
params={
"url": STREAMABLE_HTTP_URL,
"httpx_client_factory": create_custom_http_client,
"headers": {
"X-Custom-Client": "agents-mcp-example",
"User-Agent": "OpenAI-Agents-MCP/2.0",
},
"timeout": 60.0,
"sse_read_timeout": 120.0,
# To add authentication, pass an httpx2.Auth instance:
# "auth": httpx2.BasicAuth(username="user", password="secret"),
Comment on lines +72 to +73

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Import httpx2 before advertising BasicAuth

When a user follows the inline instruction and uncomments the provided auth entry, the example raises NameError: name 'httpx2' is not defined because this revision removed the only HTTP-client import without adding import httpx2. Add the import so the advertised authentication configuration is runnable.

AGENTS.md reference: AGENTS.md:L267-L267

Useful? React with 👍 / 👎.

},
) as server:
trace_id = gen_trace_id()
with trace(workflow_name="Custom HTTP Client Example", trace_id=trace_id):
with trace(workflow_name="Custom HTTP Config Example", trace_id=trace_id):
print(f"View trace: https://platform.openai.com/logs/trace?trace_id={trace_id}\n")
await run_with_custom_client(server)
await run_with_server(server)


if __name__ == "__main__":
# Let's make sure the user has uv installed
if not shutil.which("uv"):
raise RuntimeError(
"uv is not installed. Please install it: https://docs.astral.sh/uv/getting-started/installation/"
)

# We'll run the Streamable HTTP server in a subprocess. Usually this would be a remote server, but for this
# demo, we'll run it locally at STREAMABLE_HTTP_URL
process: subprocess.Popen[Any] | None = None
try:
this_dir = os.path.dirname(os.path.abspath(__file__))
server_file = os.path.join(this_dir, "server.py")

print(f"Starting Streamable HTTP server at {STREAMABLE_HTTP_URL} ...")

# Run `uv run server.py` to start the Streamable HTTP server
env = os.environ.copy()
env.setdefault("STREAMABLE_HTTP_HOST", STREAMABLE_HTTP_HOST)
env.setdefault("STREAMABLE_HTTP_PORT", str(STREAMABLE_HTTP_PORT))
process = subprocess.Popen(["uv", "run", server_file], env=env)
# Give it 3 seconds to start
time.sleep(3)

print("Streamable HTTP server started. Running example...\n\n")
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dependencies = [
"typing-extensions>=4.12.2, <5",
"requests>=2.0, <3",
"websockets>=15.0, <17",
"mcp>=1.19.0, <2; python_version >= '3.10'",
"mcp>=2.0.0b2,<3; python_version >= '3.10'",
]
classifiers = [
"Typing :: Typed",
Expand Down
2 changes: 1 addition & 1 deletion src/agents/extensions/experimental/codex/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# Item payloads are emitted inside item.* events from the Codex CLI JSONL stream.

if TYPE_CHECKING:
from mcp.types import ContentBlock as McpContentBlock
from mcp_types import ContentBlock as McpContentBlock
else:
McpContentBlock = Any # type: ignore[assignment]

Expand Down
Loading
Loading