test(mcp): compare the installed SDK against the pyproject pin for every 2.0-only file

Move the require_mcp_2_sdk fixture from tests/tools/conftest.py to the root
tests/conftest.py so tests/test_mcp_serve.py can use it too, and read the
required version from the `mcp==X` pin in pyproject's [mcp] extra instead of
a hardcoded "2.0.0" — the pin moves, the guard follows.

Sharpen the remaining presence-only guards named in the issue that still went
red under an installed-but-older SDK on current main: tests/test_mcp_serve.py
(the mcp_server_e2e fixture and TestServerCreation asserted `mcp.server.MCPServer`,
absent in 1.x) and tests/tools/test_mcp_oauth.py::TestCallbackPortReservation
(2.0 AuthorizationCodeResult vs the 1.x tuple). test_mcp_oauth_manager.py and
test_mcp_device_flow.py already pass under 1.28.1 on current main, so they are
left alone; PR #114068's importorskip hunks in those files do not overlap these
edits.
This commit is contained in:
teknium1
2026-09-18 00:55:12 -07:00
committed by Teknium
parent dc9db93b20
commit b43976ea52
4 changed files with 39 additions and 28 deletions

View File

@@ -1266,6 +1266,40 @@ def _relocate_basetemp_outside_operator_home(config) -> None:
config.option.basetemp = str(safe)
def _pinned_mcp_sdk_version() -> str:
"""The ``mcp==X`` pin carried by the ``[mcp]`` extra in pyproject.toml."""
import tomllib
with open(Path(__file__).resolve().parent.parent / "pyproject.toml", "rb") as fh:
extras = tomllib.load(fh)["project"]["optional-dependencies"]
for req in extras["mcp"]:
if req.startswith("mcp=="):
return req.split("==", 1)[1].strip()
raise RuntimeError("pyproject.toml [mcp] extra no longer pins mcp==X")
@pytest.fixture
def require_mcp_2_sdk():
"""Skip tests that pin mcp 2.0-only behaviour when an older SDK is installed.
The runtime deliberately supports both SDK generations (the dual streamable-client probe in
mcp_tool), so a stale ``mcp`` distribution imports fine and presence-only guards let these
tests through — where they fail later with opaque SDK errors. Compare the installed
distribution against the pin so the outcome is an explicit skip with an actionable reason.
"""
from importlib.metadata import PackageNotFoundError, version as dist_version
from packaging.version import Version
pinned = _pinned_mcp_sdk_version()
try:
found = dist_version("mcp")
except PackageNotFoundError:
pytest.skip(f"requires mcp=={pinned} (not installed); install the [mcp] extra")
if Version(found) < Version(pinned):
pytest.skip(f"requires mcp=={pinned} (found {found}); install the [mcp] extra")
@pytest.hookimpl(trylast=True) # after _pytest.tmpdir has built config._tmp_path_factory
def pytest_configure(config): # noqa: D401 — pytest hook
"""Register markers used by hermetic conftest."""

View File

@@ -522,9 +522,8 @@ class TestEventBridge:
# ---------------------------------------------------------------------------
@pytest.fixture
def mcp_server_e2e(populated_sessions_dir, mock_session_db, monkeypatch):
"""Create a fully wired MCP server for E2E testing."""
mcp = pytest.importorskip("mcp", reason="MCP SDK not installed")
def mcp_server_e2e(populated_sessions_dir, mock_session_db, monkeypatch, require_mcp_2_sdk):
"""Create a fully wired MCP server for E2E testing (pinned SDK: 1.x lacks mcp.server.MCPServer)."""
import mcp_serve
monkeypatch.setattr(mcp_serve, "_get_sessions_dir", lambda: populated_sessions_dir)
monkeypatch.setattr(mcp_serve, "_get_session_db", lambda: mock_session_db)
@@ -987,14 +986,14 @@ class TestToolRegistration:
# ---------------------------------------------------------------------------
class TestServerCreation:
@pytest.mark.usefixtures("require_mcp_2_sdk")
def test_create_server(self, populated_sessions_dir, monkeypatch):
pytest.importorskip("mcp", reason="MCP SDK not installed")
import mcp_serve
monkeypatch.setattr(mcp_serve, "_get_sessions_dir", lambda: populated_sessions_dir)
assert mcp_serve.create_mcp_server() is not None
@pytest.mark.usefixtures("require_mcp_2_sdk")
def test_create_with_bridge(self, populated_sessions_dir, monkeypatch):
pytest.importorskip("mcp", reason="MCP SDK not installed")
import mcp_serve
monkeypatch.setattr(mcp_serve, "_get_sessions_dir", lambda: populated_sessions_dir)
bridge = mcp_serve.EventBridge()

View File

@@ -55,29 +55,6 @@ def _materialize_mcp_sdk_symbols():
yield
@pytest.fixture
def require_mcp_2_sdk():
"""Skip tests that pin mcp 2.0-only behaviour when an older SDK is installed.
The runtime deliberately supports both SDK generations (the dual
streamable-client probe in mcp_tool), so a stale ``mcp`` distribution
imports fine and presence-only guards let these tests through — where
they fail later with opaque SDK errors. Compare the installed
distribution against the 2.0.0 pin carried by the ``[mcp]`` extra so the
outcome is an explicit skip with an actionable reason.
"""
from importlib.metadata import PackageNotFoundError, version as dist_version
from packaging.version import Version
try:
found = dist_version("mcp")
except PackageNotFoundError:
pytest.skip("mcp SDK not installed; install the [mcp] extra")
if Version(found) < Version("2.0.0"):
pytest.skip(f"requires mcp>=2.0.0 (found {found}); install the [mcp] extra")
@pytest.fixture(autouse=True)
def _clear_web_result_cache():
"""Reset the web_search TTL memo between tests.

View File

@@ -543,6 +543,7 @@ class TestCallbackHandlerErrorEscaping:
# TOCTOU port reservation (#22161)
# ---------------------------------------------------------------------------
@pytest.mark.usefixtures("require_mcp_2_sdk")
class TestCallbackPortReservation:
"""The socket picked at selection time stays bound until callback bind.