Skip to content

Commit c595101

Browse files
committed
docs: update README with comprehensive per-request headers examples
- Add 7 diverse usage examples covering tracing, A/B testing, debugging, auth, multi-tenancy - Include multi-tenant example function demonstrating practical usage patterns - Add proper error handling and realistic server endpoints - Update code snippets via automated script for consistency - Fix markdownlint formatting issues
1 parent 40eae95 commit c595101

File tree

1 file changed

+129
-49
lines changed

1 file changed

+129
-49
lines changed

README.md

Lines changed: 129 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2160,9 +2160,12 @@ When using HTTP transports, you can pass custom headers on a per-request basis.
21602160
<!-- snippet-source examples/snippets/clients/per_request_headers_example.py -->
21612161
```python
21622162
"""
2163-
Example demonstrating per-request HTTP headers functionality.
2164-
Run from the repository root:
2165-
uv run examples/snippets/clients/per_request_headers_example.py
2163+
Example demonstrating per-request headers functionality for MCP client.
2164+
2165+
This example shows how to use the new extra_headers parameter in call_tool()
2166+
to send different HTTP headers with each tool call, enabling various use cases
2167+
such as per-user authentication, request tracing, A/B testing, debugging flags,
2168+
and multi-tenant applications.
21662169
"""
21672170

21682171
import asyncio
@@ -2172,82 +2175,159 @@ from mcp.client.streamable_http import streamablehttp_client
21722175

21732176

21742177
async def main():
2175-
"""Demonstrate per-request headers usage."""
2176-
# Connect to a streamable HTTP server
2177-
async with streamablehttp_client("http://localhost:8000/mcp") as (
2178+
"""Demonstrate per-request headers functionality."""
2179+
2180+
# Connection-level headers (static for the entire session)
2181+
connection_headers = {"Authorization": "Bearer org-level-token", "X-Org-ID": "org-123"}
2182+
2183+
# Connect to MCP server with connection-level headers
2184+
async with streamablehttp_client("https://mcp.example.com/mcp", headers=connection_headers) as (
21782185
read_stream,
21792186
write_stream,
21802187
_,
21812188
):
21822189
async with ClientSession(read_stream, write_stream) as session:
21832190
await session.initialize()
21842191

2185-
# Example 1: Request tracing and debugging
2192+
# Example 1: Call tool without per-request headers
2193+
# Uses only connection-level headers
2194+
print("=== Example 1: Default headers ===")
2195+
result = await session.call_tool("get_data", {})
2196+
print(f"Result: {result}")
2197+
2198+
# Example 2: Request tracing and correlation
2199+
print("\n=== Example 2: Request tracing ===")
2200+
tracing_headers = {
2201+
"X-Request-ID": "req-12345",
2202+
"X-Trace-ID": "trace-abc-456",
2203+
"X-Correlation-ID": "corr-789",
2204+
}
2205+
2206+
result = await session.call_tool("process_data", {"type": "analytics"}, extra_headers=tracing_headers)
2207+
print(f"Result with tracing: {result}")
2208+
2209+
# Example 3: A/B testing and feature flags
2210+
print("\n=== Example 3: A/B testing ===")
2211+
experiment_headers = {
2212+
"X-Experiment-ID": "new-ui-test",
2213+
"X-Variant": "variant-b",
2214+
"X-Feature-Flags": "enable-beta-features,new-algorithm",
2215+
}
2216+
21862217
result = await session.call_tool(
2187-
"analyze_data",
2188-
arguments={"dataset": "sales_q4"},
2189-
extra_headers={
2190-
"X-Request-ID": "req-12345",
2191-
"X-Debug-Mode": "true",
2192-
"X-Trace-ID": "trace-abc-456"
2193-
}
2218+
"get_recommendations", {"user_id": "user123"}, extra_headers=experiment_headers
21942219
)
2220+
print(f"Result with A/B testing: {result}")
21952221

2196-
print(f"Result with tracing: {result}")
2222+
# Example 4: Debug and profiling
2223+
print("\n=== Example 4: Debug mode ===")
2224+
debug_headers = {"X-Debug-Mode": "true", "X-Profile": "performance", "X-Log-Level": "verbose"}
2225+
2226+
result = await session.call_tool("complex_calculation", {"data": [1, 2, 3]}, extra_headers=debug_headers)
2227+
print(f"Result with debugging: {result}")
2228+
2229+
# Example 5: Authentication context (user-specific)
2230+
print("\n=== Example 5: User authentication ===")
2231+
user_headers = {"X-Auth-Token": "user-token-12345", "X-User-ID": "alice", "X-Session-ID": "sess-789"}
21972232

2198-
# Example 2: A/B testing context
21992233
result = await session.call_tool(
2200-
"get_recommendations",
2201-
arguments={"user_id": "user123"},
2202-
extra_headers={
2203-
"X-Experiment-ID": "rec-algo-v2",
2204-
"X-Variant": "variant-b"
2205-
}
2234+
"get_user_data", {"fields": ["profile", "preferences"]}, extra_headers=user_headers
22062235
)
2236+
print(f"Result for user: {result}")
22072237

2208-
print(f"Result with A/B test context: {result}")
2209-
2210-
# Example 3: Per-user authentication context
2211-
users = [
2212-
{"id": "user1", "token": "token_abc123"},
2213-
{"id": "user2", "token": "token_def456"},
2214-
{"id": "user3", "token": "token_ghi789"},
2215-
]
2238+
# Example 6: Override connection-level headers
2239+
print("\n=== Example 6: Override connection-level authorization ===")
2240+
override_headers = {
2241+
"Authorization": "Bearer user-specific-token", # Overrides connection-level
2242+
"X-Special-Permission": "admin",
2243+
}
22162244

2217-
for user in users:
2218-
print(f"Making request for {user['id']}")
2245+
result = await session.call_tool("admin_operation", {"operation": "reset"}, extra_headers=override_headers)
2246+
print(f"Result with overridden auth: {result}")
22192247

2220-
# Call tool with user-specific authentication header
2221-
result = await session.call_tool(
2222-
"get_user_data",
2223-
arguments={"user_id": user["id"]},
2224-
extra_headers={"Authorization": f"Bearer {user['token']}"}
2225-
)
2248+
# Example 7: Combine with other call_tool parameters
2249+
print("\n=== Example 7: Combined with meta and other parameters ===")
2250+
combined_headers = {"X-Request-Source": "api", "X-Priority": "high", "X-Client-Version": "1.2.3"}
22262251

2227-
print(f"Result for {user['id']}: {result}")
2252+
meta_data = {"correlation_id": "req-123", "client_version": "1.0.0"}
22282253

2229-
# Headers can also be combined with other per-request parameters
22302254
result = await session.call_tool(
2231-
"slow_operation",
2232-
arguments={"data": "example"},
2233-
extra_headers={
2234-
"X-Request-ID": "req-12345",
2235-
"X-Priority": "high"
2236-
},
2237-
read_timeout_seconds=30.0 # Extended timeout for slow operation
2255+
"complex_operation",
2256+
{"param1": "value1", "param2": "value2"},
2257+
meta=meta_data,
2258+
extra_headers=combined_headers,
22382259
)
2260+
print(f"Result with combined parameters: {result}")
2261+
2262+
2263+
# Multi-tenant example showing how different users can use the same connection
2264+
async def multi_tenant_example():
2265+
"""Example of multi-tenant usage with per-request headers."""
22392266

2240-
print(f"Slow operation result: {result}")
2267+
print("\n" + "=" * 60)
2268+
print("MULTI-TENANT EXAMPLE")
2269+
print("=" * 60)
2270+
2271+
# Organization-level connection
2272+
org_headers = {"Authorization": "Bearer org-api-key-xyz789", "X-Org-ID": "org-acme-corp"}
2273+
2274+
async with streamablehttp_client("https://mcp.example.com/mcp", headers=org_headers) as (
2275+
read_stream,
2276+
write_stream,
2277+
_,
2278+
):
2279+
async with ClientSession(read_stream, write_stream) as session:
2280+
await session.initialize()
2281+
2282+
# Simulate handling requests from different tenants/users
2283+
tenants = [
2284+
{"tenant_id": "tenant-001", "user_id": "alice", "auth_token": "alice-jwt-token-abc123"},
2285+
{"tenant_id": "tenant-002", "user_id": "bob", "auth_token": "bob-jwt-token-def456"},
2286+
{"tenant_id": "tenant-001", "user_id": "charlie", "auth_token": "charlie-jwt-token-ghi789"},
2287+
]
2288+
2289+
for i, tenant in enumerate(tenants, 1):
2290+
print(f"\n--- Request {i}: {tenant['user_id']} from {tenant['tenant_id']} ---")
2291+
2292+
# Each request gets tenant-specific headers
2293+
tenant_headers = {
2294+
"X-Tenant-ID": tenant["tenant_id"],
2295+
"X-User-ID": tenant["user_id"],
2296+
"X-Auth-Token": tenant["auth_token"],
2297+
"X-Request-ID": f"req-{i}-{tenant['user_id']}",
2298+
}
2299+
2300+
try:
2301+
result = await session.call_tool(
2302+
"get_tenant_data", {"data_type": "dashboard"}, extra_headers=tenant_headers
2303+
)
2304+
print(f"Success for {tenant['user_id']}: {len(result.content)} items")
2305+
2306+
except Exception as e:
2307+
print(f"Error for {tenant['user_id']}: {e}")
22412308

22422309

22432310
if __name__ == "__main__":
2244-
asyncio.run(main())
2311+
print("MCP Client Per-Request Headers Example")
2312+
print("=" * 50)
2313+
2314+
# Note: This example assumes a running MCP server at the specified URL
2315+
# In practice, you would replace with your actual MCP server endpoint
2316+
2317+
try:
2318+
asyncio.run(main())
2319+
asyncio.run(multi_tenant_example())
2320+
except Exception as e:
2321+
print(f"Example requires a running MCP server. Error: {e}")
2322+
print("\nThis example demonstrates the API usage patterns.")
2323+
print("Replace 'https://mcp.example.com/mcp' with your actual MCP server URL.")
22452324
```
22462325

22472326
_Full example: [examples/snippets/clients/per_request_headers_example.py](https://github.com/modelcontextprotocol/python-sdk/blob/main/examples/snippets/clients/per_request_headers_example.py)_
22482327
<!-- /snippet-source -->
22492328

22502329
The `extra_headers` parameter is available for all `ClientSession` methods that make server requests:
2330+
22512331
- `call_tool()`
22522332
- `get_prompt()`
22532333
- `read_resource()`

0 commit comments

Comments
 (0)