TL;DR
codegraphandcodebase-memory-mcphad the idea first: hand a coding agent a code graph over MCP. But on my own open-ended questions the token bill didn't budge, so I built@ttsc/graph.It hands the agent an index the TypeScript compiler already resolved (never source bodies), through one tool with a forced chain-of-thought. The result on "how does this work?" questions: ~10× fewer tokens, answers no worse. A measured, conservative median.
- Repository: https://github.com/samchon/ttsc
- Benchmark: https://ttsc.dev/docs/benchmark/graph
- Issues: https://github.com/samchon/ttsc/issues
1. Preface
1.1. What @ttsc/graph Is
Ask a coding agent "how does this project actually work?" and you know what comes next. It opens a file. Follows an import, opens another. Opens another. Dozens of files later, it answers.
@ttsc/graph kills that crawl. It hands your agent a graph of your TypeScript codebase — drawn by the compiler itself — over MCP: what calls what, what depends on what, where each piece lives. The agent answers structural questions from the graph instead of spelunking through files — and every claim comes anchored to an exact file:line the compiler pointed at. Not invented; something you can open and check.
How big is the difference? Here's the chart.
Same question, same agent. Only @ttsc/graph stays flat at ~42k tokens across every repo. The others swing wildly — and some even spend more than the baseline (no MCP at all). Full numbers and method are on the benchmark page.
1.2. What a Code Graph Is
First, what a "code graph" even is. Land in a city you've never visited and you don't read every street sign in order — you glance at a subway map. It throws away the buildings, the trees, the real distances, and keeps the one thing you need: what connects to what. Readable in five seconds.
Code has the same structure. Nodes are functions, classes, files; edges are calls, imports, extends, depends-on. Draw them all and you get a code graph — an index of "what calls what." The agent queries that index once instead of walking every street.
1.3. Index, Not Source Inlining
Here's the first fork in the road. @ttsc/graph doesn't return source bodies. Names, edges, signatures, spans — an index, nothing more.
And the span is the point. Every range it cites is a coordinate the compiler vouched for, so if you doubt an answer you just open that spot and check. The reassuring part isn't "it read no files" — it's that a place to verify always comes attached.
Tokens stay flat because the response size is bounded independent of repo size. A 100k-line project or a 10k-line one — one question returns a similarly sized chunk of index. Why the other tools spill source instead is the autopsy in §2.
1.4. How It Works, in One Breath
In one sentence: it reads an index from the program the TypeScript compiler already resolved, and feeds a single MCP tool a forced chain-of-thought so the agent doesn't wander. That's the whole thing. Details in §3. First — why I built it at all.
2. Why I Built It
2.1. The Promise
Honestly, not my idea. codegraph put a code graph in front of an agent over MCP first, and codebase-memory-mcp does the same across 158 languages. This post's benchmark is a faithful port of codegraph's, run on two prompt families: a common onboarding question, and codegraph's own per-repo questions. (codebase-memory-mcp's would have made a third, but it ships no reproducible method.)
And their core claim is legit. The enemy is the agent's grep · find · Read crawl loop. Every "where is this?" turns into a grep, a file open, an import chased, another grep — dozens of round-trips burning tokens and time. codegraph's pitch is clean: replace that loop with one graph query — "58% fewer tool calls, file reads to ~zero." codebase-memory-mcp goes further, headlining "120× fewer tokens" (a 99.2% cut).
Sounded great. So I installed them.
One thing up front, though. Cutting an agent's tool calls is the easy part. Cutting its tokens at the same time is harder. And cutting tokens without the answer getting any worse is the real problem. Those are three different things, not one.
2.2. So I Tried Them
Here's what actually happened.
-
Tokens didn't drop at all. The tool-call count may have fallen, but the thing you pay for — tokens — didn't budge. On some cells it was even worse than the baseline (
codegraphspent up to 47% more,codebase-memory-mcpup to 96% more). - Claude Code and Codex got worse, not smarter. They kept missing the intent.
- Useless MCP calls fired constantly, blocking or delaying the work I actually wanted done.
- I couldn't just ask in plain language. Every query had to be hand-shaped —
codebase-memory-mcpwants a Cypher query with an exactqualified_name;codegraphwants you to name the symbols for a flow. That babysitting alone ate the productivity.
In short: for my kind of question, worse than before I installed them. So I went digging.
2.3. Bodies, and a Buried Graph
The first cause was what each tool does with what it has.
codegraph returns whole source bodies — by its own description, "the Read, done for you." Fine for editing. But for a broad "how does this work?", the body is the token bomb. Past a dozen files it truncates and says "call again for more." So you call again. More bodies.
codebase-memory-mcp is the more interesting one, because under the hood it has the right idea. It builds a real relation graph, a lot like the one I ended up with: not just where things are, but how they call and depend on each other. The trouble is the surface. That capability is spread across fourteen MCP tools, and the useful ones want precise input, a Cypher query or an exact qualified_name. Handed fourteen tools and a query language, the agent mostly never reached the relation data at all. Measured, it called the MCP 0 times in every run and fell back to the shell: it gave up and grepped. The graph was right there; the surface buried it.
So one hands back too much, and the other keeps the right thing behind a door the agent cannot find. Neither moves the token bill.
2.4. The Instructions
The second cause is the instructions, and here the two tools go in opposite directions.
codegraph forces its tool, hard. Its MCP instructions tell the agent to "use it instead of reading files," "call it before you Read," "don't grep or Read first," and reach for it on "almost any question." So calls fire even when the graph isn't the answer — a config, a small edit, a question it can't answer at all — and those wasted calls block the real work. codegraph's README is candid about why: the tool only helps when queried directly and is overhead otherwise, so the instructions push hard to keep it from being overhead.
codebase-memory-mcp does the reverse. Its MCP initialize sends no instructions at all, and most of its tools ship with no usage guidance (a couple of descriptions say "use instead of grep" for specific operations; auto-indexing exists but is off by default). Fourteen tools, and almost nothing telling the agent which to reach for or when — so it mostly didn't, and fell back to the shell, as we just saw.
So one over-directs and the other under-directs. Neither lands on the thing you actually want: use the graph when it helps, and stop when it doesn't.
And to their credit, none of the limits are hidden. codegraph's README says its token savings are scale-dependent and small on a normal codebase. codebase-memory-mcp reports its biggest numbers on a handful of structural queries, not open-ended ones. They tell you, plainly, that the wins are measured on targeted scenarios.
That is the whole point. Those limits get bigger the more general your use is, and general use is where I live. So I took these two for what they are, the pioneers that put a code graph in front of an agent at all, learned from where each one ran into a wall, and built for the case they were not aimed at: the open-ended question, with the tokens down and the answer still good.
3. How It Works
@ttsc/graph is designed to cure those four pains one at a time. The order below is the prescription.
| Pain (§2) | Antidote (§3) |
|---|---|
| 2.4 — over-forced, or no guidance at all |
3.1 — guides without forcing (escape + stop) |
| 2.3 — a real graph buried under 14 tools | 3.2 — one tool, asked in plain language |
| 2.3 — source bodies blow up tokens |
3.3 — returns index only |
| (only works if trustworthy) | 3.4 — the real compiler |
3.1. It Never Forces You
First, it never forces. Not one line of the instruction says "use this instead of Read." It states a condition instead: "use it when the answer depends on TypeScript symbols, calls, or types." And it makes escape a first-class choice for when the evidence lives outside the graph.
The only strong rule is a single one: "once you've used it, that's compiler-issued truth — don't re-doubt it by reading files." When enough evidence is in, the result tells the agent "answer now and stop" — it points at a stopping line, it doesn't demand more calls.
So the useless forced calls that used to block work simply don't happen. The agent uses the graph when it should and skips it when it shouldn't. (Antidote to §2.4.)
3.2. One Tool, with an Escape Hatch
Second, Claude Code won't always pick the right tool at the right moment. The more options, the more misfires, and that is exactly what buried codebase-memory-mcp's relation graph: 14 MCP tools, and the agent never found its way to the right one. (codegraph avoids the tool-count trap with a single default tool; it just pushes that one tool too hard — the §3.1 problem.)
@ttsc/graph has exactly one tool. Inside it, what to do is split by a CoT-fed union type. And the safeguards stack: a review step mid-CoT can switch the request type, and if it's still wrong, escape bails. Safety first.
Here's the entire surface — one type, with the comments trimmed to one line each:
// TOOL DESCRIPTION: inspect the compiler-built TypeScript code graph over MCP.
export interface ITtscGraphApplication {
inspect_typescript_graph(
props: ITtscGraphApplication.IProps,
): ITtscGraphApplication.IResult;
}
export namespace ITtscGraphApplication {
// The forced chain-of-thought, then exactly one graph request.
export interface IProps {
question: string; // restate the code question being asked
draft: string; // intended request type + why it is the smallest
review: string; // self-correct a wrong/broad draft; pick `escape` if off-graph
request: // the final operation, chosen after review
| ITtscGraphEntrypoints.IRequest // orientation: where to start reading
| ITtscGraphLookup.IRequest // find a symbol by name
| ITtscGraphTrace.IRequest // trace call / data flow
| ITtscGraphDetails.IRequest // a symbol's signature, members, neighbors
| ITtscGraphOverview.IRequest // repo-level overview
| ITtscGraphTour.IRequest // broad code tour, answered in one call
| ITtscGraphEscape.IRequest; // not a graph question -> bail out
}
// The selected request's result; `result.type` mirrors `request.type`.
export interface IResult {
result:
| ITtscGraphEntrypoints
| ITtscGraphLookup
| ITtscGraphTrace
| ITtscGraphDetails
| ITtscGraphOverview
| ITtscGraphTour
| ITtscGraphEscape;
}
}
As you can see, question → draft → review are forced function arguments. typia compiles this type into the tool's JSON schema and validator, so an unfilled CoT is rejected at the call boundary. In typia's words: "free prose can hide a skipped step; a typed submission cannot." (Deeper: CoT compliance, function-calling harness.)
Those one-line comments are condensed; in the source, the fuller JSDoc on each member is what typia compiles into the MCP instruction and the schema descriptions the model reads. Want the details? Read ITtscGraphApplication.ts.
This is also the antidote to that babysitting. You just ask in plain English — "just figure it out." Translating that vague ask into the precise request type, and self-correcting, is the CoT's job. No symbol names, no Cypher, no query syntax to memorize.
Each request branch is one graph operation — its own request (.IRequest) and result type. Dig in:
-
ITtscGraphTour— a broad code tour. Answers "new project, how does it work?" in one index. -
ITtscGraphEntrypoints— find the entry points. Where to start reading. -
ITtscGraphLookup— find a symbol by name. -
ITtscGraphTrace— trace call / data flow (forward, or impact radius). -
ITtscGraphDetails— a chosen symbol's signature, members, neighbors. -
ITtscGraphOverview— a repo-level overview. -
ITtscGraphEscape— a no-op bail-out, out of the graph.
3.3. Index Only, So Nothing Explodes
Third, it returns index only. Names, edges, signatures, spans — the relationships themselves, not just where things sit. The edges and signatures carry "what calls what, and how," so the agent assembles the answer right there without opening a file. Source bodies are never inlined, and a span is a citation to verify, not a command to go read.
Two effects. No token explosion from spilled bodies, and no compounding mess where that explosion plus a misfired MCP call clouds the agent's judgment. The response is bounded independent of repo size, so tokens stay flat.
The source-body blowup from §2.3 and the "agent got worse" from §2.2 both ease in this one decision.
And the thesis I opened in §2.1 closes here. @ttsc/graph kills the grep crawl loop too — that's table stakes. The difference is it does so without the tokens blowing up, and without the answer getting any worse.
3.4. Only the Compiler Makes This Possible
Fourth. All three above only hold if the results are trustworthy — and that trust comes only from a real compiler.
A heuristic parser like tree-sitter sees text. So there are things it can't resolve — tsconfig paths aliases (@app/* → the real file), cross-package references in a pnpm monorepo, symlinks, re-export chains. Only a compiler that actually finished module resolution can wire those up correctly.
@ttsc/graph reads the program ttsc already type-checked and resolved. So aliases and monorepos just line up. And because it rides the toolchain, the graph falls out as a near-free byproduct of the type-check that's already running — no separate index step (codegraph init, codebase-memory-mcp's index_repository), no file watcher, no stale-index problem.
There's a bonus the compiler throws in, too. The graph doesn't just hold structure — it also carries every tsc compile error and @ttsc/lint/plugin (typia, nestia) lint violation, each fused onto the symbol that owns it. So "what's broken here?" and "what breaks if I change this?" come from the same index. Not just the shape of the code, but what's wrong with it right now — in one graph.
Exact, so you can trust it; trusted, so you can stop. exact = trust = stop. That's the root of all of it.
4. Try It
4.1. What It Can't Do
Bragging only would be a scam. Limits first.
@ttsc/graph is TypeScript-only. It traded breadth for depth — the opposite bet from the 158-language tools.
But that's not a footgun. On a non-TypeScript project there's simply no graph to hand over, so the agent never leans on it. Even inside a TS project, things outside the typed graph — configs, docs, exact-text search — escape cleanly out. Step outside its scope and it doesn't break; it just quietly steps aside. It's strong on exactly the questions a graph can answer, and no further.
And here's the most important prerequisite. @ttsc/graph rides ttsc, and ttsc runs on the TypeScript-Go (TypeScript v7) runtime. v7 isn't released yet — it's at RC. That's why the install pins typescript@rc. It runs fine on the RC, but to be clear: this is not something you drop onto your current stable TypeScript (v6.x). Once v7 ships, that caveat dissolves on its own.
4.2. It Might Break
I built this because I couldn't stand using codegraph. So I worked hard to make it hold up across general situations — no "I installed it and Codex got dumber." At least on my machine, it was solid.
But the world is wide, and there could be failure modes I haven't seen. It's open source. If something's awkward, or you find something I got wrong, file an issue. That's the fastest way to make this thing solid.
4.3. Add It in Four Lines
Setup is four lines.
npm install -D ttsc @ttsc/graph typescript@rc
{
"mcpServers": {
"ttsc-graph": {
"command": "npx",
"args": ["-y", "@ttsc/graph"]
}
}
}
The agent queries the graph on its own — you never call it by hand. The title says Claude Code, but any MCP-capable agent works — Codex, Cursor, and the rest.
4.4. See It in 3D
Bonus. Run it in your own project and you can spin your code graph in 3D in the browser.
npx @ttsc/graph view
This is TypeORM, colored by kind:
And — to those of you on other languages. This approach isn't TypeScript's alone. Truly conquering the code graph takes each language's real compiler — heuristics can't resolve aliases, monorepos, or types. I picked TypeScript. Someone in Go, Rust, Python, Java, C# should ride their language's compiler or LSP and conquer theirs. One language at compiler depth beats 158 skimmed off the top — far more valuable to an agent. I'd love to see it.
One last thing. Don't make your agent read the whole codebase. Hand it the index the compiler drew.
5. References
- Repository: https://github.com/samchon/ttsc
- Benchmark: https://ttsc.dev/docs/benchmark/graph
- Issues: https://github.com/samchon/ttsc/issues
The pioneers this builds on:
-
codegraph: https://github.com/colbymchenry/codegraph -
codebase-memory-mcp: https://github.com/DeusData/codebase-memory-mcp





Top comments (0)