By Kodelyth · updated · v2.30.0
Codebase AST Knowledge Graphs
If you've ever waited 30 seconds for Claude Code to answer "who calls this function" because it opened eleven files to figure it out, you've paid the grep tax. An AST knowledge graph answers the same question in one query, at ~1% the token cost, in sub-millisecond time regardless of repo size. This is the reference for how they work, what they cost, and when they are the right answer.
What an AST knowledge graph actually is
Three layers from source code to query:
- Parse — a language-aware parser reads your source and produces an
Abstract Syntax Tree (AST): a tree of nodes like
function_definition,function_call,class,import,type_annotation. tree-sitter is the dominant universal parser — one parser framework, one grammar per language, 158+ grammars in the official registry. - Extract symbols + edges — a traversal walks the AST and emits
structured facts:
function(name, file, line),calls(caller, callee, line),imports(module, from),inherits(child, parent),http_call(method, path, handler). - Store as a graph — the symbols become nodes, the facts become
edges. Now
trace_path(function='main', direction='outbound')is a graph walk, not a grep.
The graph is an index. Like any index, it is fast to query and slow to build. Build cost is amortised over many queries.
Why it beats grep by two orders of magnitude on structural questions
Grep answers lexically. It finds the string processPayment wherever it
appears — a call, a definition, a doc comment, a string literal, a dead code
block. To distinguish them, the AI has to read the surrounding file. On a
real repository, that pattern costs roughly 412,000 tokens to answer one
question whose answer is a short list of names.
The graph returns the answer directly: "nodes with an incoming CALLS edge
into processPayment are: OrderController.checkout:42,
WebhookHandler.replay:117, tests/billing.test.js:201." Roughly 3,400
tokens. 99% reduction. And the saving is not just money:
- No context-window pollution — grep's 412K tokens bloat your context so you can't do anything else in the same turn.
- Flat latency — the graph query is sub-millisecond on repos of any size. grep scales with repo size; graph scales with the answer.
- Accurate, not approximate — grep can't tell
processPaymentthe function fromprocessPaymentthe string literal or the comment. The graph can.
The numbers above are from codebase-memory-mcp's own comparison on a Django-scale repo and match what we see in production.
The three queries that cover most of it
Your AI tool translates natural language into these MCP calls. All run locally, at zero LLM cost, and return structured JSON.
search_graph — find nodes by name or pattern
kodelythecc codebase query search_graph \
'{"name_pattern": ".*Handler.*"}'"Every function or class with Handler in the name." Fast enumeration — good
for "show me every X we have."
trace_path — follow CALLS edges in a direction
# Outbound from an entry point — "what does main reach?"
kodelythecc codebase query trace_path \
'{"function_name": "main", "direction": "outbound"}'
# Inbound to a function — "who calls processPayment?"
# (The blast radius of changing it.)
kodelythecc codebase query trace_path \
'{"function_name": "processPayment", "direction": "inbound"}'Inbound is impact analysis. If you're about to rename, delete, or change
the contract of processPayment, the inbound trace tells you exactly which
call sites you need to update. No grep, no false positives.
query_graph — Cypher for everything else
# "Every function with zero inbound CALLS edges" — i.e. dead code candidates.
kodelythecc codebase query query_graph \
"MATCH (f:Function) WHERE NOT ()-[:CALLS]->(f) RETURN f.name, f.file LIMIT 50"Cypher (the Neo4j query language, adopted by many graph databases) covers the arbitrary patterns the first two queries don't. Examples from real use:
- "Which HTTP routes touch the
userstable?" → joinHANDLES_HTTPedges toSQL_READSedges through the handler's call tree. - "What's the deepest call chain in the codebase?" → bounded path traversal.
- "Which classes implement
PaymentProcessorand overridecharge?" →IMPLEMENTS+OVERRIDESedge joins.
You rarely write Cypher by hand — the AI dispatches it. Knowing the shape tells you why the answer was cheap.
Hybrid LSP semantic types
tree-sitter alone gives you syntactic structure — "there's a function
call here, with arguments x and y." For many questions that's enough. For
others — "what type is user.email in this handler?", "does this generic T
bind to a Row or a Document?" — you need semantic type resolution,
which means running a type checker (an LSP server, typically).
codebase-memory-mcp calls this Hybrid LSP: tree-sitter for structure, LSP for types, merged into one graph. Semantic types are indexed for:
- Python
- TypeScript / JavaScript / JSX / TSX
- Go
- Rust
- Java
- Kotlin
- C
- C++
- C#
- PHP
For the other 148+ languages, you get the AST structure (names, calls, imports) without the deep type resolution. That's still a huge win over grep for the structural questions, but if your bug hinges on type inference and you're in a language outside the Hybrid LSP list, don't ship a wrong answer just because the graph said so.
When the graph wins — and when grep is still right
Use the graph when the question is about edges between symbols:
| Question | Why it's a graph question |
|---|---|
Who calls AuthMiddleware? | Inbound CALLS edge |
| What's the impact of changing this type? | Multi-hop USES_TYPE edges |
| Show the architecture / entry points | Function nodes with no inbound CALLS |
| Find dead code (functions with zero callers) | MATCH NOT ()-[:CALLS]->(f) |
Which HTTP routes touch the users table? | Join HANDLES_HTTP + SQL_READS |
Use grep when the question is lexical:
| Question | Why it's a grep question |
|---|---|
Find every TODO: comment | Not a symbol — a string |
| Find a literal config key | Just text |
| Find a log message's exact wording | Just text |
| Find a typo'd variable name | String occurrence |
| One-off string count | Low-cardinality lexical |
The discriminator in one sentence: is the question about edges between symbols, or about a string? grep is excellent at the second. The moment the question becomes "who / what / how do these connect," grep has to reconstruct structure the graph already holds.
Installation and first index
npm i -g kodelyth-ecc
kodelythecc --target claude-code --codebase-graphThe --codebase-graph flag installs the
codebase-memory-mcp
binary and auto-registers it as an MCP server in your AI tool's config.
Then open a project and say:
Index this projectYour AI tool calls index_repository and builds the graph. Speed, for
reference:
| Repo | Scale | Full index |
|---|---|---|
| Typical Django app | ~50k LOC, 500 files | ~6 seconds |
| Typical React/Next app | ~80k LOC, 1,200 files | ~10 seconds |
| Rails monolith | ~200k LOC, 3,000 files | ~30 seconds |
| Linux kernel | ~28M LOC, 75k files | ~3 minutes |
All in memory with the working set released afterwards. Incremental updates
(detect_changes) are sub-second — you're not re-indexing from scratch
after every edit.
Honest limits
- It's an index — it can go stale. After a large refactor, run
detect_changesor re-index. If you don't,trace_pathanswers confidently about code that moved. - Hybrid LSP semantic types cover 11 languages. Other grammars get AST structure only. For a type-inference-dependent bug in (say) Haskell or Scala, verify the graph's answer against the compiler.
- It's a separate binary on PATH. Install one more thing, keep one more thing current. Local only; your code never leaves the machine.
- Edge types are the ones codebase-memory-mcp emits. Current list:
CALLS,IMPORTS,IMPLEMENTS,INHERITS,USES_TYPE,HANDLES_HTTP,SQL_READS,SQL_WRITES. If you need an edge type they don't emit (say, your own custom DSL's control flow), you're writing a parser plugin.
None of that changes the core trade: for structural questions, one graph query beats reading a repository into the context window, by about two orders of magnitude.
How Kodelyth ECC uses it
- Installed on
--codebase-graph— the binary and MCP registration land in your AI tool in one step. - Exposed via MCP — your AI tool calls
search_graph,trace_path, andquery_graphdirectly as MCP tools; nocodebasesubcommand needed from your side in normal use. - Explicit pass-through —
kodelythecc codebase query <tool>exposes the same queries at the CLI for scripts, CI jobs, and debugging. - Routed from intent — the
code-explorerandimpact-analyzeragents dispatch graph queries automatically when the user asks "who calls X" or "what breaks if I change Y."
Full subcommand reference: /docs/codebase-graph.
Related reading
- /docs/codebase-graph — ECC's integration subcommand reference.
- /blog/codebase-graph-structural-queries — "Who calls processPayment?" in grep vs graph, with the 99% number derived from first principles.
- /docs/best-mcp-servers-2026 — codebase-memory-mcp in the 10-way MCP roundup.
- /docs/reduce-claude-code-token-cost — the AST graph is Lever 4 of 5 in the token-cost pillar.
One-command start
npm i -g kodelyth-ecc
kodelythecc --target claude-code --codebase-graph
# Then in your AI tool:
# "Index this project"
# "Who calls processPayment?"First query usually surprises people — 3,400 tokens for an answer that used to cost 412,000.