@@ -38,8 +38,8 @@ async def main():
3838
3939import logging
4040import warnings
41- from collections .abc import AsyncIterator , Awaitable , Callable
42- from contextlib import AbstractAsyncContextManager , AsyncExitStack , asynccontextmanager
41+ from collections .abc import AsyncIterator , Awaitable , Callable , Iterator
42+ from contextlib import AbstractAsyncContextManager , AsyncExitStack , asynccontextmanager , contextmanager
4343from importlib .metadata import version as importlib_version
4444from typing import Any , Generic
4545
@@ -52,8 +52,8 @@ async def main():
5252from typing_extensions import TypeVar
5353
5454from mcp import types
55- from mcp .server .auth .middleware .auth_context import AuthContextMiddleware
56- from mcp .server .auth .middleware .bearer_auth import BearerAuthBackend , RequireAuthMiddleware
55+ from mcp .server .auth .middleware .auth_context import AuthContextMiddleware , auth_context_var
56+ from mcp .server .auth .middleware .bearer_auth import AuthenticatedUser , BearerAuthBackend , RequireAuthMiddleware
5757from mcp .server .auth .provider import OAuthAuthorizationServerProvider , TokenVerifier
5858from mcp .server .auth .routes import build_resource_metadata_url , create_auth_routes , create_protected_resource_routes
5959from mcp .server .auth .settings import AuthSettings
@@ -74,6 +74,23 @@ async def main():
7474LifespanResultT = TypeVar ("LifespanResultT" , default = Any )
7575
7676
77+ @contextmanager
78+ def _bind_request_auth_context (request_context : Any ) -> Iterator [None ]:
79+ """Rebind auth context from the current transport request while handling a message."""
80+ authenticated_user = None
81+ scope = getattr (request_context , "scope" , None )
82+ if isinstance (scope , dict ):
83+ scope_user = scope .get ("user" )
84+ if isinstance (scope_user , AuthenticatedUser ):
85+ authenticated_user = scope_user
86+
87+ token = auth_context_var .set (authenticated_user )
88+ try :
89+ yield
90+ finally :
91+ auth_context_var .reset (token )
92+
93+
7794class NotificationOptions :
7895 def __init__ (self , prompts_changed : bool = False , resources_changed : bool = False , tools_changed : bool = False ):
7996 self .prompts_changed = prompts_changed
@@ -452,28 +469,32 @@ async def _handle_request(
452469 close_sse_stream_cb = message .message_metadata .close_sse_stream
453470 close_standalone_sse_stream_cb = message .message_metadata .close_standalone_sse_stream
454471
455- client_capabilities = session .client_params .capabilities if session .client_params else None
456- task_support = self ._experimental_handlers .task_support if self ._experimental_handlers else None
457- # Get task metadata from request params if present
458- task_metadata = None
459- if hasattr (req , "params" ) and req .params is not None :
460- task_metadata = getattr (req .params , "task" , None )
461- ctx = ServerRequestContext (
462- request_id = message .request_id ,
463- meta = message .request_meta ,
464- session = session ,
465- lifespan_context = lifespan_context ,
466- experimental = Experimental (
467- task_metadata = task_metadata ,
468- _client_capabilities = client_capabilities ,
469- _session = session ,
470- _task_support = task_support ,
471- ),
472- request = request_data ,
473- close_sse_stream = close_sse_stream_cb ,
474- close_standalone_sse_stream = close_standalone_sse_stream_cb ,
475- )
476- response = await handler (ctx , req .params )
472+ # Stateful HTTP sessions process later requests on tasks that were
473+ # created during session setup, so ContextVar snapshots can lag
474+ # behind the current request unless we rebind them here.
475+ with _bind_request_auth_context (request_data ):
476+ client_capabilities = session .client_params .capabilities if session .client_params else None
477+ task_support = self ._experimental_handlers .task_support if self ._experimental_handlers else None
478+ # Get task metadata from request params if present
479+ task_metadata = None
480+ if hasattr (req , "params" ) and req .params is not None :
481+ task_metadata = getattr (req .params , "task" , None )
482+ ctx = ServerRequestContext (
483+ request_id = message .request_id ,
484+ meta = message .request_meta ,
485+ session = session ,
486+ lifespan_context = lifespan_context ,
487+ experimental = Experimental (
488+ task_metadata = task_metadata ,
489+ _client_capabilities = client_capabilities ,
490+ _session = session ,
491+ _task_support = task_support ,
492+ ),
493+ request = request_data ,
494+ close_sse_stream = close_sse_stream_cb ,
495+ close_standalone_sse_stream = close_standalone_sse_stream_cb ,
496+ )
497+ response = await handler (ctx , req .params )
477498 except MCPError as err :
478499 response = err .error
479500 except anyio .get_cancelled_exc_class ():
0 commit comments