Skip to main content
Kodelyth ECC
pillar

Codebase AST Knowledge Graphs — The 2026 Reference

Complete reference for codebase AST knowledge graphs: how tree-sitter builds them across 158 languages, why structural queries beat grep by 99% on token cost, the three queries that cover most practical use, Hybrid LSP semantic types, honest limits (staleness, type-resolution coverage), and when grep is still right.

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:

  1. 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.
  2. 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).
  3. 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 processPayment the function from processPayment the 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 users table?" → join HANDLES_HTTP edges to SQL_READS edges through the handler's call tree.
  • "What's the deepest call chain in the codebase?" → bounded path traversal.
  • "Which classes implement PaymentProcessor and override charge?" → IMPLEMENTS + OVERRIDES edge 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:

QuestionWhy 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 pointsFunction 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:

QuestionWhy it's a grep question
Find every TODO: commentNot a symbol — a string
Find a literal config keyJust text
Find a log message's exact wordingJust text
Find a typo'd variable nameString occurrence
One-off string countLow-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-graph

The --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 project

Your AI tool calls index_repository and builds the graph. Speed, for reference:

RepoScaleFull 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_changes or re-index. If you don't, trace_path answers 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, and query_graph directly as MCP tools; no codebase subcommand 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-explorer and impact-analyzer agents 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

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.

Last updated: 2026-11-11T00:00:00.000Z · v2.30.0