fix(mcp): get_context resolves by name without mode=#9
Conversation
A bare get_context {"name": X} silently returned {"contexts":[],"count":0}:
the name-resolution path was gated behind a non-empty `mode` (the block
`if (!mode.empty() && has_name)`), so name-only calls fell through to the
id-only path and resolved nothing — with no error. Found driving the MCP tools
against the chi corpus (get_context name=NewRouter / ServeHTTP both empty).
The old id-only-no-mode contract mirrored Go's handleGetObjectContext, but the
Go oracle is retired (goldens are the sole oracle), and a silent empty result
for a valid symbol name is exactly the "synthetic-passes-while-real-is-empty"
trap the suite guards against. Run the name path for any name lookup; `mode`
still tunes depth/sections via apply_context_lookup_mode.
Verified on chi: name=NewRouter -> 1 context, name=ServeHTTP -> 3 overloads,
each with signature/location/purity.
Tests/golden updated to the corrected contract:
- HandlersFixture.GetContextNameOnlyReturnsEmpty -> GetContextNameOnlyResolves
(asserts count>0 + symbol_name), inverting the test that locked the bug.
- FeatureAudit.ChiGetContextByNameWorks: was EXPECT_FALSE(result.empty()) which
passed on {count:0} (JSON object non-empty) — now asserts count>0 + matching
symbol. (This weak assertion is why the bug shipped.)
- mcp/get_context/basic golden regenerated (name=Add resolves; file_path
normalized to ${CORPUS}); spec _rationale rewritten to document the divergence.
Full suite 1692/1692.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
find_files' fuzzy fallback scored on the Levenshtein DISTANCE ratio
(lev/max_len) and then tested `>= 0.7` — inverted. Unrelated filenames (high
distance) passed and EVERY file flooded in at ~0.574, while genuine near-misses
were rejected. Verified driving the MCP tool: find_files "mux" on chi and
"routing" on fastapi each returned ~every file at 0.574.
This was a verbatim port of a known Go fuzzer bug ("forgets to compute
similarity, computes 1-that"), preserved for Go parity. The Go oracle is
retired (goldens are the sole oracle), so use the correct similarity:
similarity = 1 - distance/max_len. Now find_files "mux" -> mux.go only; a typo
"handlr" still fuzzy-matches handler.go (0.43); unrelated "zzzzzzz" -> nothing.
Tests/golden updated to the corrected contract:
- FindFilesGlobFuzzyStarGo / FindFilesGlobFuzzyNonMatchingPattern (which
asserted the flood — "must reproduce this... to keep parity") replaced with
FindFilesFuzzyMatchesNearName (near-miss matches, unrelated doesn't) and
FindFilesUnrelatedPatternNoFuzzyFlood (unrelated pattern -> no fuzzy).
- mcp/find_files/basic golden regenerated (pattern "*.go" -> 0 results; it is a
filename pattern, not a glob — use `filter` for extensions); spec _rationale
documents the fix.
Full suite 1692/1692; both regenerated goldens green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Added a second MCP-usefulness fix from the same real-repo hunt: find_files fuzzy similarity was inverted (Levenshtein distance ratio tested as if it were similarity → every file flooded in at 0.574). Corrected to |
|
Third fix from the hunt: search ranked code below docs. The result cap was applied during candidate-file iteration before scoring, so docs/ files filled the cap and code was never collected. Now over-collect→rank→truncate. Measured: chi |
execute_search applied the result cap DURING the candidate-file loop (break at
results.size() >= effective_cap) and only scored/ranked afterward. So the kept
set was the first-N matches in file-iteration order, and the later score+rank
merely reordered those N. On doc-heavy repos the docs/ files filled the cap
before code files were reached, so code matches were never collected and
ranking could not promote them.
Measured (MCP search, max=100):
chi "Router" before 40 .go / 52 .md / 8 .json -> after 100 .go / 0 docs
fastapi APIRouter before 3 .py / 96 .md -> after 55 .py / 44 .md,
top-5 all .py
(code scores ~2x prose, so once collected it wins the cap.)
Fix: separate the collection cap from the output cap — over-collect up to
8x (bounded at 2000), score, rank, THEN truncate to the requested max. Mirrors
the multi-pattern path's existing *4 over-collect. Latency 17.7us -> 21.5us on
BM_RealProjectSearchLatency (still sub-ms); bounded by the 2000 cap on huge
corpora. Default unbounded (max<=0) path unchanged.
Full suite 1692/1692; all search goldens unchanged (small golden corpus never
hits the cap, so behavior there is identical).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
inspect_symbol.callers (and code_insight's caller edges) were empty for methods and near-empty for qualified calls: process_go_reference tagged a call's method name as ReferenceType::Usage, and only bare func() calls produced a Call ref. get_caller_names filters type==Call, so method-call callers were dropped (ServeHTTP: 91 incoming_refs, 0 callers; NewRouter: 115 call sites, 1 caller). For a call_expression whose function is a selector (x.M(...) / pkg.F(...)), tag the FIELD (M) as the Call instead of the whole "x.M" selector — create_reference names a ref by node text, so a Call on the selector was named "x.M" and never resolved, while only the Usage ref on the field carried "M". Mark the field handled so the selector_expression / field_identifier branches don't re-emit a Usage for it. Also label anonymous (closure) callers "<anonymous>" instead of blank in get_caller_names. Verified on chi: NewRouter callers 1 -> 17 (main/articleRouter/adminRouter/ Routes/Route/service/Profiler — the qualified chi.NewRouter() sites now resolve); ServeHTTP methods now surface real callers (routeHTTP, ServeHTTP). CAVEAT (documented, inherent): resolve_reference_target is NAME-string based (same-file match -> package-import disambiguation -> first candidate); there is no receiver-type resolution. So qualified function calls resolve accurately, but method calls on common names (ServeHTTP/String/Error/...) are name-approximate and may attribute to the wrong same-named symbol. This fix improves recall on the existing name-based scheme; type-sound dispatch is a separate, larger effort. Full suite 1692/1692; integration 128/128; no golden regen (synthetic golden corpus has few method calls). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Found driving LCI's MCP tools against the chi corpus.
Bug
get_context {"name": X}(nomode) silently returned{"contexts":[],"count":0}— the name-resolution path was gated behind a non-emptymode. Obvious call shape → empty, no error.Fix
Run the name path for any name lookup (
modestill tunes depth/sections). Verified on chi:NewRouter→1 context,ServeHTTP→3 overloads, each with signature/location/purity.Why it shipped (tests fixed too)
HandlersFixture.GetContextNameOnlyReturnsEmptyasserted the empty result → renamedGetContextNameOnlyResolves, assertscount>0.FeatureAudit.ChiGetContextByNameWorksusedEXPECT_FALSE(result.empty()), which passes on{count:0}(JSON object non-empty) — the weak assertion that masked the bug. Now assertscount>0+ matching symbol.mcp/get_context/basicgolden regenerated (name=Add resolves; path →${CORPUS}); spec_rationalerewritten (old contract mirrored the now-retired Go oracle).Full unit suite 1692/1692.
🤖 Generated with Claude Code