20. MCP Interface¶
Purpose¶
Define the current public MCP surface in the 2.0 beta line.
This interface is optional (installed via the mcp extra). It exposes
the deterministic analysis pipeline as a read-only MCP server for AI agents
and MCP-capable clients. It does not replace the CLI or the canonical report
contract.
Public surface¶
- Package extra:
codeclone[mcp] - MCP launcher:
codeclone-mcp - MCP server:
codeclone/mcp_server.py - MCP service adapter:
codeclone/mcp_service.py
Data model¶
Current server characteristics:
- optional dependency; base
codecloneinstall does not requiremcp - transports:
stdiostreamable-http
- run storage:
- in-memory only
- bounded history (
--history-limit, default4, maximum10) - latest-run pointer for
codeclone://latest/...resources - the
latestpointer moves whenever a neweranalyze_*call registers a run
- run identity:
- canonical run identity is derived from the canonical report integrity digest
- MCP payloads expose a short
run_idhandle (first 8 hex chars) - MCP tools/resources accept both short and full run ids
- MCP finding ids are compact by default and may lengthen when needed to stay unique within a run
- analysis modes:
fullclones_only
- process-count policy:
processesis an optional override- when omitted, MCP defers to the core CodeClone runtime
- initialize metadata:
serverInfo.versionreflects the CodeClone package version- clients may use it for compatibility checks
- root contract:
- analysis tools require an absolute repository root
- relative roots such as
.are rejected in MCP because server cwd may differ from the client workspace - granular
check_*tools may omitrootand use the latest compatible stored run; ifrootis provided, it must also be absolute
- cache policies:
reuseoffrefreshis rejected in MCP because the server is read-only.
- summary payload:
run_id,version,schema,mode, compactanalysis_profilehealth_scopeexplains what the health score coversfocusexplains the active summary/triage lensbaseline,metrics_baseline,cache- untrusted baseline comparisons stay compact but explicit through
baseline.compared_without_valid_baseline,baseline.baseline_python_tag, andbaseline.runtime_python_tag cache.freshnessclassifies summary cache reuse asfresh,mixed, orreused- flattened
inventory(files,lines,functions,classes) - flattened
findings(total,new,known,by_family,production,new_by_source_kind) - flattened
diff(new_clones,health_delta,typing_param_permille_delta,typing_return_permille_delta,docstring_permille_delta,api_breaking_changes,new_api_symbols) - optional
coverage_joinwhen an analysis request includedcoverage_xml(status,overall_permille,coverage_hotspots,scope_gap_hotspots,hotspot_threshold_percent) warnings,failuresanalyze_changed_pathsis intentionally more compact thanget_run_summary: it returnschanged_files, compactbaseline,focus,health_scope,health,health_delta,verdict,new_findings,new_by_source_kind,resolved_findings, and an emptychanged_findingsplaceholder, while detailed changed payload stays inget_report_section(section="changed")
- workflow guidance:
- the MCP surface is intentionally agent-guiding rather than list-first
- the cheapest useful path is designed to be the most obvious path:
get_run_summary/get_production_triagefirst, thenlist_hotspotsorcheck_*, thenget_finding/get_remediation help(topic=...)is a bounded semantic routing tool for contract/workflow uncertainty; it is not a second manual or docs proxy
- finding-list payloads:
- MCP finding ids are compact projection ids; canonical report ids are unchanged
detail_level="summary"is the default for list/check/hotspot toolsdetail_level="summary"keeps compact relative"path:line"locationsdetail_level="normal"keeps structured{path, line, end_line, symbol}locations plus remediationdetail_level="full"keeps the compatibility-oriented payload, includingpriority_factors,items, and per-locationuri- empty design
check_*responses may include a compactthreshold_context(metric,threshold,measured_units,highest_below_threshold) so agents can tell whether the run is truly quiet or just below the active threshold
The MCP layer does not introduce a separate analysis engine. It calls the current CodeClone pipeline and reuses the canonical report document already produced by the report contract.
Tools¶
Current tool set (21 tools):
| Tool | Key parameters | Purpose |
|---|---|---|
analyze_repository |
absolute root, analysis_mode, thresholds, api_surface, coverage_xml, cache/baseline paths |
Full analysis → compact summary; then get_run_summary or get_production_triage |
analyze_changed_paths |
absolute root, changed_paths or git_diff_ref, analysis_mode, api_surface, coverage_xml |
Diff-aware analysis → compact changed-files snapshot |
get_run_summary |
run_id |
Cheapest run snapshot: health, findings, baseline, inventory, active thresholds |
get_production_triage |
run_id, max_hotspots, max_suggestions |
Production-first view: health, hotspots, suggestions, active thresholds |
help |
topic, detail |
Semantic guide for workflow, analysis profile, baseline, coverage, suppressions, review state, changed-scope |
compare_runs |
run_id_before, run_id_after, focus |
Run-to-run delta: regressions, improvements, health change |
evaluate_gates |
run_id, gate thresholds, fail_on_untested_hotspots, coverage_min |
Preview CI gating decisions |
get_report_section |
run_id, section, family, path, offset, limit |
Read report sections; metrics_detail is paginated with family/path filters |
list_findings |
family, severity, novelty, sort_by, detail_level, changed_paths, pagination |
Filtered, paginated findings; use after hotspots or check_* |
get_finding |
finding_id, run_id, detail_level |
Single finding detail by id; defaults to normal |
get_remediation |
finding_id, run_id, detail_level |
Remediation payload for one finding |
list_hotspots |
kind, run_id, detail_level, changed_paths, limit |
Priority-ranked hotspot views; preferred before broad listing |
check_clones |
run_id, root, path, clone_type, source_kind, detail_level |
Clone findings only; health.dimensions includes only clones |
check_complexity |
run_id, root, path, min_complexity, detail_level |
Complexity hotspots only |
check_coupling |
run_id, root, path, detail_level |
Coupling hotspots only |
check_cohesion |
run_id, root, path, detail_level |
Cohesion hotspots only |
check_dead_code |
run_id, root, path, min_severity, detail_level |
Dead-code findings only |
generate_pr_summary |
run_id, changed_paths, git_diff_ref, format |
PR-friendly markdown or JSON summary |
mark_finding_reviewed |
finding_id, run_id, note |
Session-local review marker (in-memory) |
list_reviewed_findings |
run_id |
List reviewed findings for a run |
clear_session_runs |
none | Reset in-memory runs and session state |
All tools are read-only except mark_finding_reviewed and clear_session_runs
(session-local, in-memory). check_* tools query stored runs — call
analyze_repository or analyze_changed_paths first.
Recommended workflow:
get_run_summaryorget_production_triagehelp(topic=...)if contract meaning is unclearlist_hotspotsorcheck_*get_finding→get_remediationgenerate_pr_summary(format="markdown")
metrics_detail families currently include canonical health/quality families
plus overloaded_modules, coverage_adoption, coverage_join, and
api_surface.
For analysis sensitivity, the intended model is:
- start with repo defaults or
pyproject-resolved thresholds - lower thresholds only for an explicit higher-sensitivity exploratory pass
- compare runs only when profile differences are understood
Resources¶
Current fixed resources:
| Resource | Payload | Availability |
|---|---|---|
codeclone://latest/summary |
latest run summary projection | always after at least one run |
codeclone://latest/triage |
latest production-first triage projection | always after at least one run |
codeclone://latest/report.json |
latest canonical report document | always after at least one run |
codeclone://latest/health |
latest health score + dimensions | always after at least one run |
codeclone://latest/gates |
latest gate evaluation result | only after evaluate_gates in current server process |
codeclone://latest/changed |
latest changed-files projection | only for a diff-aware latest run |
codeclone://schema |
schema-style descriptor for canonical report sections | always available |
Current run-scoped URI templates:
| URI template | Payload | Availability |
|---|---|---|
codeclone://runs/{run_id}/summary |
run-specific summary projection | for any stored run |
codeclone://runs/{run_id}/report.json |
run-specific canonical report | for any stored run |
codeclone://runs/{run_id}/findings/{finding_id} |
run-specific canonical finding group | for an existing finding in a stored run |
Fixed resources and URI templates are convenience views over already
registered runs. They do not trigger fresh analysis by themselves.
If a client needs the freshest truth, it must start a fresh analysis run first
(typically with cache_policy="off"), rather than relying on older session
state behind codeclone://latest/....
Contracts¶
- MCP is read-only:
- no source-file mutation
- no baseline update
- no metrics-baseline update
- no cache refresh writes
- Session review markers are ephemeral only:
- stored in memory per server process
- never written to baseline, cache, or report artifacts
streamable-httpdefaults to loopback binding. Non-loopback hosts require explicit--allow-remotebecause the server has no built-in authentication.--allow-remoteexpands the trust boundary materially:- any reachable network client can trigger CPU-intensive analysis
- any reachable network client can read analysis results
- request parameters such as
rootand path filters can still probe repository-relative filesystem structure - use it only on trusted networks or behind a firewall / authenticated reverse proxy
- MCP must reuse current:
- pipeline stages
- baseline trust semantics
- cache semantics
- canonical report contract
coverage_xmlis resolved relative to the absolute root when it is not already absolute. It is a current-run Cobertura input only; MCP must never write it to baseline/cache/report artifacts or treat it as baseline truth.- When
respect_pyproject=true, MCP also respectsgolden_fixture_paths. Clone groups excluded by that policy are omitted from active clone/gate projections but remain available in the canonical report under the optionalfindings.groups.clones.suppressed.*bucket. - Invalid Cobertura XML during
analyze_*does not fail analysis; the stored run carriescoverage_join.status="invalid"plusinvalid_reason.evaluate_gates(fail_on_untested_hotspots=true)on that run is a contract error because hotspot gating requires a valid join. - Inline MCP design-threshold parameters (
complexity_threshold,coupling_threshold,cohesion_threshold) define the canonical design finding universe of that run and are recorded inmeta.analysis_thresholds.design_findings. get_run_summaryis a deterministic convenience projection derived from the canonical report (meta,inventory,findings.summary,metrics.summary.health) plus baseline-diff/gate/changed-files context.get_production_triageis also a deterministic MCP projection over the same canonical run state (summary,derived.hotlists,derived.suggestions, and canonical finding source scope). It must not create a second analysis or remediation truth path.- Canonical JSON remains the source of truth for report semantics.
list_findingsandlist_hotspotsare deterministic projections over the canonical report, not a separate analysis branch.metrics_detail(family="overloaded_modules")exposes the canonical report-only module-hotspot layer, but does not promote it into findings, hotlists, or gate semantics.metrics_detail(family="coverage_join")exposes the canonical current-run coverage join summary/items, including measured coverage hotspots and coverage scope gaps.evaluate_gates(fail_on_untested_hotspots=true)requires a stored run created with validcoverage_xml.get_remediationis a deterministic MCP projection over existing suggestions/explainability data, not a second remediation engine.analysis_mode="clones_only"must mirror the same metric/dependency skip-semantics as the regular pipeline.- Missing optional MCP dependency is handled explicitly by the launcher with a
user-facing install hint and exit code
2.
Invariants (MUST)¶
- Tool names are stable public surface.
- Resource URI shapes are stable public surface.
- Read-only vs session-local tool annotations remain accurate.
analyze_repositoryalways registers exactly one latest run.analyze_changed_pathsrequireschanged_pathsorgit_diff_ref.analyze_repositoryandanalyze_changed_pathsrequire an absoluteroot; relative roots like.are rejected.git_diff_refis validated as a safe single revision expression before invokinggit diff.changed_pathsis a structuredlist[str]of repo-relative paths, not a comma-separated string payload.analyze_changed_pathsmay return the samerun_idas a previous run when the canonical report digest is unchanged; changed-files state is an overlay, not a second canonical report.get_run_summarywith norun_idresolves to the latest stored run.codeclone://latest/...resources always resolve to the latest stored run in the current MCP server process, not to a globally fresh analysis state.- Summary-style MCP payloads expose
cache.freshnessas a derived convenience marker; canonical cache metadata remains available only through canonical report/meta surfaces. get_report_section(section="all")returns the full canonical report document.get_report_section(section="metrics")returns onlymetrics.summary.get_report_section(section="metrics_detail")is intentionally bounded: without filters it returnssummaryplus a hint; withfamilyand/orpathit returns a paginated item slice.get_report_section(section="changed")is available only for diff-aware runs.- MCP short
run_idvalues are session handles over the canonical digest of that run. - MCP summary/normal finding/location payloads use relative paths only and do
not expose absolute
file://URIs. - Finding
locationsandhtml_anchorvalues are stable projections over the current run and do not invent non-canonical ids. - For the same finding id,
source_kindremains consistent acrosslist_findings,list_hotspots, andget_finding. get_finding(detail_level="full")remains the compatibility-preserving full-detail endpoint:priority_factorsand locationuriare still available there.compare_runsis only semantically meaningful when both runs use comparable repository scope/root and analysis settings.compare_runsexposes top-levelcomparableplus optionalreason. When roots or effective analysis settings differ,regressionsandimprovementsbecome empty lists,unchangedandhealth_deltabecomenull, andverdictbecomesincomparable.compare_runs.health_deltaisafter.health - before.healthbetween the two selected comparable runs. It is independent of baseline or metrics-baseline drift.compare_runs.verdictis intentionally conservative but not one-dimensional: it returnsmixedwhen run-to-run finding deltas andhealth_deltadisagree.analysis_mode="clones_only"keeps clone findings fully usable, but MCP surfaces markhealthas unavailable instead of fabricating zeroed metrics.coverage_xmlrequiresanalysis_mode="full"because coverage join depends on function-span metrics.codeclone://latest/triageis a latest-only resource; run-specific triage is available via the tool, not via acodeclone://runs/{run_id}/...resource URI.
Failure modes¶
| Condition | Behavior |
|---|---|
mcp extra not installed |
codeclone-mcp prints install hint and exits 2 |
| Invalid root path / invalid numeric config | service raises contract error |
coverage_xml with analysis_mode="clones_only" |
service raises contract error |
| Coverage hotspot gate without valid coverage join | service raises contract error |
| Requested run missing | service raises run-not-found error |
| Requested finding missing | service raises finding-not-found error |
| Unsupported report section/resource suffix | service raises contract error |
Determinism / canonicalization¶
- MCP run identity is derived from canonical report integrity digest.
- Finding order is inherited from canonical report ordering.
- Hotlists are derived from canonical report data and deterministic derived ids.
- No MCP-only heuristics may change analysis or gating semantics.
- MCP must not re-synthesize design findings from raw metrics after the run; threshold-aware design findings belong to the canonical report document.
- Coverage join ordering and hotspot gates are inherited from canonical
metrics.families.coverage_joinfacts.
Locked by tests¶
tests/test_mcp_service.py::test_mcp_service_analyze_repository_registers_latest_runtests/test_mcp_service.py::test_mcp_service_lists_findings_and_hotspotstests/test_mcp_service.py::test_mcp_service_changed_runs_remediation_and_review_flowtests/test_mcp_service.py::test_mcp_service_granular_checks_pr_summary_and_resourcestests/test_mcp_service.py::test_mcp_service_evaluate_gates_on_existing_runtests/test_mcp_service.py::test_mcp_service_resources_expose_latest_summary_and_reporttests/test_mcp_server.py::test_mcp_server_exposes_expected_read_only_toolstests/test_mcp_server.py::test_mcp_server_tool_roundtrip_and_resourcestests/test_mcp_server.py::test_mcp_server_main_reports_missing_optional_dependency
Non-guarantees¶
- There is currently no standalone
mcp_api_versionconstant. - In-memory run history does not survive process restart.
clear_session_runsresets the in-memory run registry and related session caches, but does not mutate baseline/cache/report artifacts on disk.- Client-specific UI/approval behavior is not part of the CodeClone contract.