Skip to content

Commit 5da78b5

Browse files
DvirDukhanCopilot
andcommitted
docs(mcp): reconcile agent guidance + smoke to the real tool surface
The MCP surface is the deterministic structural set — the GraphRAG `ask` tool was dropped (it remains available on the HTTP /api/chat path), and get_callers/get_callees/get_dependencies were consolidated into a single `get_neighbors(relation, direction)` with `get_file_neighbors` and `find_symbol` added. Update AGENTS.md, README.md, and the init-agent templates (claude_mcp_section.md, cursorrules.template) to the seven registered tools: index_repo, search_code, find_symbol, get_neighbors, get_file_neighbors, impact_analysis, find_path. Fix scripts/mcp_smoke.py so its expected tool set and the tools it exercises match (search_code now takes `query`, callers come from get_neighbors direction=IN). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 4464983 commit 5da78b5

5 files changed

Lines changed: 41 additions & 42 deletions

File tree

AGENTS.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,9 @@ cgraph info [--repo <name>] # Repo stats + metadata
157157

158158
## MCP server (for agents)
159159

160-
`cgraph-mcp` exposes the code graph over MCP stdio. Eight tools:
161-
`index_repo`, `search_code`, `get_callers`, `get_callees`,
162-
`get_dependencies`, `impact_analysis`, `find_path`, `ask`.
160+
`cgraph-mcp` exposes the code graph over MCP stdio. Seven tools:
161+
`index_repo`, `search_code`, `find_symbol`, `get_neighbors`,
162+
`get_file_neighbors`, `impact_analysis`, `find_path`.
163163

164164
Drop the canonical agent guidance into any repo:
165165

@@ -169,8 +169,9 @@ cgraph init-agent --force # overwrite existing files
169169
```
170170

171171
See `api/mcp/templates/claude_mcp_section.md` for the full tool table
172-
and rules of thumb (start with `search_code`; prefer structural tools
173-
over `ask`; run `impact_analysis` before refactoring).
172+
and rules of thumb (start with `search_code`/`find_symbol`; use
173+
`get_neighbors` for who/what-calls; run `impact_analysis` before
174+
refactoring).
174175

175176
Environment:
176177

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,9 @@ Then ask Claude things like *"what functions call analyze_sources?"* or *"find t
226226

227227
For agents that speak the [Model Context Protocol](https://modelcontextprotocol.io)
228228
(Claude Code, Cursor, Cline, …), code-graph ships a stdio MCP server
229-
that exposes the knowledge graph as 8 first-class tools: `index_repo`,
230-
`search_code`, `get_callers`, `get_callees`, `get_dependencies`,
231-
`impact_analysis`, `find_path`, and `ask` (NL→Cypher via GraphRAG).
229+
that exposes the knowledge graph as 7 first-class tools: `index_repo`,
230+
`search_code`, `find_symbol`, `get_neighbors`, `get_file_neighbors`,
231+
`impact_analysis`, and `find_path`.
232232

233233
Quickstart — Claude Code:
234234

api/mcp/templates/claude_mcp_section.md

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,26 @@ need to understand how symbols connect.
99
| Tool | Call this when… | Example |
1010
|---|---|---|
1111
| `index_repo(path_or_url, branch?)` | **First** thing in a new repo; or after large changes outside your edits. Project name is **derived from the folder or repo URL** — read it back from the response. | `index_repo(path_or_url=".")` |
12-
| `search_code(prefix, project)` | You know part of a symbol name and need its id. | `search_code(prefix="processPay", project="myrepo")` |
13-
| `get_callers(symbol_id, project)` | "Who calls this?" — refactoring a function, tracking down a regression. | `get_callers(symbol_id=42, project="myrepo")` |
14-
| `get_callees(symbol_id, project)` | "What does this call?" — understanding a function before editing it. | `get_callees(symbol_id=42, project="myrepo")` |
15-
| `get_dependencies(symbol_id, project)` | All edges out of a symbol (CALLS + IMPORTS + DEFINES). | `get_dependencies(symbol_id=42, project="myrepo")` |
12+
| `search_code(query, project)` | You know part of a symbol name and need its id (hybrid prefix + ranked match). | `search_code(query="processPay", project="myrepo")` |
13+
| `find_symbol(name, project, file?)` | You know the exact symbol name (optionally in a given file) and want its id directly. | `find_symbol(name="processPayment", project="myrepo")` |
14+
| `get_neighbors(symbol_id, project, relation?, direction?)` | "Who calls this?" (`direction="IN"`), "What does this call?" (`direction="OUT"`), or other edges via `relation` (CALLS/IMPORTS/DEFINES). Replaces the old get_callers/get_callees/get_dependencies. | `get_neighbors(symbol_id=42, project="myrepo", direction="IN")` |
15+
| `get_file_neighbors(file, project)` | Symbols a file defines / depends on — "what's in this file and what does it touch?" | `get_file_neighbors(file="api/graph.py", project="myrepo")` |
1616
| `impact_analysis(symbol_id, project, direction, depth)` | **"What breaks if I change this?"** Transitive upstream callers. | `impact_analysis(symbol_id=42, project="myrepo", direction="IN", depth=3)` |
1717
| `find_path(source_id, dest_id, project)` | Show the call chain between two known symbols. | `find_path(source_id=10, dest_id=42, project="myrepo")` |
18-
| `ask(question, project)` | Open-ended natural-language question. **More expensive — use last.** | `ask(question="why does login fail when MFA is on?", project="myrepo")` |
1918

2019
## Rules of thumb
2120

22-
1. **Start with `search_code`** to turn names into ids. Most tools take a `symbol_id`.
23-
2. **Prefer structural tools over `ask`.** `get_callers` is one cheap Cypher
24-
hop; `ask` is two LLM round-trips. Use `ask` for fuzzy/conceptual
25-
questions, not for "who calls X".
21+
1. **Start with `search_code` or `find_symbol`** to turn names into ids. Most tools take a `symbol_id`.
22+
2. **Use `get_neighbors` with `direction`** for who-calls / what-calls: `IN` = callers, `OUT` = callees. Pass `relation` for IMPORTS/DEFINES edges.
2623
3. **`impact_analysis` before refactoring.** Even when you think you know
2724
the answer — the transitive closure often surprises you.
2825
4. **`branch` is optional** but pass it when working on a feature branch
2926
so you query the right per-branch index.
3027
5. **Response shape.** Tools that return collections (`search_code`,
31-
`get_callers`, `get_callees`, `get_dependencies`, `find_path`,
28+
`find_symbol`, `get_neighbors`, `get_file_neighbors`, `find_path`,
3229
`impact_analysis`) put the array in `structuredContent.result` per
3330
the MCP spec. The text content is the same JSON for convenience.
34-
`index_repo` and `ask` return a single object.
31+
`index_repo` returns a single object.
3532

3633
## Environment
3734

api/mcp/templates/cursorrules.template

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,22 @@ understand how symbols connect.
66

77
## Tool selection
88

9-
- Symbol lookup by name: use `code-graph.search_code` (gives you the
10-
numeric id every other tool needs).
11-
- "Who calls X?": `code-graph.get_callers`.
12-
- "What does X call?": `code-graph.get_callees`.
13-
- All outgoing edges (CALLS + IMPORTS + DEFINES): `code-graph.get_dependencies`.
9+
- Symbol lookup by name: use `code-graph.search_code` (hybrid prefix +
10+
ranked match) or `code-graph.find_symbol` for an exact name — both give
11+
you the numeric id every other tool needs.
12+
- "Who calls X?": `code-graph.get_neighbors` with `direction="IN"`.
13+
- "What does X call?": `code-graph.get_neighbors` with `direction="OUT"`.
14+
- Other edges (IMPORTS / DEFINES): `code-graph.get_neighbors` with the
15+
matching `relation`.
16+
- What a file defines/touches: `code-graph.get_file_neighbors`.
1417
- Refactoring impact ("what breaks if I change X"):
1518
`code-graph.impact_analysis` with `direction="IN"`.
1619
- Call chain between two specific symbols: `code-graph.find_path`.
17-
- Natural-language question over the graph (expensive — last resort):
18-
`code-graph.ask`.
1920

2021
## Rules
2122

22-
- Always `search_code` first to resolve names to ids.
23-
- Prefer structural tools (`get_callers`, `find_path`, `impact_analysis`)
24-
over `ask` for "who/what/where" questions.
23+
- Always `search_code`/`find_symbol` first to resolve names to ids.
24+
- Use `get_neighbors(direction=...)` for "who/what calls" questions.
2525
- Run `impact_analysis(direction="IN", depth=3)` before any non-trivial
2626
refactor.
2727
- Pass `branch` when on a feature branch.

scripts/mcp_smoke.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
Spawns `cgraph-mcp` over stdio, lists tools, indexes the
44
code-graph repo itself, and exercises `search_code`,
5-
`get_callers`, and `impact_analysis`. Prints a compact pass/fail line per
5+
`get_neighbors`, and `impact_analysis`. Prints a compact pass/fail line per
66
tool.
77
"""
88

@@ -61,12 +61,11 @@ async def main() -> int:
6161
expected = {
6262
"index_repo",
6363
"search_code",
64-
"get_callers",
65-
"get_callees",
66-
"get_dependencies",
64+
"get_neighbors",
65+
"get_file_neighbors",
66+
"find_symbol",
6767
"impact_analysis",
6868
"find_path",
69-
"ask",
7069
}
7170
missing = expected - set(tool_names)
7271
if missing:
@@ -92,10 +91,10 @@ async def main() -> int:
9291
branch_name = idx_payload["branch"]
9392
print(f"[index_repo] graph={idx_payload['graph_name']} project={project_name}")
9493

95-
print("[search_code] prefix='index_repo'")
94+
print("[search_code] query='index_repo'")
9695
sr = await session.call_tool(
9796
"search_code",
98-
{"prefix": "index_repo", "project": project_name, "branch": branch_name},
97+
{"query": "index_repo", "project": project_name, "branch": branch_name},
9998
)
10099
sr_payload = _pretty(sr)
101100
print(f"[search_code] -> {json.dumps(sr_payload)[:300]}")
@@ -116,20 +115,22 @@ async def main() -> int:
116115
print(f"[search_code] picked id={first_id} name={hits[0].get('name')}")
117116

118117
if first_id is not None:
119-
print(f"[get_callers] id={first_id}")
118+
print(f"[get_neighbors] id={first_id} direction=IN (callers)")
120119
gc = await session.call_tool(
121-
"get_callers",
120+
"get_neighbors",
122121
{
123122
"symbol_id": first_id,
124123
"project": project_name,
124+
"relation": "CALLS",
125+
"direction": "IN",
125126
"branch": branch_name,
126127
},
127128
)
128129
gc_payload = _pretty(gc)
129130
# Some MCP servers return list payloads in structuredContent only.
130131
gc_struct = getattr(gc, "structuredContent", None)
131-
print(f"[get_callers] -> {json.dumps(gc_payload)[:300]} struct={json.dumps(gc_struct)[:200]}")
132-
# Acceptable shapes: list of caller dicts, or {"callers": [...]}.
132+
print(f"[get_neighbors] -> {json.dumps(gc_payload)[:300]} struct={json.dumps(gc_struct)[:200]}")
133+
# Acceptable shapes: list of neighbor dicts, or {"result": [...]}.
133134
callers = None
134135
if isinstance(gc_payload, list):
135136
callers = gc_payload
@@ -138,10 +139,10 @@ async def main() -> int:
138139
elif isinstance(gc_struct, dict) and "result" in gc_struct:
139140
callers = gc_struct["result"]
140141
if callers is None:
141-
print("[FAIL] get_callers returned no recognizable payload")
142+
print("[FAIL] get_neighbors returned no recognizable payload")
142143
fails += 1
143144
else:
144-
print(f"[get_callers] {len(callers)} callers")
145+
print(f"[get_neighbors] {len(callers)} callers")
145146

146147
print("[impact_analysis] depth=2")
147148
ia = await session.call_tool(

0 commit comments

Comments
 (0)