@@ -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
21682171import asyncio
@@ -2172,82 +2175,159 @@ from mcp.client.streamable_http import streamablehttp_client
21722175
21732176
21742177async 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
22432310if __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 (" \n This 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
22502329The ` 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