From 5acaeba2bb0ed14f703f3bdd4cf185cc5d97303d Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sun, 24 May 2026 04:44:59 -0700 Subject: [PATCH] fix(mcp): raise ImportError instead of NameError when stdio SDK missing (#31450) When the 'mcp' Python SDK isn't installed, _run_stdio leaked a bare 'NameError: name StdioServerParameters is not defined' because the top-level 'from mcp import ...' fails inside try/except ImportError, leaving the names unbound at module scope. Mirror the _MCP_HTTP_AVAILABLE gate that _run_http already had: raise a clear ImportError with install instructions instead. Fixes #30904 --- tests/tools/test_mcp_tool.py | 21 +++++++++++++++++++++ tools/mcp_tool.py | 9 +++++++++ 2 files changed, 30 insertions(+) diff --git a/tests/tools/test_mcp_tool.py b/tests/tools/test_mcp_tool.py index 3212a350c37..b9a3cfcf8d9 100644 --- a/tests/tools/test_mcp_tool.py +++ b/tests/tools/test_mcp_tool.py @@ -1462,6 +1462,27 @@ class TestHTTPConfig: asyncio.run(_test()) + def test_stdio_unavailable_raises_importerror_not_nameerror(self): + """Regression test for #30904. + + When the mcp SDK isn't installed, ``_run_stdio`` previously leaked a + bare ``NameError: name 'StdioServerParameters' is not defined``. The + gate now raises a clear ``ImportError`` with install instructions, + mirroring ``_run_http``'s behaviour when the HTTP transport is + unavailable. + """ + from tools.mcp_tool import MCPServerTask + + server = MCPServerTask("local") + config = {"command": "python3", "args": ["/tmp/echo.py"]} + + async def _test(): + with patch("tools.mcp_tool._MCP_AVAILABLE", False): + with pytest.raises(ImportError, match=r"mcp.*SDK"): + await server._run_stdio(config) + + asyncio.run(_test()) + def test_http_seeds_initial_protocol_header(self): from tools.mcp_tool import LATEST_PROTOCOL_VERSION, MCPServerTask diff --git a/tools/mcp_tool.py b/tools/mcp_tool.py index e50efc05a0c..75c1c5e8633 100644 --- a/tools/mcp_tool.py +++ b/tools/mcp_tool.py @@ -1255,6 +1255,15 @@ class MCPServerTask: async def _run_stdio(self, config: dict): """Run the server using stdio transport.""" + if not _MCP_AVAILABLE: + raise ImportError( + f"MCP server '{self.name}' requires the 'mcp' Python SDK, but " + "it is not installed. Install with:\n" + " pip install 'hermes-agent[mcp]'\n" + "or (full install):\n" + " pip install 'hermes-agent[all]'" + ) + command = config.get("command") args = config.get("args", []) user_env = config.get("env")