From 0a3a482213b20b2902f96dc9bca7242c8c888559 Mon Sep 17 00:00:00 2001 From: "Alex V." <119082209+Retengart@users.noreply.github.com> Date: Fri, 31 Jul 2026 15:40:23 +0300 Subject: [PATCH] fix(mcp): constrain incompatible SDK versions --- .github/workflows/ci.yml | 21 ++++++++++++++++++++ CHANGELOG.md | 7 +++++++ sdk/mcp/pyproject.toml | 4 ++-- sdk/mcp/tests/test_stdio.py | 38 +++++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 sdk/mcp/tests/test_stdio.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 155b754..24be574 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,3 +58,24 @@ jobs: python -m pip install --quiet pytest python -m pip install --quiet -e . python -m pytest tests/ -q + + mcp-python: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + mcp: + - 'mcp==1.2.0' + # Deliberately tracks the latest compatible 1.x as an early warning. + - 'mcp>=1.2,<2' + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v6 + - name: stdio handshake + run: >- + uv run --isolated --no-project + --with-editable ./sdk/mcp + --with anyio + --with pytest + --with '${{ matrix.mcp }}' + pytest -q sdk/mcp/tests/test_stdio.py diff --git a/CHANGELOG.md b/CHANGELOG.md index ab82a13..be6060d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ Versioning](https://semver.org/spec/v2.0.0.html) once it reaches ## Unreleased +### MCP compatibility + +Constrain `forkd-mcp` to MCP `>=1.2,<2`: its FastMCP import is unavailable +in older 1.x releases and was removed in MCP 2.x. CI now verifies the stdio +initialize handshake and all registered tools against MCP 1.2.0 and the +latest compatible 1.x release. Closes #275. + ## v0.5.3 - 2026-07-22 ### KSM / memfd release catch-up diff --git a/sdk/mcp/pyproject.toml b/sdk/mcp/pyproject.toml index 455f7e3..2bb53a2 100644 --- a/sdk/mcp/pyproject.toml +++ b/sdk/mcp/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "forkd-mcp" -version = "0.2.0" +version = "0.2.1" description = "Model Context Protocol server for forkd microVM sandboxes (BRANCH, diff snapshots, prewarm)" readme = "README.md" authors = [{name = "Deeplethe", email = "info@deeplethe.com"}] @@ -21,7 +21,7 @@ classifiers = [ "Topic :: Software Development :: Libraries", ] dependencies = [ - "mcp>=1.0", + "mcp>=1.2,<2", "httpx>=0.27", ] diff --git a/sdk/mcp/tests/test_stdio.py b/sdk/mcp/tests/test_stdio.py new file mode 100644 index 0000000..5030462 --- /dev/null +++ b/sdk/mcp/tests/test_stdio.py @@ -0,0 +1,38 @@ +import sys + +import anyio +from mcp import ClientSession, StdioServerParameters +from mcp.client.stdio import stdio_client + +EXPECTED_TOOLS = { + "branch_sandbox", + "create_snapshot", + "eval_code", + "exec_command", + "get_sandbox", + "kill_sandbox", + "list_sandboxes", + "list_snapshots", + "ping_sandbox", + "spawn_sandboxes", + "wait_for_text", +} + + +def test_stdio_initialize_and_tool_registration() -> None: + async def scenario() -> None: + params = StdioServerParameters( + command=sys.executable, + args=["-m", "forkd_mcp.server"], + ) + with anyio.fail_after(10): + async with stdio_client(params) as (read_stream, write_stream): + async with ClientSession(read_stream, write_stream) as session: + result = await session.initialize() + tools = await session.list_tools() + + assert result.serverInfo.name == "forkd" + assert len(tools.tools) == len(EXPECTED_TOOLS) + assert {tool.name for tool in tools.tools} == EXPECTED_TOOLS + + anyio.run(scenario)