diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b3b8e2ee..762659bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,10 +125,22 @@ jobs: os: [ubuntu-latest, macos-latest, windows-latest] python-version: ["3.12"] mcp-version: [minimum, latest] + pydantic-version: [latest] include: - os: ubuntu-latest python-version: "3.10" mcp-version: minimum + pydantic-version: latest + # Exercise the MCP/Pydantic floor pair and the release that removed + # eval_type_backport. Other dependencies still use normal resolution. + - os: ubuntu-latest + python-version: "3.10" + mcp-version: minimum + pydantic-version: "2.11.0" + - os: ubuntu-latest + python-version: "3.12" + mcp-version: minimum + pydantic-version: "2.14.0" steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Python ${{ matrix.python-version }} @@ -149,6 +161,7 @@ jobs: shell: python env: MCP_VERSION: ${{ matrix.mcp-version }} + PYDANTIC_VERSION: ${{ matrix.pydantic-version }} run: | import os from pathlib import Path @@ -161,7 +174,9 @@ jobs: python = env_dir / ("Scripts/python.exe" if os.name == "nt" else "bin/python") requirements = [str(wheel.resolve())] if os.environ["MCP_VERSION"] == "minimum": - requirements.append("mcp[cli]==1.19.0") + requirements.append("mcp[cli]==1.21.1") + if os.environ["PYDANTIC_VERSION"] != "latest": + requirements.append("pydantic==" + os.environ["PYDANTIC_VERSION"]) subprocess.run([str(python), "-m", "pip", "install", *requirements], check=True) subprocess.run([str(python), "-m", "pip", "check"], check=True) subprocess.run([str(python), "-I", "tests/mcp-wheel-smoke.py"], check=True) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 9fda4e0e..90bdc63c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -37,6 +37,12 @@ jobs: fail-fast: false matrix: mcp-version: [minimum, latest] + pydantic-version: [latest] + include: + - mcp-version: minimum + pydantic-version: "2.11.0" + - mcp-version: minimum + pydantic-version: "2.14.0" steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 @@ -54,6 +60,7 @@ jobs: shell: python env: MCP_VERSION: ${{ matrix.mcp-version }} + PYDANTIC_VERSION: ${{ matrix.pydantic-version }} run: | import os from pathlib import Path @@ -66,7 +73,9 @@ jobs: python = env_dir / ("Scripts/python.exe" if os.name == "nt" else "bin/python") requirements = [str(wheel.resolve())] if os.environ["MCP_VERSION"] == "minimum": - requirements.append("mcp[cli]==1.19.0") + requirements.append("mcp[cli]==1.21.1") + if os.environ["PYDANTIC_VERSION"] != "latest": + requirements.append("pydantic==" + os.environ["PYDANTIC_VERSION"]) subprocess.run([str(python), "-m", "pip", "install", *requirements], check=True) subprocess.run([str(python), "-m", "pip", "check"], check=True) subprocess.run([str(python), "-I", "tests/mcp-wheel-smoke.py"], check=True) diff --git a/pyproject.toml b/pyproject.toml index 50d5c171..d512714b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,8 +29,9 @@ dependencies = [ "model2vec>=0.6.0,<1.0", "sqlite-vec>=0.1.0,<1.0", "numpy>=1.24.0,<3.0", - # Tool metadata requires 1.19; SDK 2 removes the FastMCP import path. - "mcp[cli]>=1.19.0,<2.0", + # 1.21.1 removes a private Pydantic import broken by Pydantic 2.14. + # SDK 2 removes the FastMCP import path. + "mcp[cli]>=1.21.1,<2.0", "httpx>=0.27.0,<1.0", "sentence-transformers>=3.0.0,<7.0", "torch>=2.0.0,<3.0", diff --git a/tests/mcp-wheel-smoke.py b/tests/mcp-wheel-smoke.py index 06388fe9..6a0f2522 100644 --- a/tests/mcp-wheel-smoke.py +++ b/tests/mcp-wheel-smoke.py @@ -182,6 +182,16 @@ async def _check_protocol(root: Path, env: dict[str, str], origin: Path) -> None def main() -> None: _require(bool(sys.flags.isolated), "Run this probe with python -I") distribution = importlib.metadata.distribution("truememory") + # Report public package versions before subprocess checks, whose output is + # intentionally redacted. Dependency import failures then remain traceable. + print(json.dumps({ + "stage": "dependency_versions", + "python_version": ".".join(str(part) for part in sys.version_info[:3]), + "truememory_version": distribution.version, + "mcp_version": importlib.metadata.version("mcp"), + "pydantic_version": importlib.metadata.version("pydantic"), + "pydantic_core_version": importlib.metadata.version("pydantic-core"), + }), flush=True) origin = Path(distribution.locate_file("truememory/__init__.py")).resolve() spec = importlib.util.find_spec("truememory") _require(origin.is_file() and spec is not None and spec.origin is not None,