Skip to content

Commit a005415

Browse files
harshithsunkuclaude
andcommitted
Drop call_hierarchy, halve the tool-schema context cost (v1.3.0)
Second-pass surface optimization after v1.2.0, same evidence base (130 agent A/B transcripts): - call_hierarchy removed (11 tools now): zero calls even on the multihop eval questions — agents iterate find_callers, and reachability covers the directed case. It was also the most expensive tool at runtime. - Docstrings tightened: shared conventions (project_root resolution, limit/offset pagination, JSON/text format, envelope shape) now live once in the server-level instructions string instead of being repeated in every tool description. Per-session tool payload drops from ~4,700 to ~2,900 tokens. No parameter or schema changes. 244 tests pass; golden kernel eval unchanged (def-mutex_lock known-fail only, identical on HEAD). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent f84450f commit a005415

7 files changed

Lines changed: 100 additions & 369 deletions

File tree

README.md

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -201,26 +201,12 @@ Precedence: tool-call argument > CLI flag > environment variable > project confi
201201
|---|---|
202202
| `symbol_info` | **A one-shot overview card** — definitions (with kind, signature, scope, and `#ifdef` guard), reference count, hottest files, `EXPORT_SYMBOL*` status, and which tool to use next. Multiply-defined symbols are explained as "N definitions under M distinct guards"; macro-generated ones resolve with a `resolved_via` flag. The best first query for any unfamiliar symbol. |
203203
| `get_symbol_body` | **Just the source of a definition.** The 271-line `tcp_v4_rcv` function — not the 3,500-line file it lives in. Handles functions, structs, and multi-line macros. |
204-
| `find_callers` | **The call graph, deduplicated.** Every reference mapped to its enclosing function with call counts: 245 raw lines for `ext4_mark_inode_dirty` collapse to 62 callers. |
205-
| `call_hierarchy` | **Multi-level impact analysis.** Who calls X, who calls *those*, up to 5 levels — a cycle-safe, capped tree instead of N rounds of grep. |
204+
| `find_callers` | **The call graph, deduplicated.** Every reference mapped to its enclosing function with call counts: 245 raw lines for `ext4_mark_inode_dirty` collapse to 62 callers. Iterate it to walk the caller graph as deep as you need. |
206205
| `find_callees` | **The outgoing call graph.** What does this function call? Body-extracted call sites, each verified against the index, split into in-tree (with locations) and external. |
207206
| `reachability` | **"Can this function end up in that one?"** — BFS over the caller graph returns the *shortest* call chain from A to B with the file:line of every call site (`ksys_read → vfs_read → rw_verify_area`), or an honest "no static path" that names the function-pointer caveat. One call instead of a dozen find_callers rounds. |
208207
| `blast_radius` | **What does my diff impact?** Takes `git diff <ref>`, maps changed lines to their enclosing functions via the index, then walks callers outward — results ranked by distance (changed functions first, direct callers next). The pre-merge "what else must I re-check" answer, tied to real git state. |
209208
| `summarize_references` | **A ranked per-file count.** The cheap first move for hot symbols — `kmalloc`'s 2,744 references become one screen of "where usage concentrates". |
210209

211-
A two-level `call_hierarchy` on the kernel's `ext4_mark_inode_dirty` — 87 compact lines instead of dozens of grep rounds:
212-
213-
```text
214-
ext4_mark_inode_dirty (definition: fs/ext4/ext4_jbd2.h:138)
215-
├─ ext4_rename fs/ext4/namei.c (6 sites)
216-
│ └─ ext4_rename2 fs/ext4/namei.c (1 site)
217-
├─ swap_inode_boot_loader fs/ext4/ioctl.c (5 sites)
218-
│ └─ __ext4_ioctl fs/ext4/ioctl.c (1 site)
219-
├─ ext4_mkdir fs/ext4/namei.c (3 sites)
220-
│ └─ ext4_rename2 fs/ext4/namei.c (1 site)
221-
...
222-
```
223-
224210
### Core lookups
225211

226212
| Tool | What it does | Underlying command |
@@ -258,7 +244,7 @@ Since v0.8.0 every tool returns a **machine-readable JSON envelope by default**
258244
- **`resolved_via` says *how* a symbol was found** when it took macro-family resolution rather than a literal index match (`"macro:SYSCALL_DEFINE"`, `"fuzzy:vfs_read"`) — see [Macro-generated symbols](#macro-generated-symbols-resolve-too-sys_read--syscall_define3).
259245
- `next_tools` tells the agent the highest-value follow-up call for what was (or wasn't) found.
260246
- `total`/`offset`/`truncated` replace the text continuation footer; errors keep the envelope with an `error` field.
261-
- Composite tools return tool-shaped `results` (e.g. `call_hierarchy` a nested caller tree, `find_callees` `{in_tree, external}`, `symbol_info` an overview object, `reachability` a hop chain) inside the same envelope.
247+
- Composite tools return tool-shaped `results` (e.g. `find_callees` `{in_tree, external}`, `symbol_info` an overview object, `reachability` a hop chain) inside the same envelope.
262248

263249
### `#ifdef`-aware: know which definition your config actually compiles
264250

@@ -336,7 +322,7 @@ Incremental refreshes recollect the list, so newly ignored files drop out of the
336322

337323
```text
338324
1. symbol_info("kmalloc") → definitions + usage spread + next step (12 lines)
339-
2. call_hierarchy("ext4_mark_inode_dirty") → multi-level impact tree (1 line/caller)
325+
2. find_callers("ext4_mark_inode_dirty") → deduped callers with counts (1 line/caller)
340326
3. get_symbol_body("tcp_v4_rcv") → read the ONE function that matters
341327
4. find_callees("tcp_v4_rcv") → what it depends on, with locations
342328
5. reachability("ksys_read", "rw_verify_area") → the call chain, one line per hop
@@ -360,7 +346,7 @@ sudo dnf install ctags python3-pygments # Fedora
360346
brew install ctags && pip install pygments # macOS
361347
```
362348

363-
Now `find_definition("py_util")`, `get_symbol_body` (indentation-aware for Python), `find_callees`, `call_hierarchy` — all work across every language in the tree, in one index.
349+
Now `find_definition("py_util")`, `get_symbol_body` (indentation-aware for Python), `find_callees`, `find_callers` — all work across every language in the tree, in one index.
364350

365351
Force a specific parser label with `--label`, `GTAGS_MCP_LABEL`, or `label` in `.gtags-mcp.toml` (e.g. `default` for native-only, `pygments` for plugin-everything).
366352

@@ -380,7 +366,7 @@ flowchart LR
380366
end
381367
382368
subgraph proc["mcp-gtags-server — spawned once per window, lives for the session"]
383-
T["12 navigation tools"]
369+
T["11 navigation tools"]
384370
RR["root resolution<br/>project_root → env → config → client roots → cwd"]
385371
end
386372

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "mcp-gtags-server"
3-
version = "1.2.0"
3+
version = "1.3.0"
44
description = "Indexed code navigation for AI coding agents — replace grep scans with GNU Global (gtags) lookups over MCP. ~100x faster and radically less noise on million-line C/C++ codebases."
55
readme = "README.md"
66
requires-python = ">=3.10"

server.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
"url": "https://github.com/harshithsunku/mcp-gtags-server",
77
"source": "github"
88
},
9-
"version": "1.2.0",
9+
"version": "1.3.0",
1010
"websiteUrl": "https://github.com/harshithsunku/mcp-gtags-server",
1111
"packages": [
1212
{
1313
"registryType": "pypi",
1414
"registryBaseUrl": "https://pypi.org",
1515
"identifier": "mcp-gtags-server",
16-
"version": "1.2.0",
16+
"version": "1.3.0",
1717
"runtimeHint": "uvx",
1818
"transport": {
1919
"type": "stdio"

src/gtags_mcp/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""MCP server exposing GNU Global (gtags) code navigation for C/C++ codebases."""
22

3-
__version__ = "1.2.0"
3+
__version__ = "1.3.0"

src/gtags_mcp/output.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,14 @@
5555
["find_references"],
5656
),
5757
"find_callers": (
58-
["call_hierarchy", "get_symbol_body"],
58+
["get_symbol_body", "find_callees"],
5959
["find_references"],
6060
),
6161
"summarize_references": (
6262
["find_callers", "find_references"],
6363
[],
6464
),
65-
"call_hierarchy": (["get_symbol_body", "find_callees"], ["find_references"]),
66-
"find_callees": (["get_symbol_body", "call_hierarchy"], []),
65+
"find_callees": (["get_symbol_body", "find_callers"], []),
6766
"reachability": (
6867
["get_symbol_body", "find_callers"],
6968
["find_callers", "find_callees"],

0 commit comments

Comments
 (0)