diff --git a/openbb_platform/extensions/mcp_server/openbb_mcp_server/app/app.py b/openbb_platform/extensions/mcp_server/openbb_mcp_server/app/app.py index ee4c19581da..904cbec1bbb 100644 --- a/openbb_platform/extensions/mcp_server/openbb_mcp_server/app/app.py +++ b/openbb_platform/extensions/mcp_server/openbb_mcp_server/app/app.py @@ -942,7 +942,12 @@ async def stdio_main(mcp_server): os._exit(0) # pylint: disable=protected-access for sig in (signal.SIGINT, signal.SIGTERM): - loop.add_signal_handler(sig, signal_handler) + try: + loop.add_signal_handler(sig, signal_handler) + except NotImplementedError: + # Windows event loops do not support signal handlers. The MCP client + # owns the stdio child process and closes it directly. + break logger.info("Starting OpenBB MCP Server in STDIO mode. Press Ctrl+C to stop.") diff --git a/openbb_platform/extensions/mcp_server/tests/app/test_app.py b/openbb_platform/extensions/mcp_server/tests/app/test_app.py index 4caf57bd55c..85421591147 100644 --- a/openbb_platform/extensions/mcp_server/tests/app/test_app.py +++ b/openbb_platform/extensions/mcp_server/tests/app/test_app.py @@ -1,5 +1,6 @@ """Unit tests for app module.""" +import asyncio from unittest.mock import MagicMock, patch import pytest @@ -13,6 +14,7 @@ from openbb_mcp_server.app.app import ( _read_system_prompt_file, _strip_api_prefix, create_mcp_server, + stdio_main, ) from openbb_mcp_server.models.settings import MCPSettings @@ -56,6 +58,18 @@ def test_read_system_prompt_file(tmp_path): assert _read_system_prompt_file("nonexistent.txt") is None +@pytest.mark.asyncio +async def test_stdio_main_runs_when_signal_handlers_are_unsupported(): + """Windows event loops can serve stdio without registering signal handlers.""" + loop = asyncio.get_running_loop() + mcp_server = MagicMock() + + with patch.object(loop, "add_signal_handler", side_effect=NotImplementedError): + await stdio_main(mcp_server) + + mcp_server.run.assert_called_once_with("stdio") + + @patch("openbb_mcp_server.app.app.process_fastapi_routes_for_mcp") @patch("openbb_mcp_server.app.app.CategoryIndex") @patch("openbb_mcp_server.app.app.FastMCP.from_fastapi")