You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Phase 1 ticket T11. Depends on #650 (T3 fixture), #658 (T10 prompts), and transitively #657 (T9 GraphRAG init).
Context
The ask tool is the strategic differentiator. None of the 5 competing code-graph MCP servers expose a natural-language query interface — they all top out at structural traversal. ask lets agents ask "what calls processPayment?" in English and get a grounded answer with the executed Cypher visible for transparency.
How it works (two LLM round-trips bracketing one Cypher query against FalkorDB):
The graph itself never goes to the LLM — only the schema and query results — which is why this works on huge codebases.
All the GraphRAG plumbing already exists in api/llm.py (_create_kg_agent at api/llm.py:238-258, _ask_sync at api/llm.py:260-268, ask at api/llm.py:271-273). T9 extracted the construction; T10 set up the prompt seam. This ticket is the thin async MCP wrapper.
Scope
In:
New api/mcp/tools/ask.py registering the ask MCP tool:
The cypher_query field is required in the response — the design doc explicitly calls this out as a transparency requirement so the agent can inspect, learn, and debug.
Tests in tests/mcp/test_ask.py:
Unit test with fully mocked KnowledgeGraph: assert response shape {answer, cypher_query, context_nodes}.
Integration test with mocked LiteModel: stub the model to return canned content for both LLM round-trips:
First call (cypher gen): returns a known Cypher targeting the T3 fixture (e.g. MATCH (n:Function {name:"service"})<-[:CALLS]-(c) RETURN c).
Real Cypher executes against the real fixture graph in FalkorDB, returning real nodes.
Second call (QA synthesis): returns a canned answer string.
Assert the response includes the executed cypher in cypher_query and the real nodes in context_nodes.
Protocol round-trip: tool registered and callable via stdio client.
Out:
Real-LLM E2E (Phase 1.5 nightly with API-key secrets).
Streaming responses.
Multi-turn conversation memory (each ask is independent).
Prompt iteration (Phase 1.5).
Files to create / modify
new api/mcp/tools/ask.py
modified api/mcp/server.py if needed (auto-discovery of new tools)
new tests/mcp/test_ask.py
Acceptance criteria
Tool registered with FastMCP and discoverable via session.list_tools().
Per-question caching (the KnowledgeGraph is cached per (project, branch) in T9; per-question caching is overkill).
Notes for the implementer
The async wrapper around the sync kg.ask() follows the same run_in_executor pattern as the existing api/llm.py:271-273.
Auto-detect project from CWD when not provided (similar to T4's branch auto-detect — just use Path.cwd().name).
Auto-detect branch from CWD when not provided (reuse the helper from T17).
The mocked-LLM integration test is the key innovation here: it gives us real coverage of the Cypher execution path without needing API credentials. Pattern: unittest.mock.patch("graphrag_sdk.models.litellm.LiteModel.ask", side_effect=[cypher_response, qa_response]).
Phase 1 ticket T11. Depends on #650 (T3 fixture), #658 (T10 prompts), and transitively #657 (T9 GraphRAG init).
Context
The
asktool is the strategic differentiator. None of the 5 competing code-graph MCP servers expose a natural-language query interface — they all top out at structural traversal.asklets agents ask "what calls processPayment?" in English and get a grounded answer with the executed Cypher visible for transparency.How it works (two LLM round-trips bracketing one Cypher query against FalkorDB):
The graph itself never goes to the LLM — only the schema and query results — which is why this works on huge codebases.
All the GraphRAG plumbing already exists in
api/llm.py(_create_kg_agentat api/llm.py:238-258,_ask_syncat api/llm.py:260-268,askat api/llm.py:271-273). T9 extracted the construction; T10 set up the prompt seam. This ticket is the thin async MCP wrapper.Scope
In:
api/mcp/tools/ask.pyregistering theaskMCP tool:cypher_queryfield is required in the response — the design doc explicitly calls this out as a transparency requirement so the agent can inspect, learn, and debug.tests/mcp/test_ask.py:KnowledgeGraph: assert response shape{answer, cypher_query, context_nodes}.LiteModel: stub the model to return canned content for both LLM round-trips:MATCH (n:Function {name:"service"})<-[:CALLS]-(c) RETURN c).cypher_queryand the real nodes incontext_nodes.Out:
askis independent).Files to create / modify
api/mcp/tools/ask.pyapi/mcp/server.pyif needed (auto-discovery of new tools)tests/mcp/test_ask.pyAcceptance criteria
session.list_tools().question: str, project: str | None = None, branch: str | None = None.{answer: str, cypher_query: str, context_nodes: list}.KnowledgeGraph.LiteModelso neither LLM call hits a real provider.response.context_nodescontains real nodes from the fixture (not from the mock).response.cypher_querymatches the mocked Cypher.Dependencies
Out of scope (do NOT do in this PR)
KnowledgeGraphis cached per(project, branch)in T9; per-question caching is overkill).Notes for the implementer
kg.ask()follows the samerun_in_executorpattern as the existingapi/llm.py:271-273.projectfrom CWD when not provided (similar to T4's branch auto-detect — just usePath.cwd().name).branchfrom CWD when not provided (reuse the helper from T17).unittest.mock.patch("graphrag_sdk.models.litellm.LiteModel.ask", side_effect=[cypher_response, qa_response]).