The Graph-Native Intelligence Layer for Code.
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
We haven't yet run this listing's install command through our automated sandbox check. This isn't a red flag β we're steadily working through the catalog.
π‘ Paste the JSON block into your client's configuration file under mcpServers, then restart the application.
Graph-native intelligence for codebases.
Know what breaks before you break it.
Simulated replay β the arbor commands and their output are real (tokio @ 178k LOC). Methodology: BENCHMARKS.md
v3.0.0 β The Right Node Β· v2.6.0 stopped dropping colliding symbols. It did not stop resolving them to the wrong one. When a bare name matched several modules, resolution fell through to "same directory" and confidently attached the edge to whichever definition happened to sit next to the caller. On a graded fixture the three largest hubs reported zero downstream impact while unrelated siblings inherited their centrality. A file's own imports now settle it. Reproduce it yourself: getArbor-dev/arbor-torture
Most AI coding tools treat code as text. Arbor builds a semantic dependency graph β functions, classes, and modules as nodes; calls, imports, and inheritance as edges β then answers execution-aware questions with deterministic precision:
| Question | Arbor answer |
|---|---|
| If I change this symbol, what breaks? | Blast radius with depth, confidence, and risk level |
| Who calls this β directly and transitively? | Caller/callee traversal on the call graph |
| What's the shortest path between A and B? | A* path through real dependencies |
| Is this PR too risky to merge? | CI gate on blast-radius thresholds |
No keyword guessing. No embedding hallucinations. One graph, every interface.
Where the graph is unsure, it says so β edges carry a confidence, and ambiguous resolutions are labelled rather than hidden. An honest unknown beats a confident wrong answer.
One fix, measured.
Symbol resolution consults the importing file. When a bare name matched
definitions in several modules, resolve_ref fell through to SameDir and
attached the edge to whichever definition sat in the caller's own directory β
not a dropped edge, a confidently misrouted one, stamped at 0.55 confidence.
GraphBuilder already kept a per-file import map, but only
apply_import_validation read it, and that scores an edge after one has been
chosen. It never saw the references going to the wrong node. Consulting it
between the same-file and same-directory checks keeps a local definition
shadowing an import, while letting a written import beat mere adjacency.
Resolution::ViaImport scores 0.93, above SameDir's 0.55.
A fixture of 260 modules across 10 layers, each layer defining the same 26 function names. Ground truth is derived from the generator's own edge list, so the expected answer is exact rather than estimated.
| True downstream | v2.6.0 | v3.0.0 |
|---|---|---|
| 179 | 0 | 163 |
| 178 | 0 | 161 |
| 161 | 0 | 133 |
| 143 | 22 | 133 |
| 122 | 22 | 119 |
| 36 | 22 | 61 |
| 16 | 22 | 46 |
Previously flat at about 22 regardless of the real answer. Now it tracks. Risk
on the largest hub moves from LOW to CRITICAL.
Total edge count barely moves (1335 β 1334). That is the signature of misrouting rather than loss: the edges were always there, pointing at the wrong nodes.
Resolution gains a ViaImport variant β an exhaustive match will not compileCall cycles are condensed before PageRank. Each strongly connected component is ranked once and that mass is shared across its members, so a closed ring does not fill the top of the ranking and a cycle that calls out keeps its members together. The number CentralityScores reports is still the v2.6.0 percentile, i / (n - 1).
Written down rather than left to be discovered:
class Middle(Base) is invisible, so changing
a base class shows zero blast radius.importlib, __import__, import(),
eval(require(...))) are unresolvable by construction and are documented as
expected misses in the fixture rather than counted as defects.Correctness, not speed. Each of these was silently wrong before.
| Fix | Why it mattered |
|---|---|
| Colliding symbols are kept | SymbolTable used HashMap::insert, so a second handler, new, or process replaced the first. The loser had zero callers and was invisible to blast radius. |
| Resolution is deterministic | Same-directory locality was decided by iterating a HashMap. Rust seeds RandomState per process, so the same binary on the same input could build different edges between runs. Now asserted across eight fresh processes. |
| Edges carry confidence | A proven same-file call and a same-directory guess were identical evidence. Each edge now scores [0,1] by how it resolved. |
| Exported TS symbols indexed once | export_statement recursed into its children, then the generic loop recursed again β every exported symbol became two vertices sharing one node id. 133 phantom nodes on a 149-file app, 25% of the graph. |
| Method calls on untyped receivers resolve | obj.method() was dropped outright, leaving the graph nearly edgeless on TS/JS β and an empty graph reports a blast radius of zero, which reads as "safe" rather than "unknown". |
| Centrality is a percentile rank | Scores were divided by the graph maximum, so the top node was 1.0 by construction and a 0.6 threshold meant nothing consistent between repos. Adding one hub rescaled every other node. |
| Resolution is O(1), not O(refs Γ nodes Γ files) | Unresolvable references β stdlib and third-party calls, most call sites in real code β paid the worst case. Suffixes are now indexed. |
New capability β concept search. Substring matching cannot find get_authenticated from login; they share no substring. Identifiers are now tokenized and expanded through curated concept clusters, and docstrings, signatures, and paths are indexed alongside names. Deterministic, offline, no model. Available on the library as ArborGraph::search_ranked (arbor query remains literal-substring for now).
New capability β hunk-level impact. changed_node_ids_for_ranges keeps only symbols whose lines actually changed, instead of every symbol in a touched file.
Measured on identical node sets, after the duplicate-extraction fix:
| Codebase | Before | After |
|---|---|---|
| TypeScript (149 files) | 172 edges | 196 (+14%) |
| Rust (arbor-graph) | 116 edges | 167 (+44%) |
Graph caches from earlier versions are invalidated β centrality now means something different, so a stale cache would be read wrong.
| Change | Measured |
|---|---|
| PageRank rewrite β flat call-graph adjacency replaces per-iteration traversal | 149.8ms β 6.6ms on a 10k-node graph (23x), verified side-by-side vs the old implementation |
| Parallel indexing β parse fans out across all cores, deterministic assembly | Arbor: 253ms β 95ms Β· tokio (178k LOC): 2.7s β 1.6s |
| Warm-start centrality β watcher recomputes seed from previous scores | Converges in ~2 rounds after a one-file patch instead of the full 20-iteration budget |
| Convergence early-exit | Iteration stops at 1e-9 max delta β the budget is a ceiling, not a sentence |
Think a number is wrong? cargo bench -p arbor-graph and prove it: BENCHMARKS.md.
2026-07-28, HTTP transport, Tasks, MCP Apps)Factual signals from GitHub, npm, and our automated checks β not a rating.
No reviews yet β be the first to share how this listing worked for you.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/arbor)<a href="https://allmcps.com/mcp/arbor"><img src="https://allmcps.com/api/badge/arbor?style=directory" alt="Arbor on AllMCPs" /></a>