Skip to content

Commit 635ffcb

Browse files
author
文徐
committed
return response for handle_sse
1 parent a76076b commit 635ffcb

File tree

1 file changed

+28
-32
lines changed

1 file changed

+28
-32
lines changed

src/mcp/server/fastmcp/server.py

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,6 @@
5252
logger = get_logger(__name__)
5353

5454

55-
class SilentResponse(Response):
56-
"""A response that does not send any HTTP response back to the client."""
57-
58-
def __init__(self) -> None:
59-
super().__init__()
60-
61-
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
62-
return
63-
64-
6555
class Settings(BaseSettings, Generic[LifespanResultT]):
6656
"""FastMCP server settings.
6757
@@ -745,20 +735,25 @@ def sse_app(self, mount_path: str | None = None) -> Starlette:
745735
security_settings=self.settings.transport_security,
746736
)
747737

748-
async def handle_sse(scope: Scope, receive: Receive, send: Send):
749-
# Add client ID from auth context into request context if available
750-
751-
async with sse.connect_sse(
752-
scope,
753-
receive,
754-
send,
755-
) as streams:
756-
await self._mcp_server.run(
757-
streams[0],
758-
streams[1],
759-
self._mcp_server.create_initialization_options(),
760-
)
761-
return SilentResponse()
738+
async def handle_sse(request: Request) -> Response:
739+
"""Handle SSE connection using Starlette's EventSourceResponse."""
740+
# Create a custom Response class that wraps the SSE connection
741+
class SSEConnectionResponse(Response):
742+
def __init__(self, sse_transport: SseServerTransport, server: MCPServer) -> None:
743+
super().__init__()
744+
self.sse_transport = sse_transport
745+
self.server = server
746+
747+
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
748+
async with self.sse_transport.connect_sse(scope, receive, send) as streams:
749+
await self.server.run(
750+
streams[0],
751+
streams[1],
752+
self.server.create_initialization_options(),
753+
)
754+
755+
# Return the Response object for Starlette to handle
756+
return SSEConnectionResponse(sse, self._mcp_server)
762757

763758
# Create routes
764759
routes: list[Route | Mount] = []
@@ -796,7 +791,7 @@ async def handle_sse(scope: Scope, receive: Receive, send: Send):
796791
)
797792
)
798793

799-
# When auth is configured, require authentication
794+
# Create auth wrapper if needed
800795
if self._token_verifier:
801796
# Determine resource metadata URL
802797
resource_metadata_url = None
@@ -807,11 +802,16 @@ async def handle_sse(scope: Scope, receive: Receive, send: Send):
807802
str(self.settings.auth.resource_server_url).rstrip("/") + "/.well-known/oauth-protected-resource"
808803
)
809804

810-
# Auth is enabled, wrap the endpoints with RequireAuthMiddleware
805+
# # Auth is enabled, wrap the endpoints with RequireAuthMiddleware
806+
async def handle_sse_auth(scope: Scope, receive: Receive, send: Send) -> None:
807+
request = Request(scope, receive)
808+
response = await handle_sse(request)
809+
await response(scope, receive, send)
810+
811811
routes.append(
812812
Route(
813813
self.settings.sse_path,
814-
endpoint=RequireAuthMiddleware(handle_sse, required_scopes, resource_metadata_url),
814+
endpoint=RequireAuthMiddleware(handle_sse_auth, required_scopes, resource_metadata_url),
815815
methods=["GET"],
816816
)
817817
)
@@ -824,14 +824,10 @@ async def handle_sse(scope: Scope, receive: Receive, send: Send):
824824
else:
825825
# Auth is disabled, no need for RequireAuthMiddleware
826826
# Since handle_sse is an ASGI app, we need to create a compatible endpoint
827-
async def sse_endpoint(request: Request) -> Response:
828-
# Convert the Starlette request to ASGI parameters
829-
return await handle_sse(request.scope, request.receive, request._send) # type: ignore[reportPrivateUsage]
830-
831827
routes.append(
832828
Route(
833829
self.settings.sse_path,
834-
endpoint=sse_endpoint,
830+
endpoint=handle_sse,
835831
methods=["GET"],
836832
)
837833
)

0 commit comments

Comments
 (0)