From e97742305def10dc919f54f282ff2b60d52349fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=87=E5=BE=90?= Date: Thu, 31 Jul 2025 21:09:11 +0800 Subject: [PATCH 1/4] fid sse client disconnect --- examples/fastmcp/simple_echo.py | 7 +++++-- .../simple-prompt/mcp_simple_prompt/server.py | 4 ++-- .../simple-resource/mcp_simple_resource/server.py | 4 ++-- .../servers/simple-tool/mcp_simple_tool/server.py | 4 ++-- src/mcp/server/fastmcp/server.py | 12 +++++++++++- src/mcp/server/sse.py | 2 +- tests/server/test_sse_security.py | 4 ++-- tests/shared/test_sse.py | 5 +++-- 8 files changed, 28 insertions(+), 14 deletions(-) diff --git a/examples/fastmcp/simple_echo.py b/examples/fastmcp/simple_echo.py index c26152646..8db04e702 100644 --- a/examples/fastmcp/simple_echo.py +++ b/examples/fastmcp/simple_echo.py @@ -2,13 +2,16 @@ FastMCP Echo Server """ -from mcp.server.fastmcp import FastMCP +from mcp.server.fastmcp import FastMCP,Context # Create server mcp = FastMCP("Echo Server") @mcp.tool() -def echo(text: str) -> str: +def echo(text: str, ctx: Context) -> str: """Echo the input text""" + ctx.request_context.request.query_params.get("session_id") return text + +mcp.run(transport="sse") \ No newline at end of file diff --git a/examples/servers/simple-prompt/mcp_simple_prompt/server.py b/examples/servers/simple-prompt/mcp_simple_prompt/server.py index b562cc932..e3f26c934 100644 --- a/examples/servers/simple-prompt/mcp_simple_prompt/server.py +++ b/examples/servers/simple-prompt/mcp_simple_prompt/server.py @@ -91,8 +91,8 @@ async def get_prompt( if transport == "sse": from mcp.server.sse import SseServerTransport from starlette.applications import Starlette - from starlette.responses import Response from starlette.routing import Mount, Route + from mcp.server.fastmcp.server import SilentResponse sse = SseServerTransport("/messages/") @@ -103,7 +103,7 @@ async def handle_sse(request): await app.run( streams[0], streams[1], app.create_initialization_options() ) - return Response() + return SilentResponse() starlette_app = Starlette( debug=True, diff --git a/examples/servers/simple-resource/mcp_simple_resource/server.py b/examples/servers/simple-resource/mcp_simple_resource/server.py index cef29b851..8f0dba2bb 100644 --- a/examples/servers/simple-resource/mcp_simple_resource/server.py +++ b/examples/servers/simple-resource/mcp_simple_resource/server.py @@ -58,7 +58,7 @@ async def read_resource(uri: AnyUrl) -> str | bytes: if transport == "sse": from mcp.server.sse import SseServerTransport from starlette.applications import Starlette - from starlette.responses import Response + from mcp.server.fastmcp.server import SilentResponse from starlette.routing import Mount, Route sse = SseServerTransport("/messages/") @@ -70,7 +70,7 @@ async def handle_sse(request): await app.run( streams[0], streams[1], app.create_initialization_options() ) - return Response() + return SilentResponse() starlette_app = Starlette( debug=True, diff --git a/examples/servers/simple-tool/mcp_simple_tool/server.py b/examples/servers/simple-tool/mcp_simple_tool/server.py index bf3683c9e..0cc1846a6 100644 --- a/examples/servers/simple-tool/mcp_simple_tool/server.py +++ b/examples/servers/simple-tool/mcp_simple_tool/server.py @@ -59,8 +59,8 @@ async def list_tools() -> list[types.Tool]: if transport == "sse": from mcp.server.sse import SseServerTransport from starlette.applications import Starlette - from starlette.responses import Response from starlette.routing import Mount, Route + from mcp.server.fastmcp.server import SilentResponse sse = SseServerTransport("/messages/") @@ -71,7 +71,7 @@ async def handle_sse(request): await app.run( streams[0], streams[1], app.create_initialization_options() ) - return Response() + return SilentResponse() starlette_app = Starlette( debug=True, diff --git a/src/mcp/server/fastmcp/server.py b/src/mcp/server/fastmcp/server.py index 924baaa9b..73e748434 100644 --- a/src/mcp/server/fastmcp/server.py +++ b/src/mcp/server/fastmcp/server.py @@ -52,6 +52,16 @@ logger = get_logger(__name__) +class SilentResponse(Response): + """A response that does not send any HTTP response back to the client.""" + + def __init__(self) -> None: + super().__init__() + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> Awaitable[None]: + pass + + class Settings(BaseSettings, Generic[LifespanResultT]): """FastMCP server settings. @@ -748,7 +758,7 @@ async def handle_sse(scope: Scope, receive: Receive, send: Send): streams[1], self._mcp_server.create_initialization_options(), ) - return Response() + return SilentResponse() # Create routes routes: list[Route | Mount] = [] diff --git a/src/mcp/server/sse.py b/src/mcp/server/sse.py index b7ff33280..bcd2b547c 100644 --- a/src/mcp/server/sse.py +++ b/src/mcp/server/sse.py @@ -23,7 +23,7 @@ async def handle_sse(request): streams[0], streams[1], app.create_initialization_options() ) # Return empty response to avoid NoneType error - return Response() + return SilentResponse() # Create and run Starlette app starlette_app = Starlette(routes=routes) diff --git a/tests/server/test_sse_security.py b/tests/server/test_sse_security.py index 43af35061..dd0be811e 100644 --- a/tests/server/test_sse_security.py +++ b/tests/server/test_sse_security.py @@ -10,10 +10,10 @@ import uvicorn from starlette.applications import Starlette from starlette.requests import Request -from starlette.responses import Response from starlette.routing import Mount, Route from mcp.server import Server +from mcp.server.fastmcp.server import SilentResponse from mcp.server.sse import SseServerTransport from mcp.server.transport_security import TransportSecuritySettings from mcp.types import Tool @@ -55,7 +55,7 @@ async def handle_sse(request: Request): except ValueError as e: # Validation error was already handled inside connect_sse logger.debug(f"SSE connection failed validation: {e}") - return Response() + return SilentResponse() routes = [ Route("/sse", endpoint=handle_sse), diff --git a/tests/shared/test_sse.py b/tests/shared/test_sse.py index 39ae13524..2751c88fa 100644 --- a/tests/shared/test_sse.py +++ b/tests/shared/test_sse.py @@ -19,6 +19,7 @@ from mcp.client.session import ClientSession from mcp.client.sse import sse_client from mcp.server import Server +from mcp.server.fastmcp.server import SilentResponse from mcp.server.sse import SseServerTransport from mcp.server.transport_security import TransportSecuritySettings from mcp.shared.exceptions import McpError @@ -91,7 +92,7 @@ def make_server_app() -> Starlette: async def handle_sse(request: Request) -> Response: async with sse.connect_sse(request.scope, request.receive, request._send) as streams: await server.run(streams[0], streams[1], server.create_initialization_options()) - return Response() + return SilentResponse() app = Starlette( routes=[ @@ -354,7 +355,7 @@ def run_context_server(server_port: int) -> None: async def handle_sse(request: Request) -> Response: async with sse.connect_sse(request.scope, request.receive, request._send) as streams: await context_server.run(streams[0], streams[1], context_server.create_initialization_options()) - return Response() + return SilentResponse() app = Starlette( routes=[ From 9019b4bc606e844e0babad613bda06ed0c92e33f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=87=E5=BE=90?= Date: Thu, 31 Jul 2025 22:25:32 +0800 Subject: [PATCH 2/4] fix pyright check error --- src/mcp/server/fastmcp/server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mcp/server/fastmcp/server.py b/src/mcp/server/fastmcp/server.py index 73e748434..00e028fd8 100644 --- a/src/mcp/server/fastmcp/server.py +++ b/src/mcp/server/fastmcp/server.py @@ -58,8 +58,8 @@ class SilentResponse(Response): def __init__(self) -> None: super().__init__() - async def __call__(self, scope: Scope, receive: Receive, send: Send) -> Awaitable[None]: - pass + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + return class Settings(BaseSettings, Generic[LifespanResultT]): From 98129c25906142178908be6ae6813fa5161d65d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=87=E5=BE=90?= Date: Thu, 31 Jul 2025 22:49:37 +0800 Subject: [PATCH 3/4] recover unused change --- examples/fastmcp/simple_echo.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/fastmcp/simple_echo.py b/examples/fastmcp/simple_echo.py index 8db04e702..c26152646 100644 --- a/examples/fastmcp/simple_echo.py +++ b/examples/fastmcp/simple_echo.py @@ -2,16 +2,13 @@ FastMCP Echo Server """ -from mcp.server.fastmcp import FastMCP,Context +from mcp.server.fastmcp import FastMCP # Create server mcp = FastMCP("Echo Server") @mcp.tool() -def echo(text: str, ctx: Context) -> str: +def echo(text: str) -> str: """Echo the input text""" - ctx.request_context.request.query_params.get("session_id") return text - -mcp.run(transport="sse") \ No newline at end of file From f29a898a8ee7e8e4c3d9ddf8e36b663fd14da26f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=87=E5=BE=90?= Date: Thu, 31 Jul 2025 22:57:02 +0800 Subject: [PATCH 4/4] fix ruff check --- examples/servers/simple-prompt/mcp_simple_prompt/server.py | 2 +- examples/servers/simple-resource/mcp_simple_resource/server.py | 2 +- examples/servers/simple-tool/mcp_simple_tool/server.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/servers/simple-prompt/mcp_simple_prompt/server.py b/examples/servers/simple-prompt/mcp_simple_prompt/server.py index e3f26c934..634ac43dc 100644 --- a/examples/servers/simple-prompt/mcp_simple_prompt/server.py +++ b/examples/servers/simple-prompt/mcp_simple_prompt/server.py @@ -89,10 +89,10 @@ async def get_prompt( ) if transport == "sse": + from mcp.server.fastmcp.server import SilentResponse from mcp.server.sse import SseServerTransport from starlette.applications import Starlette from starlette.routing import Mount, Route - from mcp.server.fastmcp.server import SilentResponse sse = SseServerTransport("/messages/") diff --git a/examples/servers/simple-resource/mcp_simple_resource/server.py b/examples/servers/simple-resource/mcp_simple_resource/server.py index 8f0dba2bb..80fc51521 100644 --- a/examples/servers/simple-resource/mcp_simple_resource/server.py +++ b/examples/servers/simple-resource/mcp_simple_resource/server.py @@ -56,9 +56,9 @@ async def read_resource(uri: AnyUrl) -> str | bytes: return SAMPLE_RESOURCES[name]["content"] if transport == "sse": + from mcp.server.fastmcp.server import SilentResponse from mcp.server.sse import SseServerTransport from starlette.applications import Starlette - from mcp.server.fastmcp.server import SilentResponse from starlette.routing import Mount, Route sse = SseServerTransport("/messages/") diff --git a/examples/servers/simple-tool/mcp_simple_tool/server.py b/examples/servers/simple-tool/mcp_simple_tool/server.py index 0cc1846a6..8e30aa59b 100644 --- a/examples/servers/simple-tool/mcp_simple_tool/server.py +++ b/examples/servers/simple-tool/mcp_simple_tool/server.py @@ -57,10 +57,10 @@ async def list_tools() -> list[types.Tool]: ] if transport == "sse": + from mcp.server.fastmcp.server import SilentResponse from mcp.server.sse import SseServerTransport from starlette.applications import Starlette from starlette.routing import Mount, Route - from mcp.server.fastmcp.server import SilentResponse sse = SseServerTransport("/messages/")