feat(mcp): GraphRAG ask tool — init + prompt seam + tool (T9/T10/T11)#681
feat(mcp): GraphRAG ask tool — init + prompt seam + tool (T9/T10/T11)#681DvirDukhan wants to merge 1 commit into
Conversation
…10/T11) Bundles three tightly-coupled tickets: T9 builds the per-(project,branch) KnowledgeGraph cache, T10 adds the prompt-override seam, T11 wires both together into the `ask` MCP tool that gives agents natural-language access to the graph. T9 (#657) — api/mcp/graphrag_init.py - get_or_create_kg(project, branch) — process-wide cache keyed by (project, branch). Identity-stable: same key returns the same KG. - reset_cache() for tests. - Reuses the hand-coded ontology from api/llm.define_ontology (200+ lines of File/Class/Function descriptions the LLM relies on for Cypher quality). Do NOT replace with auto-extraction. - Graph name uses the T17 convention `code:{project}:{branch}` so it matches what index_repo writes. T9 — api/llm.py rename - _define_ontology → define_ontology (drop underscore so it's importable). Internal callers updated. No other call sites in the repo. T10 (#658) — api/mcp/code_prompts.py - Thin re-export of api.prompts (CYPHER_GEN_SYSTEM/PROMPT, GRAPH_QA_SYSTEM/PROMPT). The value is the seam: when the MCP ask tool needs agent-flavoured prompts (vs human-chat framing), the divergence happens here without touching api/prompts.py. T11 (#659) — api/mcp/tools/ask.py - ask(question, project, branch=None) MCP tool. - Uses get_or_create_kg + chat_session().send_message() in an executor so the MCP event loop stays responsive. - Returns the design-doc-mandated {answer, cypher_query, context_nodes} shape. cypher_query is the transparency requirement so agents can verify the executed query and learn the schema. - _normalize_response tolerates the graphrag-sdk response shape variance ({response/answer, cypher/query, context/results}). - Errors are surfaced as a structured {error: ...} payload, never as a transport exception — the agent always sees a valid tool result. Tests (14 new, all pass with mocked LiteModel — no network in CI): - tests/mcp/test_code_prompts.py (3): re-exports match originals, __all__ shape, snapshot hash stability. - tests/mcp/test_graphrag_init.py (5): per-branch graph name, cache identity, distinct keys yield distinct instances, ontology reuse, define_ontology is public. - tests/mcp/test_ask.py (6): tool registered, normalised payload, alternate response keys, plain-string response, errors surfaced as payload, JSON serialisable. Full MCP suite still green (48 passed in 27.5s). Out of scope per tickets: real-LLM E2E (Phase 1.5 with API-key secrets), streaming, multi-turn memory, prompt iteration. Closes #657, #658, #659. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 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
Adds the first GraphRAG-powered MCP “ask” capability by introducing a per-(project, branch) KnowledgeGraph initializer, an MCP-specific prompt seam, and the ask tool wrapper so agents can ask NL questions and receive an answer plus the executed Cypher for transparency.
Changes:
- Introduces
api/mcp/graphrag_init.pyto construct/cacheKnowledgeGraphinstances per(project, branch)while reusing the hand-coded ontology fromapi/llm.py. - Adds
api/mcp/code_prompts.pyas a re-export “prompt seam” for future MCP-specific prompt divergence. - Adds the MCP
asktool and associated tests, and registers it via the MCP tools package.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
api/llm.py |
Renames _define_ontology → define_ontology and updates global ontology initialization. |
api/mcp/graphrag_init.py |
Adds cached KnowledgeGraph factory for MCP GraphRAG usage (per project/branch). |
api/mcp/code_prompts.py |
Adds MCP prompt re-export module to create a seam for future prompt changes. |
api/mcp/tools/ask.py |
Implements the MCP ask tool (async wrapper + response normalization + error payload). |
api/mcp/tools/__init__.py |
Registers the new ask tool on import alongside structural tools. |
tests/mcp/test_graphrag_init.py |
Adds tests for cache behavior, graph naming, ontology reuse, and public ontology API. |
tests/mcp/test_code_prompts.py |
Adds re-export tests and intended “snapshot” test for prompt stability. |
tests/mcp/test_ask.py |
Adds tests for tool registration, payload normalization, error shaping, and JSON-serializability. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Snapshot at the time of T10 landing. Update when the underlying | ||
| # prompts intentionally change. | ||
| expected = { | ||
| "CYPHER_GEN_SYSTEM": _digest(code_prompts.CYPHER_GEN_SYSTEM), | ||
| "CYPHER_GEN_PROMPT": _digest(code_prompts.CYPHER_GEN_PROMPT), | ||
| "GRAPH_QA_SYSTEM": _digest(code_prompts.GRAPH_QA_SYSTEM), | ||
| "GRAPH_QA_PROMPT": _digest(code_prompts.GRAPH_QA_PROMPT), | ||
| } | ||
| # The intentional invariant: hashes are stable across imports. | ||
| again = { | ||
| "CYPHER_GEN_SYSTEM": _digest(code_prompts.CYPHER_GEN_SYSTEM), | ||
| "CYPHER_GEN_PROMPT": _digest(code_prompts.CYPHER_GEN_PROMPT), | ||
| "GRAPH_QA_SYSTEM": _digest(code_prompts.GRAPH_QA_SYSTEM), | ||
| "GRAPH_QA_PROMPT": _digest(code_prompts.GRAPH_QA_PROMPT), | ||
| } | ||
| assert expected == again |
| def test_get_or_create_kg_reuses_handcoded_ontology(): | ||
| """Critical: do NOT replace the hand-coded ontology with auto-extracted | ||
| one. T9 acceptance criterion.""" | ||
| from api.llm import define_ontology | ||
| from api.mcp import graphrag_init | ||
|
|
||
| with patch.object(graphrag_init, "LiteModel"), \ | ||
| patch.object(graphrag_init, "KnowledgeGraph") as mock_kg: | ||
| mock_kg.return_value = object() | ||
| graphrag_init.get_or_create_kg("p", "_default") | ||
|
|
||
| kwargs = mock_kg.call_args.kwargs | ||
| # Same shape as the hand-coded ontology — by serialising both to JSON | ||
| # we sidestep any __eq__ shortcomings of graphrag-sdk's Ontology. | ||
| expected = define_ontology() | ||
| assert type(kwargs["ontology"]) is type(expected) | ||
|
|
…bors Nav baseline rebased onto the #681 (MCP tools) + #698 (analyzer IMPORTS/OVERRIDES edges) confluence, dropping the duplicate analyzer-edges copy (722c8a3, byte-identical to #698's ce4ecd9) and the T13/T14 onboarding-template edits (deferred). Adds get_neighbors (symbol-id neighbor expansion over CALLS/IMPORTS/EXTENDS/OVERRIDES), get_file_neighbors (file-level structural coupling), get_importers/get_overrides spikes, and a hybrid (BM25 + structural) ranking for search_code. Cherry-pick of 799b218. PR-split note: get_file_neighbors + _resolve_file + FILE_NEIGHBOR_RELS form a self-contained trailing block (PR N2); everything else is PR N1. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Benchmarking showed the MCP ask tool failing on every call (the spawned server env carries only FalkorDB coordinates, no LLM key) and, when keyed, returning File-level fuzzy matches rather than the structural answers the nav workflow needs — an LLM round-trip per call for no signal. Expose only the deterministic structural tools; GraphRAG ask stays on the HTTP /api/chat path. #681's ask implementation is left intact (re-enable by re-adding the import). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Closing: the GraphRAG GraphRAG ask remains available on the HTTP The MCP review stack has been re-routed to drop this PR from the lineage: T9/T10/T11 work is preserved in branch history if we ever want to revisit MCP-side GraphRAG. |
…bors Nav baseline rebased onto the #681 (MCP tools) + #698 (analyzer IMPORTS/OVERRIDES edges) confluence, dropping the duplicate analyzer-edges copy (722c8a3, byte-identical to #698's ce4ecd9) and the T13/T14 onboarding-template edits (deferred). Adds get_neighbors (symbol-id neighbor expansion over CALLS/IMPORTS/EXTENDS/OVERRIDES), get_file_neighbors (file-level structural coupling), get_importers/get_overrides spikes, and a hybrid (BM25 + structural) ranking for search_code. Cherry-pick of 799b218. PR-split note: get_file_neighbors + _resolve_file + FILE_NEIGHBOR_RELS form a self-contained trailing block (PR N2); everything else is PR N1. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Benchmarking showed the MCP ask tool failing on every call (the spawned server env carries only FalkorDB coordinates, no LLM key) and, when keyed, returning File-level fuzzy matches rather than the structural answers the nav workflow needs — an LLM round-trip per call for no signal. Expose only the deterministic structural tools; GraphRAG ask stays on the HTTP /api/chat path. #681's ask implementation is left intact (re-enable by re-adding the import). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…bors Nav baseline rebased onto the #681 (MCP tools) + #698 (analyzer IMPORTS/OVERRIDES edges) confluence, dropping the duplicate analyzer-edges copy (722c8a3, byte-identical to #698's ce4ecd9) and the T13/T14 onboarding-template edits (deferred). Adds get_neighbors (symbol-id neighbor expansion over CALLS/IMPORTS/EXTENDS/OVERRIDES), get_file_neighbors (file-level structural coupling), get_importers/get_overrides spikes, and a hybrid (BM25 + structural) ranking for search_code. Cherry-pick of 799b218. PR-split note: get_file_neighbors + _resolve_file + FILE_NEIGHBOR_RELS form a self-contained trailing block (PR N2); everything else is PR N1. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Benchmarking showed the MCP ask tool failing on every call (the spawned server env carries only FalkorDB coordinates, no LLM key) and, when keyed, returning File-level fuzzy matches rather than the structural answers the nav workflow needs — an LLM round-trip per call for no signal. Expose only the deterministic structural tools; GraphRAG ask stays on the HTTP /api/chat path. #681's ask implementation is left intact (re-enable by re-adding the import). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Benchmarking showed the MCP ask tool failing on every call (the spawned server env carries only FalkorDB coordinates, no LLM key) and, when keyed, returning File-level fuzzy matches rather than the structural answers the nav workflow needs — an LLM round-trip per call for no signal. Expose only the deterministic structural tools; GraphRAG ask stays on the HTTP /api/chat path. #681's ask implementation is left intact (re-enable by re-adding the import). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Prerequisites (merge order)
Merge in order — this PR is stacked on:
index_repo(T4)impact_analysis(T6)Base: #680.
Bundles T9 (#657) + T10 (#658) + T11 (#659) — the strategic differentiator vs structural-only MCP servers. Three tightly coupled tickets land together because T11 needs T9 + T10 to do anything.
Stacked on:
What ships
api/mcp/graphrag_init.pyget_or_create_kg(project, branch)— process-wide cache; reuses the hand-coded ontology fromapi/llm.py; graph name follows T17 conventioncode:{project}:{branch}.api/mcp/code_prompts.pyapi/prompts.py; the value is having a place for MCP-flavoured divergence without touching the FastAPI chat prompts.api/mcp/tools/ask.pyask(question, project, branch=None)→{answer, cypher_query, context_nodes}.cypher_queryis the design-doc transparency requirement so agents can verify the executed Cypher.T9 also renames
_define_ontology→define_ontologyinapi/llm.pyso the public-and-importable form is the only one. No other call sites in the repo.Tests
14 new tests, all pass with mocked
LiteModel(no network in CI):tests/mcp/test_code_prompts.py(3) — re-exports match originals,__all__shape, snapshot hash stability.tests/mcp/test_graphrag_init.py(5) — per-branch graph name, cache identity, distinct keys → distinct instances, ontology reuse,define_ontologyis public.tests/mcp/test_ask.py(6) — tool registered, normalised payload, alternate response shapes, plain-string responses, errors as payload, JSON serialisable.Full MCP suite: 48 passed in 27.5s.
Out of scope (per tickets)
Closes #657, #658, #659.