Skip to content

feat(mcp): query tools — get_callers/callees/deps, find_path, search_code (T5/T7/T8)#679

Merged
DvirDukhan merged 5 commits into
stagingfrom
dvirdukhan/mcp-t5-t7-t8-query-tools
Jun 8, 2026
Merged

feat(mcp): query tools — get_callers/callees/deps, find_path, search_code (T5/T7/T8)#679
DvirDukhan merged 5 commits into
stagingfrom
dvirdukhan/mcp-t5-t7-t8-query-tools

Conversation

@DvirDukhan

@DvirDukhan DvirDukhan commented May 27, 2026

Copy link
Copy Markdown
Contributor

Prerequisites (merge order)

Merge in order — this PR is stacked on:

  1. feat(mcp): index_repo tool (T4 #652) #678index_repo tool (T4)

Base: #678.


Bundles T5 (#653), T7 (#655), and T8 (#656). Stacked on #678#677#676#675#666.

Tools

Tool Direction Backing op
search_code(prefix) prefix_search (FalkorDB fulltext)
get_callers(symbol_id) IN inline Cypher (src)-[:CALLS]->(n)
get_callees(symbol_id) OUT inline Cypher (n)-[:CALLS]->(dest)
get_dependencies(symbol_id, rels=[CALLS, IMPORTS, DEFINES]) OUT multi-rel + dedup
find_path(source_id, dest_id, max_paths=5) find_paths (CALLS-only)

Shared helpers in api/mcp/tools/structural.py: _node_summary (flattens encode_node's {id, labels, properties: {...}} and drops the Searchable label), _coerce_node_id (int or stringified int; rejects bool), _project_arg (returns AsyncGraphQuery).

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

    • Added optional result limits to code search and path-finding operations for improved control and performance
    • Introduced new code navigation and analysis tools: symbol search with prefix matching, caller/callee tracking, multi-relation dependency aggregation, and call-path discovery between code symbols
  • Tests

    • Added comprehensive test suite for code navigation and query capabilities

…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>
@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

The PR parameterizes query limits across Graph and AsyncGraphQuery, then introduces five new MCP tools for code navigation: neighbor traversal (callers, callees, dependencies), path discovery, and symbol search. All tools normalize FalkorDB nodes, validate inputs, and include comprehensive tests with protocol registration.

Changes

Code navigation query tools and MCP integration

Layer / File(s) Summary
Graph query limit parameters
api/graph.py
Graph.prefix_search, Graph.find_paths, AsyncGraphQuery.prefix_search, and AsyncGraphQuery.find_paths now accept optional limit parameters (default 10 for search, optional for paths). Hardcoded Cypher LIMIT clauses are replaced with parameterized LIMIT $limit and query parameter binding.
MCP tools foundation and validation
api/mcp/tools/structural.py
Helper functions build scoped AsyncGraphQuery instances for (project, branch) scope, normalize FalkorDB nodes to flat payloads with id, name, label, file, line, and regex-validated relationship-type strings are enforced before Cypher interpolation to prevent injection.
Neighbor navigation tools
api/mcp/tools/structural.py
get_callers and get_callees wrap CALLS-edge traversal with configurable limits; get_dependencies aggregates across IMPORTS, CALLS, and DEFINES (default) with deduplication and explicit DB-work bounding. All return normalized nodes with relation and direction fields.
Path discovery and code search tools
api/mcp/tools/structural.py
find_path discovers up to max_paths CALLS-path sequences between nodes and returns node-only sequences (no edges); search_code performs prefix-based symbol lookup with DB-side limit propagation and normalized node results.
Comprehensive query tool tests
tests/mcp/test_query_tools.py
14 test functions validate search result correctness, limit enforcement, input validation (numeric coercion, relation-type allowlist, garbage ID rejection), path directionality, JSON serializability, and MCP protocol registration. Helper _find_id resolves symbol names to IDs.

Sequence Diagram

sequenceDiagram
  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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • FalkorDB/code-graph#678: Introduces MCP tool auto-discovery and registration infrastructure that the new neighbor/search tools depend on.
  • FalkorDB/code-graph#677: Provides shared test fixtures (tests/mcp/conftest.py, tests/mcp/fixtures/expected.yaml) that the new tool tests use for integration validation.

Suggested reviewers

  • gkorland
  • galshubeli

Poem

🐰 A graph walks into a bar, seeking paths and friends,
"Limit!" it cries, and the query comprehends,
With callers and callees, all properly bound,
MCP tools dance where code connections are found.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the primary change: introducing MCP query tools for code navigation (get_callers/callees/deps, find_path, search_code) and references the tool IDs (T5/T7/T8).
Linked Issues check ✅ Passed The PR implements all core requirements from #653: three neighbor-traversal tools (get_callers, get_callees, get_dependencies) with proper signatures, limit handling, and result formatting. It also adds find_path (T7) and search_code (T8) with corresponding graph method updates and comprehensive tests.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the PR objectives: graph method updates to support limits, new MCP tools, corresponding tests, and helper utilities. No unrelated modifications are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dvirdukhan/mcp-t5-t7-t8-query-tools

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread api/mcp/tools/structural.py Outdated
@DvirDukhan DvirDukhan marked this pull request as ready for review June 8, 2026 12:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_path plus shared helpers.
  • Added a bundled test suite tests/mcp/test_query_tools.py covering 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.

Comment thread api/mcp/tools/structural.py
Comment thread api/mcp/tools/structural.py Outdated
Comment thread api/mcp/tools/structural.py Outdated
Comment thread api/mcp/tools/structural.py
Comment thread api/mcp/tools/structural.py
Comment thread api/mcp/tools/structural.py
Comment thread api/mcp/tools/structural.py
DvirDukhan and others added 2 commits June 8, 2026 16:04
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>
galshubeli
galshubeli previously approved these changes Jun 8, 2026
Base automatically changed from dvirdukhan/mcp-t4-index-repo to staging June 8, 2026 13:49
@galshubeli galshubeli dismissed their stale review June 8, 2026 13:49

The base branch was changed.

@DvirDukhan DvirDukhan merged commit 4135def into staging Jun 8, 2026
10 of 13 checks passed
@DvirDukhan DvirDukhan deleted the dvirdukhan/mcp-t5-t7-t8-query-tools branch June 8, 2026 13:51
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.

[MCP T5] get_callers / get_callees / get_dependencies tools

3 participants