From 2723b21c7f65724b809a6f2208da9d055c355ee3 Mon Sep 17 00:00:00 2001 From: guptaishaan Date: Tue, 28 Jul 2026 13:36:18 -0700 Subject: [PATCH] fix(fetch): cap mcp dependency below 2.0 mcp 2.0.0 renamed McpError to MCPError, so server.py:6 raises ImportError at module import and the server exits before the initialize handshake. The dependency was declared as mcp>=1.1.3 with no upper bound, so unpinned launchers such as uvx resolve 2.0.0 and every fresh install breaks. Cap the requirement at mcp>=1.1.3,<2 and regenerate uv.lock. Resolution is unchanged at mcp 1.23.0, only the recorded specifier moves. No alias import, because 2.0.0 also changed the constructor from McpError(ErrorData(code=..., message=...)) to MCPError(code, message, data=None). Importing MCPError under the old name would import cleanly and then mis-construct all 8 call sites, replacing a loud startup crash with quiet runtime breakage. The 2.x migration belongs on v2/main. Add a regression test that parses pyproject.toml and asserts the declared mcp specifier does not admit 2.0.0. It fails on the unbounded specifier and passes with the cap. Verified by reproducing the exact traceback against mcp==2.0.0, then confirming a fresh unpinned install resolves mcp 1.29.0 and completes a real stdio initialize handshake with exit 0. --- src/fetch/pyproject.toml | 2 +- src/fetch/tests/test_server.py | 23 +++++++++++++++++++++++ src/fetch/uv.lock | 2 +- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/fetch/pyproject.toml b/src/fetch/pyproject.toml index 84735f278a..4cbf1e0b2c 100644 --- a/src/fetch/pyproject.toml +++ b/src/fetch/pyproject.toml @@ -18,7 +18,7 @@ classifiers = [ dependencies = [ "httpx>=0.27", "markdownify>=0.13.1", - "mcp>=1.1.3", + "mcp>=1.1.3,<2", "protego>=0.3.1", "pydantic>=2.0.0", "readabilipy>=0.2.0", diff --git a/src/fetch/tests/test_server.py b/src/fetch/tests/test_server.py index 96c1cb38c7..be82914d14 100644 --- a/src/fetch/tests/test_server.py +++ b/src/fetch/tests/test_server.py @@ -1,8 +1,11 @@ """Tests for the fetch MCP server.""" +import tomllib import pytest +from pathlib import Path from unittest.mock import AsyncMock, patch, MagicMock from mcp.shared.exceptions import McpError +from packaging.requirements import Requirement from mcp_server_fetch.server import ( extract_content_from_html, @@ -324,3 +327,23 @@ async def test_fetch_with_proxy(self): # Verify AsyncClient was called with proxy mock_client_class.assert_called_once_with(proxy="http://proxy.example.com:8080") + + +class TestDeclaredDependencies: + """Tests for the dependencies declared in pyproject.toml.""" + + def test_mcp_requirement_excludes_2x(self): + """Test that the mcp requirement excludes the 2.x line. + + mcp 2.0.0 renamed McpError to MCPError and changed its constructor, so + server.py fails to import against it. Without an upper bound, unpinned + launchers such as uvx resolve 2.x and the server dies on startup. + """ + pyproject = Path(__file__).parent.parent / "pyproject.toml" + with pyproject.open("rb") as f: + dependencies = tomllib.load(f)["project"]["dependencies"] + + requirements = [Requirement(dep) for dep in dependencies] + mcp_requirement = next(req for req in requirements if req.name == "mcp") + + assert not mcp_requirement.specifier.contains("2.0.0") diff --git a/src/fetch/uv.lock b/src/fetch/uv.lock index ea9b0567e2..4f6f729518 100644 --- a/src/fetch/uv.lock +++ b/src/fetch/uv.lock @@ -582,7 +582,7 @@ dev = [ requires-dist = [ { name = "httpx", specifier = ">=0.27" }, { name = "markdownify", specifier = ">=0.13.1" }, - { name = "mcp", specifier = ">=1.1.3" }, + { name = "mcp", specifier = ">=1.1.3,<2" }, { name = "protego", specifier = ">=0.3.1" }, { name = "pydantic", specifier = ">=2.0.0" }, { name = "readabilipy", specifier = ">=0.2.0" },