feat(mcp): query tools — get_callers/callees/deps, find_path, search_code (T5/T7/T8)#679
Conversation
…code Bundles T5 (#653), T7 (#655), T8 (#656) into one PR; all three are thin async wrappers around existing AsyncGraphQuery operations and share the same _node_summary / _coerce_node_id / _project_arg helpers. - search_code: prefix search backed by the FalkorDB fulltext index. Surfaces flat {id, name, label, file, line} so agents can hand the id straight to the navigation tools. - get_callers / get_callees: incoming / outgoing CALLS edges. The shared _neighbors_payload inlines the IN-direction Cypher because AsyncGraphQuery.get_neighbors only walks OUT. - get_dependencies: same machinery, but aggregates a configurable set of relations (default CALLS/IMPORTS/DEFINES) and dedups by node id. - find_path: returns up to N CALLS-only paths between two symbols as a node sequence; strips encode_edge entries from the alternating [node, edge, ...] list produced by AsyncGraphQuery.find_paths. Helpers: - _node_summary flattens the encode_node shape (which nests data under 'properties' and includes the 'Searchable' fulltext-index label) into the {id, name, label, file, line} agents want. - _coerce_node_id accepts int or stringified-int and rejects bool. Tests (tests/mcp/test_query_tools.py, 13 tests): - search_code prefix happy/limit/no-match/serialisability paths. - get_callees(entrypoint) ⊇ {service}; get_callers(service) ⊇ {entrypoint}. - get_dependencies includes the CALLS relation. - Neighbor tools accept string ids; reject garbage. - find_path(entrypoint → db) ≥ 1; reverse direction returns []; max_paths is honored. - All five tools are registered on the MCP app. Also drops a stray venv/ that snuck into the fixture directory and was polluting the prefix-search results. Closes #653, #655, #656. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughThe PR parameterizes query limits across ChangesCode navigation query tools and MCP integration
Sequence DiagramsequenceDiagram
participant Agent
participant MCP Tool
participant Validator
participant AsyncGraphQuery
participant FalkorDB
participant Normalizer
Agent->>MCP Tool: call get_callers(symbol_id=42, limit=50)
MCP Tool->>Validator: coerce symbol_id to int
MCP Tool->>AsyncGraphQuery: _get_neighbors(node_id=42, rel=CALLS, dir=IN)
AsyncGraphQuery->>FalkorDB: MATCH (n)-[r:CALLS]->(target) LIMIT $limit
FalkorDB-->>AsyncGraphQuery: rows with node and relation data
AsyncGraphQuery->>Normalizer: _normalize_node(node) for each
Normalizer-->>AsyncGraphQuery: {id, name, label, file, line}
AsyncGraphQuery-->>MCP Tool: enriched results with relation/direction
MCP Tool-->>Agent: list[dict] with caller nodes and metadata
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR adds new MCP “query” tools to the api/mcp server so agents can perform code-graph navigation and discovery against an indexed FalkorDB graph (callers/callees/dependencies, call-path discovery, and prefix search), along with an integration-oriented test module for these tools.
Changes:
- Implemented MCP tools in
api/mcp/tools/structural.py:search_code,get_callers,get_callees,get_dependencies,find_pathplus shared helpers. - Added a bundled test suite
tests/mcp/test_query_tools.pycovering core behaviors (basic correctness, limit handling, string-id coercion, tool registration).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
api/mcp/tools/structural.py |
Adds T5/T7/T8 MCP tool implementations and shared helpers for node summaries and id coercion. |
tests/mcp/test_query_tools.py |
Adds integration tests for new query tools using the shared indexed fixture/contract. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
find_path walks the alternating [node, edge, node, ...] list that AsyncGraphQuery.find_paths returns and was filtering elements with `"properties" in x` to keep only nodes. But FalkorDB's Edge also exposes a `properties` attribute, so edges slipped through and became bogus, all-null `_node_summary` entries in the returned path. Discriminate on `labels` instead: `encode_node` emits a top-level `labels` key while `encode_edge` does not (edges carry `relation`/`src_node`/`dest_node`). The find_path test now asserts every path element resolves a non-null name and label, which catches the edge-leak regression. Also fixed two pre-existing wrong contract keys in the same test file that broke the mcp-tests CI job (`min_paths` -> `paths_count`, `callees_any_of` -> `callees`) and removed an unused `pathlib.Path` import. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… ids
Addresses Copilot review on the T5/T7/T8 query tools:
- get_dependencies: validate agent-supplied relation types before they are
string-interpolated into Cypher (`-[e:{rel}]->`). FalkorDB can't bind
relationship types as parameters, so an unvalidated `rels` value was a
Cypher-injection vector. Reject anything that isn't a bare identifier.
- get_dependencies: request only the remaining limit per relation so total
DB work is bounded by `limit`, not `limit * len(rels)`.
- find_path: push `max_paths` down to the database (new optional `limit` on
AsyncGraphQuery.find_paths) instead of enumerating every path and slicing
in Python — avoids unbounded path enumeration on large graphs.
- search_code: honor `limit` at the database. prefix_search hard-capped at
10 rows in Cypher and the wrapper sliced afterwards, so callers could
never get more than 10. LIMIT is now parameterized (default 10 preserves
existing HTTP/CLI behavior).
- Annotate the public id parameters as `int | str` instead of `Any` so the
generated MCP JSON schema reflects the real input contract.
Adds a Cypher-injection regression test for get_dependencies.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Prerequisites (merge order)
Merge in order — this PR is stacked on:
index_repotool (T4)Base: #678.
Bundles T5 (#653), T7 (#655), and T8 (#656). Stacked on #678 → #677 → #676 → #675 → #666.
Tools
search_code(prefix)prefix_search(FalkorDB fulltext)get_callers(symbol_id)(src)-[:CALLS]->(n)get_callees(symbol_id)(n)-[:CALLS]->(dest)get_dependencies(symbol_id, rels=[CALLS, IMPORTS, DEFINES])find_path(source_id, dest_id, max_paths=5)find_paths(CALLS-only)Shared helpers in
api/mcp/tools/structural.py:_node_summary(flattensencode_node's{id, labels, properties: {...}}and drops theSearchablelabel),_coerce_node_id(int or stringified int; rejects bool),_project_arg(returnsAsyncGraphQuery).Tests
tests/mcp/test_query_tools.py— 13 tests, all green against FalkorDB on 6390. Full MCP suite: 25 passed in 23s.Side fix
Dropped stray
tests/mcp/fixtures/sample_project/venv/that was polluting prefix-search results.Out of scope
T6 (#654,
impact_analysis) — variable-depth Cypher with cycle avoidance, deserves its own PR.Closes #653, #655, #656.
Summary by CodeRabbit
New Features
Tests