Skip to content

Commit e9c64e9

Browse files
fix: remove host prefix from lock-file path resolution in transitive MCP collection
_collect_transitive_mcp_deps() included the lock-file 'host' field (e.g. github.com) in the apm_modules path, but packages are stored without the host segment. All locked paths resolved to non-existent directories, so zero transitive MCP deps were collected and .vscode/mcp.json was never generated. - Remove host from path construction to match get_install_path() behavior - Add tests for lock-file-scoped collection (regular and virtual_path)
1 parent f334098 commit e9c64e9

File tree

2 files changed

+63
-2
lines changed

2 files changed

+63
-2
lines changed

src/apm_cli/cli.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2178,11 +2178,10 @@ def _collect_transitive_mcp_deps(apm_modules_dir: Path, lock_path: Path = None)
21782178
lock_data = yaml.safe_load(f) or {}
21792179
locked_paths = builtins.set()
21802180
for dep in lock_data.get("dependencies", []):
2181-
host = dep.get("host", "github.com")
21822181
repo_url = dep.get("repo_url", "")
21832182
virtual_path = dep.get("virtual_path", "")
21842183
if repo_url:
2185-
yml = apm_modules_dir / host / repo_url / virtual_path / "apm.yml" if virtual_path else apm_modules_dir / host / repo_url / "apm.yml"
2184+
yml = apm_modules_dir / repo_url / virtual_path / "apm.yml" if virtual_path else apm_modules_dir / repo_url / "apm.yml"
21862185
locked_paths.add(yml.resolve())
21872186
except Exception:
21882187
locked_paths = None # Fall back to full scan

tests/unit/test_transitive_mcp.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,68 @@ def test_skips_unparseable_apm_yml(self):
143143
result = _collect_transitive_mcp_deps(Path(tmp))
144144
self.assertEqual(result, [])
145145

146+
def test_lockfile_scopes_collection_to_locked_packages(self):
147+
"""Lock-file filtering should only collect MCP deps from locked packages."""
148+
with tempfile.TemporaryDirectory() as tmp:
149+
apm_modules = Path(tmp) / "apm_modules"
150+
# Package that IS in the lock file
151+
locked_dir = apm_modules / "org" / "locked-pkg"
152+
locked_dir.mkdir(parents=True)
153+
(locked_dir / "apm.yml").write_text(yaml.dump({
154+
"name": "locked-pkg",
155+
"version": "1.0.0",
156+
"dependencies": {"mcp": ["ghcr.io/locked/server"]},
157+
}))
158+
# Package that is NOT in the lock file (orphan)
159+
orphan_dir = apm_modules / "org" / "orphan-pkg"
160+
orphan_dir.mkdir(parents=True)
161+
(orphan_dir / "apm.yml").write_text(yaml.dump({
162+
"name": "orphan-pkg",
163+
"version": "1.0.0",
164+
"dependencies": {"mcp": ["ghcr.io/orphan/server"]},
165+
}))
166+
# Write lock file referencing only the locked package
167+
lock_path = Path(tmp) / "apm.lock"
168+
lock_path.write_text(yaml.dump({
169+
"lockfile_version": "1",
170+
"dependencies": [
171+
{"repo_url": "org/locked-pkg", "host": "github.com"},
172+
],
173+
}))
174+
result = _collect_transitive_mcp_deps(apm_modules, lock_path)
175+
self.assertEqual(result, ["ghcr.io/locked/server"])
176+
177+
def test_lockfile_with_virtual_path(self):
178+
"""Lock-file filtering works for subdirectory (virtual_path) packages."""
179+
with tempfile.TemporaryDirectory() as tmp:
180+
apm_modules = Path(tmp) / "apm_modules"
181+
# Subdirectory package matching lock entry
182+
sub_dir = apm_modules / "org" / "monorepo" / "skills" / "azure"
183+
sub_dir.mkdir(parents=True)
184+
(sub_dir / "apm.yml").write_text(yaml.dump({
185+
"name": "azure-skill",
186+
"version": "1.0.0",
187+
"dependencies": {"mcp": [{"name": "learn", "type": "http", "url": "https://learn.example.com"}]},
188+
}))
189+
# Another subdirectory NOT in the lock
190+
other_dir = apm_modules / "org" / "monorepo" / "skills" / "other"
191+
other_dir.mkdir(parents=True)
192+
(other_dir / "apm.yml").write_text(yaml.dump({
193+
"name": "other-skill",
194+
"version": "1.0.0",
195+
"dependencies": {"mcp": ["ghcr.io/other/server"]},
196+
}))
197+
lock_path = Path(tmp) / "apm.lock"
198+
lock_path.write_text(yaml.dump({
199+
"lockfile_version": "1",
200+
"dependencies": [
201+
{"repo_url": "org/monorepo", "host": "github.com", "virtual_path": "skills/azure"},
202+
],
203+
}))
204+
result = _collect_transitive_mcp_deps(apm_modules, lock_path)
205+
self.assertEqual(len(result), 1)
206+
self.assertEqual(result[0]["name"], "learn")
207+
146208

147209
# ---------------------------------------------------------------------------
148210
# _deduplicate_mcp_deps

0 commit comments

Comments
 (0)