fix(mcp): resolver reliability, structured diagnostics, token-efficiency tools#12
Open
Znbiz wants to merge 9 commits into
Open
fix(mcp): resolver reliability, structured diagnostics, token-efficiency tools#12Znbiz wants to merge 9 commits into
Znbiz wants to merge 9 commits into
Conversation
sysinfo can report exactly 0 bytes available under memory pressure. The old threshold check (avail < 1_500_000_000) treated that the same as "no memory left" and permanently forced graph-only mode (no embeddings). Extract the check into should_skip_embeddings_for_available_memory() and treat 0 as an unreliable reading instead of OOM, so the server still attempts to load the embedding model.
…ctured not-found diagnostics
Several independent but related resolution bugs made uri+line and nodeId
lookups unreliable across almost every MCP tool:
- nodeId args were read with `.as_str()` only, so a numeric `{"nodeId": 1306}`
(the same JSON type codegraph_symbol_search itself returns) silently failed
to resolve. Add arg_node_id() to accept both.
- The indexer stores node paths relative to the workspace root, while
handlers required an absolute file:// URI and compared with `==` — the two
never matched. Worse, a failed URI parse silently became an empty string,
which could accidentally match synthetic nodes and return an unrelated
symbol with no error at all. Add paths_match()/resolve_uri_to_path() and
use them everywhere a strict `==`/file:// URI was assumed.
- find_nearest_node treated nodes without an explicit line range (e.g. a
Python module's CodeFile node) as a 0..0 range, which won every
containment check for target_line == 0 and shadowed real symbols. Skip
nodes without a line range instead.
- Failed resolutions returned one generic "Could not find symbol" message
regardless of cause. Add SymbolNotFoundReason (invalid_uri /
workspace_not_indexed / file_not_indexed) with a hint and
suggested_next_queries(), wired into every uri+line/nodeId tool.
- Add a match_confidence ("exact" | "fallback") field, always present, so
callers don't have to reconstruct trust level from the optional
used_fallback field.
- codegraph_reindex_workspace could report status: "success" on a reindex
that left the graph empty. Compare node_count after indexing against files
processed and mark the response "degraded"; auto-retry once with
force=true before returning, since that's what reliably fixes it.
A dedicated, minimal-cost way to check "did uri+line/nodeId resolve to the symbol I expect" before paying for a full get_symbol_info or get_detailed_symbol call. Returns only name/type/node_id/uri/line_start/ line_end/used_fallback/match_confidence — no source, no callers/callees, no graph traversal. Added to the Core tool profile.
…mode get_detailed_symbol called query_engine.get_symbol_info(node_id), which already computes callers/callees at depth 1 and embeds them in the nested symbol object, and then called get_callers/get_callees again with the same arguments for the top-level fields — the response contained every caller twice (once nested, once top-level) plus a redundant graph traversal. Introduce DetailedSymbolMeta (the same metadata, without callers/callees) and derive the top-level callers/callees from the single get_symbol_info call instead. Add an opt-in `compact` flag that caps callers/callees at 5 entries each (with `*_truncated` counts) for symbols with large caller lists, where that list dominates response size.
get_edit_context always computed all 5 sections (symbol/callers/tests/ memories/recentChanges) and the symbol's full source code, even when a caller only needed one or two of them. Add EditContextOptions (includeSymbol/includeCallers/includeTests/ includeMemories/includeRecentChanges, maxCallers, maxTests — defaults unchanged from prior behavior) and thread it through get_edit_context so disabling a section skips its underlying query entirely, not just its serialization — the compute savings matter as much as the token savings. EditContextSymbol.code becomes optional so includeSymbol=false can omit the single most expensive field while still returning name/type/location.
Agents had no cheap way to check whether the current index is stale or empty before trusting a "no results" answer, or to see what other projects share the same on-disk graph database. Reuses existing infrastructure (the _registry:<slug> entries persist_graph already writes, graph_db_generation(), GitExecutor::head_commit(), IndexState::all_hashes()) rather than adding new storage. Returns current node/edge counts, generation, last-indexed time, a short git HEAD hint, a cheap re-hash-based staleness check against already-tracked files (no directory walk — that's what a real reindex is for), the other namespaces sharing this machine's graph.db, and suggested_next_queries when something looks off. Also surfaces codegraph_index_health as a suggested next query on a degraded codegraph_reindex_workspace response.
Two independent bugs meant a call from inside one method to another via
self./cls. — the majority of calls inside any Python class — never became a
Calls edge in the graph:
- ir_to_graph registered functions in node_map only under their bare name,
but extract_class extracts calls with a qualified caller name
("ClassName.method_name"). Looking up the caller always missed, so the
call fell through to cross-file unresolved-call resolution instead of a
same-file edge.
- extract_callee_name returned the full attribute text ("self.helper") for
a `self.helper()` call instead of the bare method name, which couldn't
match anything in node_map either.
Register methods under both the bare and qualified name, and strip
self./cls. from the callee name before matching. Also fixes Contains
(class -> method) edges incorrectly linking same-named methods across two
different classes in one file, which used the same bare-name lookup.
get_callers/get_callees/get_call_graph/analyze_impact on methods were
systematically undercounting call relationships unless the callee happened
to also be found by the cross-file fallback.
Summarizes the resolver, MCP tool, and Python call-graph fixes on this branch: what was broken, what changed, and the measured effect of each.
…ADME Удалён временный MCP_FIX_REPORT.md — его содержимое перенесено в раздел Unreleased CHANGELOG.md без выпуска новой версии.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
nodeIdargs, relative/absolute path mismatches, and aCodeFile-node resolution bug that together madeuri+line/nodeIdlookups unreliable across most MCP tools.SymbolNotFoundReasondiagnostics +suggested_next_queries()and an always-presentmatch_confidencefield, replacing one generic "Could not find symbol" message.codegraph_reindex_workspacenow detects a reindex that left the graph empty (status: "degraded") and auto-retries once withforce=true.codegraph_probe_symbol(cheapest possible symbol lookup) andcodegraph_index_health(cheap staleness/cross-namespace check).callers/calleesincodegraph_get_detailed_symbol(previously returned twice) and adds an opt-incompactmode.codegraph_get_edit_contextsections independently toggleable, skipping the underlying query (not just serialization) when disabled.self.method()/cls.method()calls — the majority of intra-class calls — never becameCallsedges in the graph.See
MCP_FIX_REPORT.mdfor the full before/after/effect writeup per change.Test plan
cargo test -p codegraph-server --lib— 369 passed, 0 failed, 1 ignoredcargo test -p codegraph-python --lib— 35 passed, 0 failedcargo build -p codegraph-server/-p codegraph-python) — verified via isolated worktrees per commit