Coverage for gco_mcp / tools / deps.py: 100.00%
17 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-14 22:07 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-09-14 22:07 +0000
1"""Dependency-maintenance tools (update scan + NodePool registry freshness)."""
3import json
5import cli_runner
6from audit import audit_logged
7from server import mcp
10@mcp.tool(tags={"safe", "observability"})
11@audit_logged
12async def deps_scan(nodepools_only: bool = False) -> str:
13 """Generate the dependency update list the monthly deps-scan produces.
15 Runs `gco deps scan` — the same scanner behind the rolling
16 "[Automated] Dependency updates available" GitHub issue — and returns
17 a JSON envelope with `has_drift`, `scan_complete`, and the full
18 Markdown report under `report_markdown`. Surfaces that need AWS
19 credentials or missing host tools are skipped and flagged as
20 incomplete rather than failing.
22 The full scan reaches out to PyPI, npm, container registries, GitHub,
23 and (with credentials) AWS, and typically takes several minutes. Its
24 Python surface pip-installs the project's extras into the server's
25 active environment, mirroring how the CI scan runs. Requires a GCO
26 checkout (the scanner lives under .github/scripts/).
28 Args:
29 nodepools_only: Run only the accelerator-catalog / Karpenter
30 NodePool freshness check (offline policy validation always;
31 live EC2 catalog comparison when AWS credentials resolve).
32 Fast, and the only network it may touch is EC2.
33 """
34 args = ["deps", "scan"]
35 if nodepools_only:
36 args.append("--nodepools-only")
37 # A full scan sweeps several registries and installs the Python extras;
38 # give it far more headroom than the default two minutes.
39 timeout = 300 if nodepools_only else 1800
40 result = await cli_runner._run_cli_async(*args, timeout_seconds=timeout)
41 # _run_cli_async returns either the command's stdout (already a JSON
42 # envelope — the CLI honors the global --output json flag) or its own
43 # JSON error envelope; both are valid JSON strings. Guard anyway so a
44 # partial read never surfaces as an unparseable blob.
45 try:
46 json.loads(result)
47 except TypeError, ValueError:
48 return json.dumps({"error": "deps scan produced unparseable output", "raw": result[:2000]})
49 return result