Skip to content

Commit 0322681

Browse files
committed
Fall back to unpinned mcp when distribution metadata is missing
Also pin down two test contracts on the new requirement helper: the dev-version fallback preserves extras, and a user-supplied mcp[cli] coexists with the pinned requirement rather than deduplicating into it.
1 parent ae217fd commit 0322681

2 files changed

Lines changed: 27 additions & 3 deletions

File tree

src/mcp/cli/claude.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@ def mcp_requirement(package: str = "mcp") -> str:
2020
an unpinned `mcp` means the latest stable release — not necessarily the
2121
version the user installed (pre-releases in particular are never selected
2222
without an explicit pin). Source builds carry dev/local version segments
23-
that are not published to PyPI, so they fall back to the unpinned form.
23+
that are not published to PyPI, so they fall back to the unpinned form,
24+
as does a missing distribution (no metadata to pin from).
2425
"""
25-
version = importlib.metadata.version("mcp")
26+
try:
27+
version = importlib.metadata.version("mcp")
28+
except importlib.metadata.PackageNotFoundError:
29+
return package
2630
if ".dev" in version or "+" in version:
2731
return package
2832
return f"{package}=={version}"

tests/cli/test_claude.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def test_mcp_requirement_leaves_dev_versions_unpinned(monkeypatch: pytest.Monkey
4343
"""Dev versions are not published to PyPI, so the requirement falls back to the unpinned package."""
4444
_set_mcp_version(monkeypatch, "2.0.0a2.dev3")
4545
assert mcp_requirement() == "mcp"
46+
assert mcp_requirement("mcp[cli]") == "mcp[cli]"
4647

4748

4849
def test_mcp_requirement_leaves_local_versions_unpinned(monkeypatch: pytest.MonkeyPatch):
@@ -51,6 +52,17 @@ def test_mcp_requirement_leaves_local_versions_unpinned(monkeypatch: pytest.Monk
5152
assert mcp_requirement() == "mcp"
5253

5354

55+
def test_mcp_requirement_falls_back_when_mcp_is_not_installed(monkeypatch: pytest.MonkeyPatch):
56+
"""Without distribution metadata there is no version to pin, so the requirement stays unpinned."""
57+
58+
def raise_not_found(distribution_name: str) -> str:
59+
raise importlib.metadata.PackageNotFoundError(distribution_name)
60+
61+
monkeypatch.setattr(importlib.metadata, "version", raise_not_found)
62+
assert mcp_requirement() == "mcp"
63+
assert mcp_requirement("mcp[cli]") == "mcp[cli]"
64+
65+
5466
def _read_server(config_dir: Path, name: str) -> dict[str, Any]:
5567
config = json.loads((config_dir / "claude_desktop_config.json").read_text())
5668
return config["mcpServers"][name]
@@ -75,13 +87,21 @@ def test_file_spec_without_object_suffix(config_dir: Path):
7587

7688

7789
def test_with_packages_sorted_and_deduplicated(config_dir: Path):
78-
"""Extra packages should appear as --with flags, sorted and deduplicated with mcp[cli]."""
90+
"""Extra packages should appear as sorted --with flags with duplicates removed."""
7991
assert update_claude_config(file_spec="s.py:app", server_name="s", with_packages=["zebra", "aardvark", "zebra"])
8092

8193
args = _read_server(config_dir, "s")["args"]
8294
assert args[:8] == ["run", "--frozen", "--with", "aardvark", "--with", "mcp[cli]==1.2.3", "--with", "zebra"]
8395

8496

97+
def test_explicit_mcp_cli_kept_alongside_pinned_requirement(config_dir: Path):
98+
"""A user-supplied mcp[cli] no longer collapses into the pinned requirement; uv resolves both to the pin."""
99+
assert update_claude_config(file_spec="s.py:app", server_name="s", with_packages=["mcp[cli]"])
100+
101+
args = _read_server(config_dir, "s")["args"]
102+
assert args[:6] == ["run", "--frozen", "--with", "mcp[cli]", "--with", "mcp[cli]==1.2.3"]
103+
104+
85105
def test_with_editable_adds_flag(config_dir: Path, tmp_path: Path):
86106
"""with_editable should add --with-editable after the --with flags."""
87107
editable = tmp_path / "project"

0 commit comments

Comments
 (0)