Skip to content

refactor: extract MCPIntegrator from cli.py#215

Merged
danielmeppiel merged 2 commits intomainfrom
refactor/extract-mcp-integrator
Mar 9, 2026
Merged

refactor: extract MCPIntegrator from cli.py#215
danielmeppiel merged 2 commits intomainfrom
refactor/extract-mcp-integrator

Conversation

@danielmeppiel
Copy link
Collaborator

Summary

Closes #209 — Extract MCPIntegrator: move MCP lifecycle logic out of cli.py

Moves 11 MCP bare functions (~760 lines) from cli.py into a standalone MCPIntegrator class in src/apm_cli/integration/mcp_integrator.py.

What changed

New file: src/apm_cli/integration/mcp_integrator.py

A static-method namespace class containing all MCP lifecycle orchestration:

Old name (cli.py) New name (MCPIntegrator)
_collect_transitive_mcp_deps collect_transitive
_deduplicate_mcp_deps deduplicate
_build_self_defined_server_info _build_self_defined_info
_apply_mcp_overlay _apply_overlay
_get_mcp_dep_names get_server_names
_remove_stale_mcp_servers remove_stale
_update_lockfile_mcp_servers update_lockfile
_install_mcp_dependencies install
_detect_runtimes_from_scripts _detect_runtimes
_filter_available_runtimes _filter_runtimes
_install_for_runtime _install_for_runtime

Design decisions

  • Not a BaseIntegrator subclass — MCP integration is config-level orchestration (registry APIs, runtime configs, lockfile), not file-level deployment like the other integrators.
  • All @staticmethod — the class is a logical namespace, not a stateful object. No instantiation needed.
  • Hardened error handling — replaced 6 bare except: pass blocks with logger.debug(..., exc_info=True) for debuggability.
  • Cycle guard — added visited set to _collect_descendants in cli.py to prevent infinite loops in circular dependency graphs.

cli.py impact

5267 → 4508 lines (−759 net). Call sites in install() and uninstall() updated to use MCPIntegrator.*.

Test updates

5 test files updated to point imports and @patch targets to the new module:

  • tests/unit/test_mcp_overlays.py
  • tests/unit/test_mcp_lifecycle_e2e.py
  • tests/unit/test_transitive_mcp.py
  • tests/unit/test_runtime_detection.py
  • tests/integration/test_selective_install_mcp.py

Verification

All 1729 tests pass (0 failures, 92 skipped).

Move 11 MCP lifecycle functions (~760 lines) from cli.py into a
standalone MCPIntegrator class in integration/mcp_integrator.py.

- collect_transitive, deduplicate, install, remove_stale, update_lockfile
- _build_self_defined_info, _apply_overlay, get_server_names
- _detect_runtimes, _filter_runtimes, _install_for_runtime
- All methods are @staticmethod (namespace, not stateful object)
- Hardened bare except blocks with logger.debug(exc_info=True)
- Added cycle guard to _collect_descendants in cli.py
- Updated 5 test files to use new import paths and patch targets

cli.py: 5267 → 4508 lines (-759 net)
Copilot AI review requested due to automatic review settings March 9, 2026 21:02
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extracts MCP lifecycle orchestration out of the monolithic cli.py into a dedicated MCPIntegrator module under src/apm_cli/integration/, keeping CLI call sites and tests aligned with the new location.

Changes:

  • Added src/apm_cli/integration/mcp_integrator.py containing MCP dependency resolution, install orchestration, stale cleanup, lockfile updates, and runtime detection helpers.
  • Updated cli.py to call MCPIntegrator.* and added a cycle guard to dependency tree traversal.
  • Updated unit/integration tests to import/patch the new module paths.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/apm_cli/integration/mcp_integrator.py New standalone orchestrator for MCP lifecycle logic previously embedded in cli.py.
src/apm_cli/cli.py Switched MCP call sites to MCPIntegrator and added a cycle guard to _collect_descendants.
src/apm_cli/integration/__init__.py Exports MCPIntegrator from the integration package.
tests/unit/test_transitive_mcp.py Updated imports and patch targets to the new MCP integrator module.
tests/unit/test_runtime_detection.py Updated runtime detection tests to call MCPIntegrator helpers.
tests/unit/test_mcp_overlays.py Updated overlay/self-defined server tests to the new integrator paths.
tests/unit/test_mcp_lifecycle_e2e.py Updated end-to-end MCP lifecycle tests to use MCPIntegrator.
tests/integration/test_selective_install_mcp.py Updated selective install integration tests to patch MCPIntegrator.install.
Comments suppressed due to low confidence (1)

src/apm_cli/integration/mcp_integrator.py:896

  • Same as above: configured_count is incremented unconditionally for self-defined deps, even if runtime configuration fails. Consider aggregating per-runtime results and only counting the server as configured when at least one runtime update succeeds (or adjusting the docstring/return semantics).
                if console:
                    console.print(
                        f"│  [green]✓[/green]  {dep.name} → "
                        f"{', '.join([rt.title() for rt in target_runtimes])}"
                    )
                configured_count += 1

- Early exit when all runtimes excluded via --exclude
- Add encoding='utf-8' to apm.yml file open
- Gate configured_count on _install_for_runtime success (returns bool)
- Log MCP cleanup errors instead of silently swallowing
@danielmeppiel danielmeppiel merged commit 80e7cb5 into main Mar 9, 2026
7 checks passed
@danielmeppiel danielmeppiel deleted the refactor/extract-mcp-integrator branch March 9, 2026 21:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extract MCPIntegrator: move MCP lifecycle logic out of cli.py

2 participants