From c4dc40bf9b06f6648d199362e26ee944a3639add Mon Sep 17 00:00:00 2001 From: Yufeng He <40085740+he-yufeng@users.noreply.github.com> Date: Sun, 24 May 2026 03:57:46 +0800 Subject: [PATCH] fix: exit cleanly on stdio KeyboardInterrupt --- src/mcp/server/mcpserver/server.py | 5 ++++- tests/server/mcpserver/test_server.py | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/mcp/server/mcpserver/server.py b/src/mcp/server/mcpserver/server.py index b3471163b7..26b23ef072 100644 --- a/src/mcp/server/mcpserver/server.py +++ b/src/mcp/server/mcpserver/server.py @@ -293,7 +293,10 @@ def run( match transport: case "stdio": - anyio.run(self.run_stdio_async) + try: + anyio.run(self.run_stdio_async) + except KeyboardInterrupt: + return case "sse": # pragma: no cover anyio.run(lambda: self.run_sse_async(**kwargs)) case "streamable-http": # pragma: no cover diff --git a/tests/server/mcpserver/test_server.py b/tests/server/mcpserver/test_server.py index 3457ec944a..b61c4da091 100644 --- a/tests/server/mcpserver/test_server.py +++ b/tests/server/mcpserver/test_server.py @@ -74,6 +74,17 @@ def test_dependencies(self): mcp_no_deps = MCPServer("test") assert mcp_no_deps.dependencies == [] + def test_run_stdio_exits_cleanly_on_keyboard_interrupt(self, monkeypatch: pytest.MonkeyPatch): + mcp = MCPServer("test") + + def raise_keyboard_interrupt(func: Any) -> None: + assert func == mcp.run_stdio_async + raise KeyboardInterrupt + + monkeypatch.setattr("mcp.server.mcpserver.server.anyio.run", raise_keyboard_interrupt) + + mcp.run("stdio") + async def test_sse_app_returns_starlette_app(self): """Test that sse_app returns a Starlette application with correct routes.""" mcp = MCPServer("test")