Part of #82. Depends on #86.
Summary
Expose the graph to agents: find_references and list_imports MCP tools on the existing FastMCP app.
Scope
find_references(symbol, repo?, branch?, kind?): ranked candidate call/reference sites, each with enclosing symbol. Docstring states candidate-set semantics (grep, not LSP) explicitly — agents must know results are candidates.
list_imports(target, repo?, branch?, direction=imports|imported_by): 1-hop import-graph neighborhood for a file/module.
- Both return
json.dumps(dict) through the existing dispatch choke-point; recoverable states (symbol-not-found, truncation) are payload fields per the established contract.
- Document and test the "what tests cover symbol X" composition:
find_references + path filter — no new primitive.
Acceptance criteria
Scope note (added at execution time, PR implementing this issue)
Shipped:
find_references(symbol, ctx, limit=200, branch=None) — thin app/main.py tool over the unchanged service.find_references_payload builder. Corpus-wide, edge_kind="call" fixed. Docstring states candidate-set (grep, not LSP) semantics explicitly.
list_imports(ctx, repo=None, target=None, direction="imports", branch=None, limit=DEFAULT_ROW_LIMIT) — both direction=imports and direction=imported_by ship, via one additive extension of service.list_imports_payload. imports requires repo; imported_by requires target (index-served by ix_reference_edges_target_name, no resolver SQL change). Deterministic PRE-DB structured validation (unsupported_direction / missing_repo / missing_target) lives in the service-layer builder so it stays reachable by future non-MCP callers.
- "What tests cover symbol X" composition:
find_references + client-side filename filter, documented in the tool docstring, README, and the reference-edges runbook, and proven end-to-end in the e2e test.
- Smoke coverage:
validate_references_payload shape predicate (unit-tested) + a live find_references leg in scripts/smoke.py's --enable-mcp check.
Explicitly deferred (candidate follow-up issue):
find_references(repo?, kind?) filters from the original scope sketch — would require an additive find_references_payload change that introduces a repo_known key, which the pinned test tests/unit/test_service.py:588 currently asserts absent. Left for a follow-up that deliberately breaks that pin as an additive contract change.
- Per-file forward imports ("what does file F import") — needs a new resolver source-file filter; out of scope for this additive, resolver-untouched change.
list_imports does not add a list_imports(repo?, kind?)-style symbol-kind filter; direction already fully covers both directions from the original sketch (imports / imported_by).
Part of #82. Depends on #86.
Summary
Expose the graph to agents:
find_referencesandlist_importsMCP tools on the existing FastMCP app.Scope
find_references(symbol, repo?, branch?, kind?): ranked candidate call/reference sites, each with enclosing symbol. Docstring states candidate-set semantics (grep, not LSP) explicitly — agents must know results are candidates.list_imports(target, repo?, branch?, direction=imports|imported_by): 1-hop import-graph neighborhood for a file/module.json.dumps(dict)through the existing dispatch choke-point; recoverable states (symbol-not-found, truncation) are payload fields per the established contract.find_references+ path filter — no new primitive.Acceptance criteria
app/main.py, backed only byapp/service.pybuilders.Scope note (added at execution time, PR implementing this issue)
Shipped:
find_references(symbol, ctx, limit=200, branch=None)— thinapp/main.pytool over the unchangedservice.find_references_payloadbuilder. Corpus-wide,edge_kind="call"fixed. Docstring states candidate-set (grep, not LSP) semantics explicitly.list_imports(ctx, repo=None, target=None, direction="imports", branch=None, limit=DEFAULT_ROW_LIMIT)— bothdirection=importsanddirection=imported_byship, via one additive extension ofservice.list_imports_payload.importsrequiresrepo;imported_byrequirestarget(index-served byix_reference_edges_target_name, no resolver SQL change). Deterministic PRE-DB structured validation (unsupported_direction/missing_repo/missing_target) lives in the service-layer builder so it stays reachable by future non-MCP callers.find_references+ client-side filename filter, documented in the tool docstring, README, and the reference-edges runbook, and proven end-to-end in the e2e test.validate_references_payloadshape predicate (unit-tested) + a livefind_referencesleg inscripts/smoke.py's--enable-mcpcheck.Explicitly deferred (candidate follow-up issue):
find_references(repo?, kind?)filters from the original scope sketch — would require an additivefind_references_payloadchange that introduces arepo_knownkey, which the pinned testtests/unit/test_service.py:588currently asserts absent. Left for a follow-up that deliberately breaks that pin as an additive contract change.list_importsdoes not add alist_imports(repo?, kind?)-style symbol-kind filter;directionalready fully covers both directions from the original sketch (imports/imported_by).