Skip to content

fix(build): ignore global MCP IDs in legacy detection - #2441

Open
aryanbonigala wants to merge 1 commit into
Graphify-Labs:v8from
aryanbonigala:fix/2408-mcp-legacy-id-false-positive
Open

fix(build): ignore global MCP IDs in legacy detection#2441
aryanbonigala wants to merge 1 commit into
Graphify-Labs:v8from
aryanbonigala:fix/2408-mcp-legacy-id-false-positive

Conversation

@aryanbonigala

Copy link
Copy Markdown

Summary

graph_has_legacy_ids() uses source_location == "L1" as a cheap proxy for "this node is file-level, so its ID is unambiguously the file stem." MCP ingestion breaks that assumption: JSON exposes no line information, so mcp_ingest stamps every node it emits with line 1 — including node kinds whose IDs are deliberately global rather than derived from source_file.

mcp_command, mcp_package, and env_var are global by design: mcp_command_npx is meant to be the same node across every config file that invokes npx, so its ID never carries a file stem.

For a nested config such as sub/.mcp.json, the canonical stem is sub_mcp while _old_file_stems() also yields the bare pre-#1504 form mcp. A perfectly valid, freshly generated mcp_command_npx therefore matches the old mcp_ stem prefix but not the canonical one, and the heuristic reports the graph as legacy. Because the IDs are correct, rebuilding does not clear it — the warning is permanent.

A root-level .mcp.json never tripped this: there mcp is the canonical stem, so the same IDs hit the canonical branch first.

The fix excludes only the three documented global MCP kinds from the heuristic, via a named _MCP_GLOBAL_ID_KINDS set and a _has_global_id() predicate that reads metadata.mcp_kind defensively (missing or non-dict metadata is not exempted and does not raise). File-scoped MCP nodes (mcp_config_file, mcp_server) are stem-derived and remain fully subject to legacy detection, as do genuine pre-#1504 IDs. Callers, warning wording, and the graph schema are unchanged.

Fixes #2408

Testing

Focused legacy/MCP tests:

$ uv run --frozen pytest tests/test_build.py -k "legacy or mcp" -q
12 passed, 62 deselected, 1 warning in 0.24s

The three new assertions fail on the base commit (both mcp_-prefixed global kinds and the nested end-to-end case) and pass with the fix.

Related suites:

$ uv run --frozen pytest tests/test_build.py tests/test_mcp_ingest.py \
    tests/test_id_normalization_contract.py tests/test_extract.py -q
297 passed, 1 skipped, 1 warning in 1.57s

Full suite:

$ uv run --frozen pytest -q
3914 passed, 35 skipped, 3 warnings in 46.66s

Lint and whitespace:

$ uv run --frozen ruff check graphify tests
All checks passed!

$ git diff --check
(clean)

uv.lock is unchanged (git diff -- uv.lock is empty).

The end-to-end regression (test_fresh_mcp_graph_is_not_flagged_legacy) is parametrized over nested sub/.mcp.json and root-level .mcp.json, and drives real extraction through graphify.extract.extract() rather than a hand-built node dict — it asserts mcp_command_npx is actually present before asserting the graph is not flagged. A companion test keeps mcp_config_file / mcp_server nodes and malformed-metadata nodes under detection so the exemption cannot widen silently.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).


Graphify review — findings

This PR addresses a false-positive in the legacy-ID detection logic for MCP nodes. In graphify/build.py, it adds a new _MCP_GLOBAL_ID_KINDS set and a _has_global_id helper, then makes graph_has_legacy_ids skip nodes whose IDs are global by construction (like mcp_command, mcp_package, env_var) rather than derived from the source file stem. The intent is to prevent freshly-built graphs containing a nested .mcp.json from being incorrectly flagged as using an outdated ID scheme. The change also adds parametrized unit tests and an end-to-end extraction test in tests/test_build.py covering the exemption, the narrowness of that exemption for file-scoped MCP kinds, and malformed metadata handling.

No blocking issues surfaced.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 802 functions depend on the 200 functions this change touches.

Health — this change adds coupling hotspots:

  • worse: graph_has_legacy_ids() — 9 callers, 6 callees

Verification — 802 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 601 function(s) in the blast radius were not formally verified this run

· 1 grounded finding(s) anchored inline below.

Comment thread graphify/build.py
return meta.get("mcp_kind") in _MCP_GLOBAL_ID_KINDS


def graph_has_legacy_ids(nodes: list, root: str | Path | None = None, sample: int = 300) -> bool:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Health regressiongraph_has_legacy_ids()

fans out to 6 callees (efferent coupling); 9 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

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.

graph_has_legacy_ids false-positives on any repo with a nested .mcp.json, nagging users to rebuild forever

1 participant