Skip to content

fix(mcp): get_context resolves by name without mode=#9

Merged
andylbrummer merged 4 commits into
mainfrom
fix-mcp-getcontext-name
Jun 19, 2026
Merged

fix(mcp): get_context resolves by name without mode=#9
andylbrummer merged 4 commits into
mainfrom
fix-mcp-getcontext-name

Conversation

@andylbrummer

Copy link
Copy Markdown
Member

Found driving LCI's MCP tools against the chi corpus.

Bug

get_context {"name": X} (no mode) silently returned {"contexts":[],"count":0} — the name-resolution path was gated behind a non-empty mode. Obvious call shape → empty, no error.

Fix

Run the name path for any name lookup (mode still tunes depth/sections). Verified on chi: NewRouter→1 context, ServeHTTP→3 overloads, each with signature/location/purity.

Why it shipped (tests fixed too)

  • HandlersFixture.GetContextNameOnlyReturnsEmpty asserted the empty result → renamed GetContextNameOnlyResolves, asserts count>0.
  • FeatureAudit.ChiGetContextByNameWorks used EXPECT_FALSE(result.empty()), which passes on {count:0} (JSON object non-empty) — the weak assertion that masked the bug. Now asserts count>0 + matching symbol.
  • mcp/get_context/basic golden regenerated (name=Add resolves; path → ${CORPUS}); spec _rationale rewritten (old contract mirrored the now-retired Go oracle).

Full unit suite 1692/1692.

🤖 Generated with Claude Code

andylbrummer and others added 2 commits June 16, 2026 21:39
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>
@andylbrummer

Copy link
Copy Markdown
Member Author

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 1 - dist/maxlen. Verified: mux→mux.go only, typo handlr→handler.go, unrelated→nothing. Tests that asserted the flood replaced; golden regenerated. Full suite 1692/1692.

@andylbrummer

Copy link
Copy Markdown
Member Author

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 Router 40→100 code/0 docs; fastapi APIRouter 3→55 code, top-5 all .py. Latency 17.7→21.5µs (still sub-ms). 1692/1692, goldens unchanged.

andylbrummer and others added 2 commits June 17, 2026 00:06
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>
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.

1 participant