Title: Propagate ContextVars to Transport Layer in MCP Clients · Issue #1969 · modelcontextprotocol/python-sdk · GitHub
Open Graph Title: Propagate ContextVars to Transport Layer in MCP Clients · Issue #1969 · modelcontextprotocol/python-sdk
X Title: Propagate ContextVars to Transport Layer in MCP Clients · Issue #1969 · modelcontextprotocol/python-sdk
Description: Description Summary Ensure that contextvars set in the calling thread are properly propagated to the transport layer at request time, enabling dynamic configuration of transport settings (e.g., HTTP headers, auth mechanism, etc.) on a pe...
Open Graph Description: Description Summary Ensure that contextvars set in the calling thread are properly propagated to the transport layer at request time, enabling dynamic configuration of transport settings (e.g., HTT...
X Description: Description Summary Ensure that contextvars set in the calling thread are properly propagated to the transport layer at request time, enabling dynamic configuration of transport settings (e.g., HTT...
Opengraph URL: https://github.com/modelcontextprotocol/python-sdk/issues/1969
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"Propagate ContextVars to Transport Layer in MCP Clients","articleBody":"### Description\n\n## Summary\n\nEnsure that `contextvars` set in the calling thread are properly propagated to the transport layer at request time, enabling dynamic configuration of transport settings (e.g., HTTP headers, auth mechanism, etc.) on a per-request basis.\n\n## Problem Statement\n\nCurrently, `contextvars` set in the main thread are not visible in the transport layer when making requests. This is because:\n\n1. **Context isolation**: The `streamable_http_transport` runs in a different async context or thread, so context vars set after initialization are not visible\n2. **One-time copy**: Context may be copied only at session/transport creation time, not per-request\n\nThis prevents common use cases:\n- **Authentication**: Custom `httpx.Auth` implementations that read from context vars cannot access values set in the calling code\n- **Token Exchange**: Middleware that performs OAuth token exchange and stores the result in a context var cannot make that token available to outbound MCP requests\n- **Tracing**: Trace context set in request handlers isn't visible when the HTTP request is made\n- **Multi-tenancy**: Tenant context set per-request isn't available to the transport\n\n### Why ContextVars?\n\nPython's `contextvars` module is the standard mechanism for request-scoped state:\n- Already widely used in async frameworks (FastAPI, Starlette, aiohttp)\n- Thread-safe and async-safe by design\n- Values can be set in middleware/handlers and should be available downstream\n- Zero coupling between application code and transport internals\n\n## Proposed Solution\n\nEnsure that context vars are properly propagated from the calling code to the transport layer at request time. This would allow:\n\n1. Custom `httpx.Auth` implementations to read context vars\n2. Any code in the transport layer to access current context\n\n### Example Use Case: Custom Auth\n\n```python\nfrom contextvars import ContextVar\nimport httpx\nfrom mcp.client.streamable_http import streamable_http_client\n\n# Application defines context var for auth\nauth_token_var: ContextVar[str] = ContextVar('auth_token')\n\n\nclass ContextVarAuth(httpx.Auth):\n \"\"\"Auth that reads token from context var.\"\"\"\n \n def auth_flow(self, request):\n try:\n token = auth_token_var.get()\n request.headers[\"Authorization\"] = f\"Bearer {token}\"\n except LookupError:\n pass # No token set\n yield request\n\n\n# Create client with custom auth\nclient = httpx.AsyncClient(auth=ContextVarAuth())\n\nasync with streamable_http_client(\n \"https://mcp.example.com\",\n http_client=client,\n) as (read, write, _):\n async with ClientSession(read, write) as session:\n await session.initialize()\n \n # Request 1: Set context for user A\n auth_token_var.set(\"user-a-token\")\n await session.call_tool(\"tool1\", arguments={}) # Auth reads \"user-a-token\"\n \n # Request 2: Set context for user B \n auth_token_var.set(\"user-b-token\")\n await session.call_tool(\"tool2\", arguments={}) # Auth reads \"user-b-token\"\n```\n\n### Current Behavior (Broken)\n\nThe above example doesn't work today because when `auth_flow` is called, it runs in a different context where `auth_token_var` doesn't have the value set in the calling code.\n\n### Expected Behavior\n\nContext vars set before calling `session.call_tool()` should be visible in:\n- The transport's HTTP request code\n- Custom `httpx.Auth` implementations\n- Any callbacks or handlers in the transport layer\n\n## Implementation Notes\n\nThe fix likely requires copying the context at request time (not just at session creation) using `contextvars.copy_context()` and running transport code within that context.\n\n## Relationship to Explicit Transport Configuration\n\nThere is a separate proposal (#1966) for explicit per-request transport configuration. Both approaches serve different purposes: context vars enable implicit, zero-touch propagation from middleware/handlers without changing caller code, while explicit configuration provides direct control with clear data flow. Both features are valuable and complementary. Additionally, fixing context var propagation would prevent breaking existing code that relies on context vars being visible in downstream code.\n\n## Related Issues\n\n- https://github.com/modelcontextprotocol/python-sdk/issues/1966\n\n\n### References\n\n_No response_","author":{"url":"https://github.com/somaraani","@type":"Person","name":"somaraani"},"datePublished":"2026-01-28T19:17:59.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":5},"url":"https://github.com/1969/python-sdk/issues/1969"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:85b96876-42f6-c0e8-ca76-09ef51de18e6 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | A71A:6973B:624B55:82DA1E:6A613F4E |
| html-safe-nonce | 9405d6fdd905f50cede71f13f717761e0917d4831fbb3ebe8f529d9626199b7f |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBNzFBOjY5NzNCOjYyNEI1NTo4MkRBMUU6NkE2MTNGNEUiLCJ2aXNpdG9yX2lkIjoiNjM2OTMwOTg2ODkzNzAwMjgzMCIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | 763364a6d897bf8fa7abcf8bdfa1422fecc57dcf1568e4cb2afd6580de7059ee |
| hovercard-subject-tag | issue:3866977549 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/modelcontextprotocol/python-sdk/1969/issue_layout |
| twitter:image | https://opengraph.githubassets.com/7fadfb934dd4efe92d6144fc111fa386df8e84a829c8a81c7164e0f0472f7714/modelcontextprotocol/python-sdk/issues/1969 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/7fadfb934dd4efe92d6144fc111fa386df8e84a829c8a81c7164e0f0472f7714/modelcontextprotocol/python-sdk/issues/1969 |
| og:image:alt | Description Summary Ensure that contextvars set in the calling thread are properly propagated to the transport layer at request time, enabling dynamic configuration of transport settings (e.g., HTT... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | somaraani |
| hostname | github.com |
| expected-hostname | github.com |
| None | 2b7a3d1e6770dc7153450d820fcfe9a7851c4cf2251dcc29c7ad3c5cd1b9e88b |
| turbo-cache-control | no-preview |
| go-import | github.com/modelcontextprotocol/python-sdk git https://github.com/modelcontextprotocol/python-sdk.git |
| octolytics-dimension-user_id | 182288589 |
| octolytics-dimension-user_login | modelcontextprotocol |
| octolytics-dimension-repository_id | 862584018 |
| octolytics-dimension-repository_nwo | modelcontextprotocol/python-sdk |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 862584018 |
| octolytics-dimension-repository_network_root_nwo | modelcontextprotocol/python-sdk |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 78cf9d43f120d1f03636ca22ab420bcbb1712804 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width