5151from starlette .middleware .authentication import AuthenticationMiddleware
5252from starlette .requests import Request
5353from starlette .responses import Response
54- from starlette .routing import Mount , Route
55- from starlette .types import Receive , Scope , Send
54+ from starlette .routing import Mount , Route , request_response
5655
5756from mcp .server .auth .middleware .auth_context import AuthContextMiddleware
5857from mcp .server .auth .middleware .bearer_auth import BearerAuthBackend , RequireAuthMiddleware
@@ -1172,14 +1171,19 @@ def sse_app(
11721171 message_path , security_settings = transport_security , max_request_body_size = max_request_body_size
11731172 )
11741173
1175- async def handle_sse (scope : Scope , receive : Receive , send : Send ): # pragma: no cover
1176- # Add client ID from auth context into request context if available
1177-
1178- async with sse .connect_sse (scope , receive , send ) as streams :
1174+ async def sse_endpoint (request : Request ) -> Response :
1175+ if request .method == "HEAD" :
1176+ # The SSE stream never ends, so a HEAD answered with it would never complete.
1177+ return Response (status_code = 405 , headers = {"Allow" : "GET" })
1178+ async with sse .connect_sse ( # pragma: no cover
1179+ request .scope ,
1180+ request .receive ,
1181+ request ._send , # type: ignore[reportPrivateUsage]
1182+ ) as streams :
11791183 await self ._lowlevel_server .run (
11801184 streams [0 ], streams [1 ], self ._lowlevel_server .create_initialization_options ()
11811185 )
1182- return Response ()
1186+ return Response () # pragma: no cover
11831187
11841188 # Create routes
11851189 routes : list [Route | Mount ] = []
@@ -1237,7 +1241,9 @@ async def handle_sse(scope: Scope, receive: Receive, send: Send): # pragma: no
12371241 routes .append (
12381242 Route (
12391243 sse_path ,
1240- endpoint = RequireAuthMiddleware (handle_sse , required_scopes , resource_metadata_url ),
1244+ endpoint = RequireAuthMiddleware (
1245+ request_response (sse_endpoint ), required_scopes , resource_metadata_url
1246+ ),
12411247 methods = ["GET" ],
12421248 )
12431249 )
@@ -1249,11 +1255,6 @@ async def handle_sse(scope: Scope, receive: Receive, send: Send): # pragma: no
12491255 )
12501256 else :
12511257 # Auth is disabled, no need for RequireAuthMiddleware
1252- # Since handle_sse is an ASGI app, we need to create a compatible endpoint
1253- async def sse_endpoint (request : Request ) -> Response : # pragma: no cover
1254- # Convert the Starlette request to ASGI parameters
1255- return await handle_sse (request .scope , request .receive , request ._send ) # type: ignore[reportPrivateUsage]
1256-
12571258 routes .append (
12581259 Route (
12591260 sse_path ,
0 commit comments