MCP · stdio · read-only

The map an agent cannot build for itself.

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

Nine steps, each one tool call.

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?

  1. 01

    Ground overview

    Which Feature does this task belong to, and which files does it claim? Skipped when the project has no .elv layer.

  2. 02

    Orient map

    Get the folder’s shape instead of reading ten files to reconstruct it.

  3. 03

    Locate similar

    Check the helper does not already exist under another name.

  4. 04

    Prepare context

    Pull the edit target plus its neighbours’ signatures in one call.

  5. 05

    Check reach impact · trace

    Who breaks if this changes, and how control arrives here.

  6. 06

    Edit

    Ordinary edits, with LSP for the precise pointwise work. Mezzanine does not replace that layer.

  7. 07

    Verify tests_for

    Run the tests that actually reach the change, not the whole suite.

  8. 08

    Self-review assess_change

    Metric deltas and smell churn on the real diff, before presenting it.

  9. 09

    Re-shape reshape

    If the folder slipped a tier, the drawing and the one move that puts it back. One rung, not four.

The surface

15 tools, all read-only.

ToolAnswers
overviewThe Elevator domain map, or a focused bundle for one entity. The grounding step — which capability does this task belong to.
mapFiles and entities of a folder with complexity and coupling annotations, at three depth levels. One call instead of ten file reads.
similarSimilarity-ranked search before you write something that already exists.
contextA minimal edit pack: the target’s source plus the signatures of everything it uses and everything using it.
impactBlast radius — what it uses, direct dependents, then transitive dependents level by level.
traceShortest dependency path(s) from A to B, with the relationship kind of every hop.
qualitySmells, top offenders by composite refactor pressure, dependency cycles.
hotspotsGit churn × complexity. The ranking complexity alone cannot give you.
tests_forWhich tests reach an entity, directly or transitively — including Rust inline mod tests.
assess_changeWorking tree versus a git ref: per-entity metric deltas, added and removed entities, smell churn.
dead_codeEntities nothing in the analyzed tree reaches. Evidence to check, never a delete list.
spec_sliceOne branch of the Elevator spec, for a task that belongs to a single capability.
reshapeThe graph one folder draws — children, levels, every edge with its reading, the doors — plus the single move that lifts it one tier.
layoutWhere 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.
boundariesThe 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

Two properties make the loop worth running.

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.

Exact type edges where it counts

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 →

Measured call edges

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.

Warm cache

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

The ranking can hand you the task, not just the finding.

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.

Two rules keep it honest

  • Every number comes from the analyzer. Measured values off the entity's metrics, thresholds off the same struct the report reads — so the prompt cannot disagree with the ranking that produced it.
  • It cites the metrics that actually rank it. The set mirrors the composite-score calculation and splits on whether the entity is callable, so it can neither cite a metric the ranking ignores nor omit one it weights.

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.

Choose what it costs

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:

ModeSizeTokensCarries
full94.0 KB~23,500Every entity in scope, in full.
hybrid17.8 KB~4,446The target’s own source, signatures for its neighbours.
ranges4.7 KB~1,167Line 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

One binary, one stdio server.

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 →
.mcp.json
{
  "mcpServers": {
    "mezz": {
      "command": "mezz",
      "args": ["mcp"]
    }
  }
}

A good first prompt

“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.”