Deterministic analysis
Identical trees produce byte-identical graphs. That is what makes assess_change usable: every delta it reports is a real change in the code,
not parser jitter between two runs.
MCP · stdio · read-only
Agents have a specific economics: every token of context costs, and the global view never fits. An agent that re-reads five files to work out what calls a function has spent its budget rebuilding something a graph already knows. mezz serves that graph as compact ranked text — capped, explicitly truncated, filtered of noise.
The loop
This is the shape of a task that goes well. Every step replaces something the agent would otherwise do by reading files and inferring — and the last one asks a question the agent has no way to ask itself: is the folder still followable after what I just did?
overviewWhich Feature does this task belong to, and which files does it claim? Skipped when the project has no .elv layer.
mapGet the folder’s shape instead of reading ten files to reconstruct it.
similarCheck the helper does not already exist under another name.
contextPull the edit target plus its neighbours’ signatures in one call.
impact · traceWho breaks if this changes, and how control arrives here.
Ordinary edits, with LSP for the precise pointwise work. Mezzanine does not replace that layer.
tests_forRun the tests that actually reach the change, not the whole suite.
assess_changeMetric deltas and smell churn on the real diff, before presenting it.
reshapeIf the folder slipped a tier, the drawing and the one move that puts it back. One rung, not four.
The surface
| Tool | Answers |
|---|---|
| overview | The Elevator domain map, or a focused bundle for one entity. The grounding step — which capability does this task belong to. |
| map | Files and entities of a folder with complexity and coupling annotations, at three depth levels. One call instead of ten file reads. |
| similar | Similarity-ranked search before you write something that already exists. |
| context | A minimal edit pack: the target’s source plus the signatures of everything it uses and everything using it. |
| impact | Blast radius — what it uses, direct dependents, then transitive dependents level by level. |
| trace | Shortest dependency path(s) from A to B, with the relationship kind of every hop. |
| quality | Smells, top offenders by composite refactor pressure, dependency cycles. |
| hotspots | Git churn × complexity. The ranking complexity alone cannot give you. |
| tests_for | Which tests reach an entity, directly or transitively — including Rust inline mod tests. |
| assess_change | Working tree versus a git ref: per-entity metric deltas, added and removed entities, smell churn. |
| dead_code | Entities nothing in the analyzed tree reaches. Evidence to check, never a delete list. |
| spec_slice | One branch of the Elevator spec, for a task that belongs to a single capability. |
| reshape | The graph one folder draws — children, levels, every edge with its reading, the doors — plus the single move that lifts it one tier. |
| layout | Where a folder’s files would sit if its own drawing decided, with the after-numbers measured rather than estimated. Output is a list of git mv lines. |
| boundaries | The folder’s own imports, split into landed on a door, landed on shared vocabulary, and reached past a door — the last being the work list. |
Why the output is trustworthy
Identical trees produce byte-identical graphs. That is what makes assess_change usable: every delta it reports is a real change in the code,
not parser jitter between two runs.
UsesType edges come straight out of signatures and fields in
Rust, Java, TypeScript, Python, Go, Dart, Groovy, Kotlin, Svelte — so type-level impact is exact rather than inferred in those
9. Everywhere else it is approximated →
93.7% recall at 98.6% precision against a rust-analyzer oracle. Good enough to act on — not good enough to treat an empty result as proof.
Graphs are cached per path with watcher-driven invalidation. The first call after a source change re-analyses; the rest are milliseconds.
The full limitations, including what the parsers still miss →
Past reading, into doing
Knowing an entity is in trouble is only half of it — turning that into work for an agent
used to mean copying paths and line ranges out of the tool and writing the ask yourself. POST /api/scope now returns a refactor_prompt: the ask, the
measured evidence for it, and the surrounding context, in one paste.
In the VS Code extension this is one click on a quality row: it opens a terminal with Claude Code already running against the prompt. The agent runs in your own shell under your own permissions, with nothing listening on a port — it is what you could have typed.
Embedding every entity's full source is mostly waste — the agent has a Read tool and the
repository is right there. prompt_context picks how much rides along:
| Mode | Size | Tokens | Carries |
|---|---|---|---|
| full | 94.0 KB | ~23,500 | Every entity in scope, in full. |
| hybrid | 17.8 KB | ~4,446 | The target’s own source, signatures for its neighbours. |
| ranges | 4.7 KB | ~1,167 | Line ranges only — the agent opens what it needs. |
Measured on one real hotspot with a fan-out of 89. ranges is 95% smaller
than full and says the same thing.
Setup
mezz mcp speaks MCP over stdio. Drop it in your client's server config — .mcp.json at the project root for Claude Code, the equivalent block for any
other MCP client — and the tools appear.
There is no daemon to run, no port to pick and nothing to authenticate against. The server reads the checkout it was pointed at and nothing else.
Install first →{
"mcpServers": {
"mezz": {
"command": "mezz",
"args": ["mcp"]
}
}
}“Use Mezzanine's overview and map before you read any files. Then use impact on the function you plan to change, and assess_change before you show me the diff.”