1414from mcp .server .experimental .request_context import Experimental
1515from mcp .server .models import InitializationOptions
1616from mcp .server .session import ServerSession
17- from mcp .shared ._context import RequestContext
17+ from mcp .shared ._context import RequestContext , tenant_id_var
1818from mcp .shared .message import SessionMessage
1919from mcp .shared .session import BaseSession
2020from mcp .types import ListToolsResult , NotificationParams , PaginatedRequestParams , ServerCapabilities
2121
2222
2323def _simulate_tenant_binding (session : ServerSession , tenant_id_value : str ) -> None :
24- """Simulate the set-once tenant binding logic from lowlevel/server.py."""
24+ """Simulate the set-once tenant binding logic from lowlevel/server.py.
25+
26+ Sets both auth_context_var (as AuthContextMiddleware does) and tenant_id_var
27+ (the transport-agnostic contextvar that the server reads).
28+ """
2529 access_token = AccessToken (
2630 token = f"token-{ tenant_id_value } " ,
2731 client_id = "client" ,
@@ -30,13 +34,15 @@ def _simulate_tenant_binding(session: ServerSession, tenant_id_value: str) -> No
3034 tenant_id = tenant_id_value ,
3135 )
3236 user = AuthenticatedUser (access_token )
33- context_token = auth_context_var .set (user )
37+ auth_token = auth_context_var .set (user )
38+ tenant_token = tenant_id_var .set (tenant_id_value )
3439 try :
35- tenant_id = get_tenant_id ()
40+ tenant_id = tenant_id_var . get ()
3641 if tenant_id is not None and session .tenant_id is None :
3742 session .tenant_id = tenant_id
3843 finally :
39- auth_context_var .reset (context_token )
44+ tenant_id_var .reset (tenant_token )
45+ auth_context_var .reset (auth_token )
4046
4147
4248@pytest .fixture
@@ -269,18 +275,20 @@ async def simulate_request(tenant_id: str, request_key: str) -> None:
269275 )
270276 user = AuthenticatedUser (access_token )
271277
272- # Set the auth context - this is what AuthContextMiddleware does
273- context_token = auth_context_var .set (user )
278+ # Set both contextvars - this is what AuthContextMiddleware does
279+ auth_token = auth_context_var .set (user )
280+ tenant_token = tenant_id_var .set (tenant_id )
274281 try :
275282 # Yield control to allow other tasks to run. This is the critical
276283 # point where context leakage could occur if isolation is broken.
277284 await anyio .sleep (0.01 )
278285
279286 # Read back the tenant_id - should still be our tenant, not the other
280- results [request_key ] = get_tenant_id ()
287+ results [request_key ] = tenant_id_var . get ()
281288 finally :
282289 # Always reset the context (mirrors middleware behavior)
283- auth_context_var .reset (context_token )
290+ tenant_id_var .reset (tenant_token )
291+ auth_context_var .reset (auth_token )
284292
285293 # Run both requests concurrently using a task group
286294 async with anyio .create_task_group () as tg :
@@ -362,12 +370,14 @@ async def handle_list_tools(ctx: ServerRequestContext, params: PaginatedRequestP
362370 tenant_id = "tenant-e2e" ,
363371 )
364372 user = AuthenticatedUser (access_token )
365- token = auth_context_var .set (user )
373+ auth_token = auth_context_var .set (user )
374+ tenant_token = tenant_id_var .set ("tenant-e2e" )
366375 try :
367376 async with Client (server ) as client :
368377 await client .list_tools ()
369378 finally :
370- auth_context_var .reset (token )
379+ tenant_id_var .reset (tenant_token )
380+ auth_context_var .reset (auth_token )
371381
372382 assert captured_ctx_tenant == "tenant-e2e"
373383 assert captured_session_tenant == "tenant-e2e"
@@ -399,13 +409,15 @@ async def handle_roots_list_changed(ctx: ServerRequestContext, params: Notificat
399409 tenant_id = "tenant-notify" ,
400410 )
401411 user = AuthenticatedUser (access_token )
402- token = auth_context_var .set (user )
412+ auth_token = auth_context_var .set (user )
413+ tenant_token = tenant_id_var .set ("tenant-notify" )
403414 try :
404415 async with Client (server ) as client :
405416 await client .session .send_roots_list_changed ()
406417 with anyio .fail_after (5 ):
407418 await notification_received .wait ()
408419 finally :
409- auth_context_var .reset (token )
420+ tenant_id_var .reset (tenant_token )
421+ auth_context_var .reset (auth_token )
410422
411423 assert notification_tenant == "tenant-notify"
0 commit comments