opencode CodeGraph: Pre-Indexed Code Intelligence for Faster Agent Work — PupPal
2026-08-09
opencode CodeGraph: Pre-Indexed Code Intelligence for Faster Agent Work — PupPal
Every coding agent has the same bottleneck. It is not the model's reasoning — it is the cost and latency of discovering code. A long-lived agent editing a large repository spends most of its context and most of its tool calls on the same primitive operations: grep for a symbol, read a file, grep for its callers, read another file, repeat. Each round trip is cheap in isolation, but they add up to a tax that dominates real sessions — and on API-metered models the tax is literal dollars. PupPal's team hit this wall across every agent harness it runs, from Claude Code to opencode to Hermes Agent itself, and the solution that finally stuck is a pre-indexed code knowledge graph served over the Model Context Protocol.
CodeGraph (58k stars) builds a local, queryable knowledge graph of your codebase with tree-sitter, stores it in SQLite with FTS5 full-text search, and exposes it to agents as an MCP server. Because the surface is MCP, the same index works for any agent that speaks the protocol: Claude Code, Cursor, Codex CLI, opencode, Gemini CLI, AntiGravity, Kiro, and Hermes Agent. This post is the practical integration guide — how to wire CodeGraph into opencode specifically, what the tools actually do, how to keep the index in sync with your edits, and why one well-aimed tool beats a menu of narrow ones.
Why Grep-and-Read Is the Hidden Tax on Every Agent Session
Before touching configuration, it is worth making the cost model
explicit, because it explains every design decision CodeGraph makes.
When an agent needs to answer "how does mutateElement reach
renderScene?", the naive path is:
grep "mutateElement"— a location list, no bodies.readthe definition file — now you have one function.grepfor callers of that function — locations again.readthe caller file — and so on, one hop at a time.
Each hop is a separate tool call: model tokens to form the call, a round-trip, and — worst of all — the results of every hop land in context and stay there. The session fills with partial file reads, location lists, and intermediate state. Measured agent behavior shows the majority of tool calls in a typical editing session are exactly this discovery loop, and a large share of the context budget is consumed by the fragments it produces. On a 200k-token context window with a large monorepo, the agent can exhaust its budget on discovery before it ever writes a line of code.
The fix is not a faster grep. The fix is to invert the architecture: index the codebase once, up front, into a structure that already contains definitions, callers, callees, call paths, and dependency relationships — then let the agent ask the index directly. One query returns what five read-grep-read cycles would have returned, in one tool call, with far fewer tokens. That is the entire premise of CodeGraph, and it is why the integration story starts with "index once, query forever."
Step 1: Install the CodeGraph CLI
CodeGraph ships as a self-contained binary — a bundled Node runtime, nothing to compile, no native build. The installer works on Windows, macOS, and Linux, x64 and arm64 alike. The one-command install is:
npx @colbymchenry/codegraph
If you do not have Node.js, there is a plain installer for your OS
(we use the Windows PowerShell installer in the PupPal monorepo) that
grabs the right build and puts codegraph on your PATH. Either way,
the CLI bundles everything it needs — the docs are explicit that "no
Node.js required — one command grabs the right build for your OS" —
and nothing is compiled on your machine.
Verify the install in a fresh terminal:
codegraph --version
The installer only puts the binary on your path; it does not touch your current shell, so open a new terminal before proceeding.
Step 2: Wire CodeGraph Into opencode (and Every Other Agent)
This is the step that connects the index to your agent, and it is where opencode's MCP support does most of the work. The CodeGraph installer auto-detects installed agents — Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, AntiGravity IDE, and Kiro — and writes each one's MCP server configuration for you:
codegraph install
Run it, pick opencode from the list (or use --target opencode for
non-interactive setups), and it writes the MCP entry into your
opencode config. For opencode, that means an entry in opencode.json
(or opencode.jsonc) under the mcp field. The resulting config
looks like this:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"codegraph": {
"type": "local",
"command": ["codegraph", "serve", "--mcp"],
"enabled": true
}
}
}
The shape follows opencode's standard local-MCP schema: type: "local"
tells opencode this is a child process it spawns, command is the
argv array to launch (you can also pass ["bun", "x", "..."]-style
commands or any runtime you like), and enabled: true activates it at
startup. If you ever need to pause it, flip enabled to false
without removing the entry — useful when you want to compare sessions
with and without the index. You can also pass an environment object
for server env vars and a timeout in milliseconds (default 5000) for
tool discovery.
CodeGraph also writes a short marker-fenced section into the agent's
instructions file — AGENTS.md for opencode — so subagents and
non-MCP harnesses learn the codegraph explore CLI command. That
matters more than it sounds: even without MCP, a subagent told "run
codegraph explore <question>" gets the same indexed lookup through
the terminal. codegraph uninstall removes everything cleanly.
If you prefer to wire it by hand — say, in a CI image or a team
standard config — the manual equivalent for any agent is to register
the server in the agent's MCP config with the stdio transport:
{
"mcpServers": {
"codegraph": {
"type": "stdio",
"command": "codegraph",
"args": ["serve", "--mcp"]
}
}
}
For opencode specifically, keep the mcp field form above; the
type: "local" + command array is the opencode-native way to
declare a stdio child process.
Where should that config live? opencode's configuration is layered the
same way most tools handle it: a global config at ~/.config/opencode/
(or %APPDATA%\opencode\ on Windows) applies to every project, a
project-level opencode.json in the repo root applies to that
workspace, and the two merge — project values win over global ones.
For CodeGraph, the project-level file is usually the right home for
the mcp entry, because the server only does useful work in repos
that have a .codegraph/ index. A global entry with enabled: false
and a per-repo override with enabled: true is a clean pattern for
teams that want the config checked in once and activated per project.
Because opencode ships MCP tool definitions into the model's context
on every request, keeping servers that are not applicable to the
current repo disabled is not just tidy — it saves tokens on every
single message.
You can verify the wiring without opening a session: opencode lists
the tools it loaded on startup, and codegraph status (or the
codegraph_status MCP tool) confirms the index is healthy and
reachable. If you do not see the tools, the usual suspects are a
stale opencode process (restart it — MCP servers load at startup), a
command that is not on the PATH of the process opencode spawns, or
a repo with no .codegraph/ directory (in which case the server
intentionally exposes nothing).
Step 3: Initialize a Project With codegraph init
Wiring the agent and indexing the code are two separate steps, and this is the one people forget. Installing the CLI and registering the MCP server does not index anything. Per project, run:
cd /path/to/your/repo
codegraph init
codegraph init creates the local .codegraph/ directory and builds
the full graph in the same command — one step, done. The index is a
SQLite database (codegraph.db) plus support files, and it lives in
the repo's .codegraph/ folder, which CodeGraph configures to be
git-ignored (the directory keeps only a .gitignore that excludes
everything except itself, so the database, daemon logs, and sockets
never appear in diffs). In PupPal's monorepo, that database sits at
roughly 300 MB after indexing the Flutter app, the Rust bridge, the
Dart packages, and the Cloudflare worker — a substantial graph, and
queries against it are sub-millisecond.
What actually gets indexed is controlled by ignore files. CodeGraph
respects a .graphifyignore at the repo root; PupPal's excludes the
usual suspects — .dart_tool/, build/, ios/Pods/,
android/.gradle/, and generated Dart files (*.g.dart,
*.freezed.dart, *.gen.dart, *.mocks.dart) — so the graph
contains hand-written code, not build output. That single decision
keeps the index small, relevant, and cheap to sync.
Restart opencode after the install/init sequence so the MCP server
loads. When a .codegraph/ index exists, the agent sees the
CodeGraph tools; in a workspace with no index, the server announces
itself inactive and lists no tools — the agent just works normally
with its built-in tools, and indexing stays your decision. No
breakage, no noisy errors, no tools to ignore.
What the Tools Actually Do: One Tool by Default
Here is the design choice that makes CodeGraph different from every
other code-intelligence MCP server. By default, the server exposes a
single tool: codegraph_explore. It is Read-equivalent — you give
it a natural-language question or a bag of symbol and file names, and
it returns:
- the verbatim, line-numbered source of the relevant symbols,
grouped by file — the same shape the
Readtool gives you; - the call paths among them, including dynamic-dispatch hops like callbacks, React re-renders, and JSX children that grep cannot follow;
- a blast-radius summary of what depends on the symbols.
One call usually answers the whole question. This is deliberate, and the maintainers are explicit about why: measured agent behavior showed that one well-aimed tool steers agents to a direct answer better than a menu of narrower ones — fewer mis-picks — and agents reach for it both when answering questions and while editing code. You do not have to teach the model which of nine tools fits the query; there is one, and it is the right one for almost everything.
Seven more tools exist and remain fully functional, but they are
unlisted by default — everything they return already arrives
inline on a codegraph_explore response:
| Tool | Purpose |
|------|---------|
| codegraph_node | One symbol's source + caller/callee trail, or a whole file read with line numbers (Read-parity). Returns every overload's body for an ambiguous name. |
| codegraph_search | Find symbols by name across the codebase (locations only) |
| codegraph_callers | Find what calls a function |
| codegraph_callees | Find what a function calls |
| codegraph_impact | Analyze what code is affected by changing a symbol |
| codegraph_files | Get the indexed file structure (faster than filesystem scanning) |
| codegraph_status | Check index health and statistics |
Re-enable any of them with the environment variable that controls the tool allowlist — a comma-separated list of short names that replaces the default. The list is a different way of saying "the single-tool default is the product; the rest are power tools for when you need them."
A concrete session makes the shape of a query clear. In PupPal's
repo, asking codegraph_explore "how does a photo check-in flow
from the Rust bridge into the UI?" returns, in one response: the
pi_bridge.rs functions that expose the session API, the Dart
RustBridgeApi methods that call into them, the call path across the
package boundary, and the blast-radius list of UI files that consume
the stream. No grep, no guessing file names, no partial reads — the
agent gets the source it needs and can answer immediately. That is
the experience the rest of this post is trying to reproduce in your
own setup.
Keeping the Index Honest: Sync on Edit
A pre-indexed graph is only useful if it does not lie. CodeGraph solves this with a three-layer sync that keeps the index in step with your code, and — crucially — makes sure the agent never gets a silent wrong answer in the brief window between an edit and the next sync:
- Incremental re-indexing. Edits to indexed files trigger targeted updates to the affected symbols rather than full rebuilds.
- File watching. The server watches the workspace; when you save, the affected parts of the graph refresh.
- Honest staleness. If the agent queries a region that has changed but not yet been re-indexed, the server says so instead of returning stale source silently.
That third layer is the one that separates this from a naive "index
once, hope for the best" tool. In PupPal's daily workflow — agents
editing Dart and Rust files in the same monorepo while a daemon keeps
the graph warm — the index is effectively never stale enough to
matter, and the agent's answers cite line-numbered source that matches
what read_file would show. We verify this the same way we verify any
agent claim: read the file. It matches.
Token Economics: Why the Single Tool Wins
The numbers that matter for an API-metered agent are tokens per
question and tool calls per task. The third-party measurements around
CodeGraph consistently land in the same range: roughly 35% lower API
cost and 70% fewer tool calls on typical code tasks, compared with
grep-and-read loops. The mechanism is easy to understand once you have
seen a session: a discovery loop that used to be five tool calls and a
screenful of partial reads becomes one codegraph_explore call
returning exactly the source the model needs to answer — nothing
more. Fewer calls means fewer model round-trips; tighter payloads mean
less context pollution; both mean the model's budget goes to
reasoning, not archaeology.
There is a caveat every opencode user should hear, straight from opencode's own MCP docs: every MCP server you enable consumes context space. The tool definitions themselves occupy tokens on every request, and servers that return large payloads can eat context fast. CodeGraph's single-tool default is precisely an answer to this — one tool definition instead of nine — and its payloads are bounded by relevance rather than by file scans. The guidance stands though: be selective about which MCP servers you keep enabled. In PupPal's opencode config, CodeGraph is one of only a handful of servers, and it is the only code-intelligence one. The rest of the intelligence comes from the index, not from more servers.
PupPal's Setup: One Index, Every Harness
PupPal runs several agent harnesses against the same monorepo —
opencode for day-to-day edits, Claude Code for reviews, Hermes Agent
for the automation pipeline — and CodeGraph's MCP surface is what
makes one index serve all of them. The .codegraph/ directory at the
repo root is a single source of truth; whichever agent launches
codegraph serve --mcp reads the same SQLite graph. The repo's
AGENTS.md documents the arrangement: the four indexed projects
(the Flutter app, the Dart packages, the Rust bridge, the Cloudflare
worker) are all reachable through codegraph_explore's projectPath
parameter, so an agent working in any subproject gets the same
indexed lookup without extra setup.
The projectPath parameter deserves a callout for monorepo users: it
lets one MCP server serve multiple indexed projects, and the agent
names which project it is exploring. The same pattern applies to
opencode — the server inherits the workspace root, and a monorepo
with several codegraph init-ed subprojects can be queried per
project.
For agent authors, the operational rules we settled on are simple:
- Index once per project with
codegraph init; re-init when the project layout changes dramatically. - Keep
.graphifyignorecurrent. Generated code in the index is noise; it dilutes every query. Exclude build outputs and generated files aggressively. - Trust but verify. Treat the index as a fast path to source, not as a fact engine — the line-numbered source it returns is the ground truth you verify against.
- Let subagents use the CLI. The
AGENTS.mdmarker section means even a subagent without MCP access can runcodegraph explorein the terminal.
From Index to Faster Engineering
The end state is boring in the best way: the agent just knows where
things are. When a PupPal session needs to trace how a photo
check-in event flows from the Rust bridge through the Dart packages
to the UI, codegraph_explore returns the call path and the
verbatim source in one call — the same trace that once consumed a
dozen grep-and-read round trips. The savings compound across a long
session: less context spent on discovery means more context for
actual reasoning, which means fewer corrective edits, which means
fewer rebuild cycles. On a Flutter+Rust codebase where every
rebuild is a non-trivial
cost,
getting the agent's first draft right matters even more.
CodeGraph's own docs frame it as "fewer tokens, fewer tool calls, 100% local" — and the local part is worth underlining. The index, the SQLite database, the daemon, and the MCP server all run on your machine. No code ever leaves the repo to be indexed, which makes the whole setup viable for the same reason PupPal's privacy story matters: the sensitive part of the pipeline stays local, and only the parts that must leave — the model prompts — do.
If you run opencode (or any MCP-capable agent) against a codebase
that is bigger than a few files, the sequence is short: install the
CLI, run codegraph install and pick opencode, codegraph init in
each repo, restart the agent. Ten minutes of setup buys you an agent
that stops hunting for code and starts answering questions. Given
what discovery loops cost in tokens and time, that is the cheapest
performance upgrade an agent setup can get.