zzop-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@zzop-mcpanalyze this repo and tell me where to refactor first"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
zzop ( Zero Zone Of Pain )
Your AI coding agent can't read your whole codebase. zzop reads it — and answers the same way every time.
Point zzop at one repository, or at your frontend and backend together, and it returns a single JSON document describing what is actually there: which frontend calls reach which backend routes and which reach nothing, what looks risky, what is dead, where to refactor first — and what this run could not see. An agent starts from that instead of guessing from the handful of files it had room to open.
zzop does not write code. It makes the understanding a code generator works from accurate and repeatable — same commit in, byte-identical findings out — so what your agent writes rests on what your code does rather than on what it inferred from a partial read. The thing being improved is comprehension, not capability.
See it break something
Break a route is the join axis in one change — one of the four this tool reports, and the one hardest to see any other way: rename one backend route in a frontend/backend pair that share no code and no types. The frontend still compiles, its tests still pass — and zzop names both ends of the break, file and line (abridged here; the demo page shows the run's own format):
=== unprovided consumes ===
"PUT /api/user" @ fe-vite src/pages/Settings.jsx:19 ← the call now hits nothing
=== unconsumed provides ===
"PUT /api/users/me" @ be-express src/app/routes/auth/auth.controller.ts:61 ← the route nobody callsYou can run that in seconds, on a pair this repository ships and with nothing to fetch — the demo pair is committed here, so clone this repository to get it:
git clone https://github.com/eezz4/zzop && cd zzop
bash docs/demo/break-a-route-shipped.sh # needs a zzop binary and node, nothing elseThe script finds a binary in target/release/, or any zzop on your PATH, and tells you how to
get one if it finds neither. It analyzes a temporary copy and never edits docs/demo/pair/.
It asserts the join state at each step instead of printing it, so it fails rather than narrating a
claim that has stopped being true. CI runs it on pushes to main and on pull requests; on a
development branch scripts/ci-local.sh is the lane that sees it.
The page itself is a narrated walkthrough of the same change on two independently-authored
repositories: every command and the output it produced are written out, so it
reads end to end without you running anything. The script behind it, docs/demo/break-a-route.sh, is a
maintainer tool rather than a first-run command — it builds a cargo example (so it needs a source
checkout, not a released binary) and analyzes two repositories you supply at
corpus/oss/fe-vite and corpus/oss/be-express. corpus/oss/ is gitignored and nothing in this repo
ships those trees — they are third-party checkouts, not ours to redistribute; see
CONTRIBUTING.md on bringing your own corpus. (The synthetic corpus we did write
is committed, at cases/ — every file of it but one, a fixture
that has to carry a live vendor-token literal and so cannot be committed at all; its README says what
that costs the benchmark score.)
Related MCP server: agentless-mcp
Which of the two binaries do you want?
zzop ships as two Node-free binaries. Decide which one you need before you install anything:
If you want | Use | How you drive it |
An AI agent (Claude Code, Claude Desktop, any MCP client) to answer questions about your repos |
| Install the plugin or the |
To run analyses yourself — a terminal, a CI job, a script |
|
|
Both binaries dispatch to the same shared handlers over the same engine, so a tool call and a CLI run against the same path give the identical answer. Neither one makes a network request of any kind — they carry no HTTP dependency at all (privacy).
Documentation site: https://eezz4.github.io/zzop/ (authored in
site-src/, generated byscripts/gen-site.mjs;site/is the committed output — a guard rejects hand edits to it)Documentation (in-repo):
docs/README.mdHow it works, in depth:
docs/ARCHITECTURE.mdExternal parser protocol:
docs/NORMALIZED_AST.md
Quick start
Neither binary needs Node.js, npm, or a compiler. Get them one of four ways:
Download the binaries. Grab the
zzop-cli-<platform>[.exe](CLI) and/orzzop-mcp-<platform>[.exe](MCP server) assets for your platform from GitHub Releases and run them directly, or put them onPATH. Each release also carries aSHA256SUMSasset covering every one of those assets. ⚠ On an older pin there may be no release to download at all — see VERSIONING.md under Breaking in the current0.x, which is the one owner of which tags carry assets, and build from source for the ones that do not. (This sentence said the asset exists "from v0.30.0 onward" and told you to check on an older pin. Both halves were written before the 2026-09-23 history rewrite and neither survived it: for a tag between v0.30.0 and v0.34.0 there is nothing to check, because the release itself is gone.) Verify withsha256sum -c SHA256SUMS --ignore-missing, orshasum -a 256 -c SHA256SUMS --ignore-missingon a macOS box that has nosha256sum. Its scope is narrow and worth stating: it catches a corrupted download, and it is a hook for anyone who obtained the digest through another channel. It does not defend against a compromised release origin — an attacker who can swap an asset can swapSHA256SUMSbeside it — and TLS already refuses MITM. What each platform is actually verified to do differs, and it is worth knowing before you pick one. The test suite runs on Linux x64 only. Every release build is smoke-tested by runningversionand comparing it against the manifest — on Windows x64, macOS arm64 and Linux x64; the two cross-compiled targets (macOS x64, Linux arm64) cannot execute on the runner that built them and are not smoke-tested at all. So on macOS and Windows what is proven is that the binary loads and links; the analysis behaviour is proven on Linux and assumed to carry. The engine has no platform-specific code path save one, which is why that assumption is a reasonable one and not a promise. The exception is worth naming because it is the case the assumption covers least well:crates/summary/src/siblings.rsfolds directory names case-insensitively on Windows, because a case-insensitive filesystem would otherwise report a root you analyzed as its own unanalyzed sibling. The Windows arm of that fold is executed by nothing, anywhere: the suite runs on Linux, where the arm is not taken, and a developer mac cannot test the other arm either, because APFS is case-insensitive by default and the test skips itself with that reason. The source says so at the skip. Recount:git grep -nE 'cfg!?\(\s*(not\()?\s*(windows|unix|target_os|target_family)' -- 'crates/**/*.rs' 'parser/**/*.rs' 'rules/**/*.rs' 'packages/**/*.rs'.Claude Code plugin.
/plugin marketplace add eezz4/zzop, then/plugin install zzop@zzop— see Use in Claude Code below. (Windows: the install hook needs a POSIX shell — Git for Windows is the supported path; details in packages/README.md.)Claude Desktop. One-click
.mcpbbundle (drag-and-drop install) — what an installer should know BEFORE installing (updates are manual; on macOS the unsigned binary is expected to hit Gatekeeper; the privacy statement) is packages/mcpb/BUNDLE-README.md — bundles from releases after v0.32.0 carry that file as their own README; bundles up to and including v0.32.0 ship without it, which is exactly why the pre-install pointer here matters. Packaging internals: packages/mcpb/README.md.npm.
npm i -g @zzop/cliinstalls the exact samezzopbinary above, fetched for your platform as an npm dependency — every subcommandzzop helplists, byte-for-byte the same output, no Node runtime involved beyond a tiny launcher script and no separate JS implementation that could drift from the native binary. Convenient when a project already manages its toolchain through npm. See packages/cli/README.md.
Use in Claude Code (MCP plugin)
The agent-facing lane. zzop-mcp is a self-contained binary with an MCP server built in; you install it
once and then ask questions in plain language — the agent picks the tool.
/plugin marketplace add eezz4/zzop— then/plugin install zzop@zzop(two separate steps).Start a new session. The plugin downloads the binary for your platform on first run; nothing goes on
PATH. That first session does not list the zzop tools yet — the tool list is settled before the download finishes — so restart Claude Code once and they appear (the hook says so on stdout too). Once installed, a newer release is reported to you, never installed behind your back.
The server exposes the tools analyze_repo, cross_repo, check_file, check_endpoint,
check_coverage, module_map, analyze_envelope, validate_envelope, validate_rule_pack — plus the
zzop://contract/* resources
carrying the authoring contracts (among them the envelope schema, the DSL reference, the rule catalog,
the config surface and an annotated starter config), so an adapter or rule pack can be written with
nothing but the binary. zzop-mcp itself takes no analysis subcommands: bare or mcp serves stdio,
and version/help are the only other forms.
See packages/README.md for the full install/tool/resource reference, and docs/modules/mcp.md for exact argument shapes.
Use in a terminal or CI (zzop CLI)
The human-facing lane. Write a zzop.config.jsonc and run it, ESLint-style. A config is required —
every analysis lane refuses a tree that has none, on both binaries alike, because the names zzop would
otherwise guess about your project (what you call your auth guards, which banners mark your generated
files) live in that file, and a key you do not declare is a judgment zzop does not make:
zzop init # write the starter config — do this first, once per tree
zzop analyze . # analyze one repo/tree -> JSON findings summary
zzop analyze --config ci/zzop.config.jsonc # same, for a config that does not sit at the tree root
zzop analyze . --severity critical --limit 10 # narrow the findings LIST (counts always cover everything)
zzop cross --config zzop.config.jsonc # cross-layer join, driven by that config
zzop analyze . --fail-on critical # THE CI GATE: exit 3 when anything at/above that severity exists
zzop <subcommand> --help # that one subcommand's own line (exit 0); `zzop help` prints them allzzop --help is the canonical subcommand list — this README does not repeat it.
The exit code is 0 unless you ask otherwise. Without --fail-on, a run answers only "did zzop
run", so a tree full of criticals still exits 0; with it, findings at or above the named severity
exit 3 (a third code, so a CI log can tell a broken config on 1 apart from a real finding) while
the whole reply still goes to stdout. Check what your build does rather than this paragraph:
zzop analyze . --fail-on critical; echo $?. The full code table, including the 2 a provably
unmatchable --rule id lands on, is in
docs/getting-started.md.
A run writes into the tree it analyzes. The first analysis creates .zzop/cache/ beside your
config (the default cacheDir) and keeps the per-file analysis cache there — pure derived state,
safe to delete, regenerated on the next run, and it grows with the tree rather than staying small
(measure yours with du -sh .zzop; this repository's own is not a number worth printing here because
it moves with every run). zzop init adds the anchored **/.zzop/ line to that directory's
.gitignore for you; set "cacheDir": null to write nothing at all.
To track contract drift over time rather than at one instant, commit a structural manifest and diff a later run against it — the same shape a lint baseline file has, kept by you, not by zzop:
zzop manifest ./api ./web > contracts.json # identity only: provides/edges/bucket membership
zzop diff contracts.json contracts.new.json # read `transitions` first — a key that left `edges`
# for `unprovidedConsumes` is a broken contract
zzop facts ./api ./web > facts.json # post-assembly facts (per-tree CommonIr + the whole join,
# uncapped) for your own rule program
zzop graph ./api ./web > join.mmd # the cross-layer join as mermaid, for any renderer —
# scoped with --scope/--top, every cap disclosed in the file
zzop graph . --domain dep > imports.mmd # the FILE import graph instead — cycles drawn as hexagons
# with thick arrows, from the engine's own circular findings
zzop graph . --domain risk > risk.mmd # blast-radius hubs + extraction seams. The health SCORES
# are NOT drawn — a table of numbers is not a graph
zzop graph . --domain posture # the mutating attack surface and its guard status —
# a box means GUARDED-OR-EXEMPT, never proven guarded
zzop graph . --domain cochange > churn.mmd # which files keep changing TOGETHER, from git history —
# not imports, so it finds the coupling no dep graph can see
zzop graph . --domain dep --fold 2 # the SAME import graph with each path's first 2 segments
# drawn as one box — the module map you were trying to see
# IS the picture, and every edge says how many file edges
# it collapsed. --fold 1 for the top-level view
zzop map . --fold 2 # the same folding as DATA, not a picture: one row per
# module with its files, lines and the imports between
# modules. `--fold` is the only knob (NOT --depth, which
# is git's), and `moduleEdges` counts edges BETWEEN
# modules while `fileImports` counts every file-level oneA large repository takes minutes, and every command above writes nothing until it is finished.
There is no progress output: the reply is one document, assembled and then written, so a run you
interrupt gives you zero bytes rather than a partial answer — and while it works it looks
indistinguishable from a hang. Measured on this project's own corpus: a 3,800-file tree is seconds,
zzop facts over a 24,000-file monorepo is ~50s and 10.8 MB, and an ASP.NET-sized tree (17,000
files, 1.26M resolved import edges) is ~8 minutes for analyze. Redirect to a file and wait; if you
need a bound, wrap the call in your own timeout and treat a kill as no answer, never as a clean one.
The manifest is deliberately uncapped and carries no file or line, so a pure refactor diffs empty while
a route leaving the join cannot hide above a summary's caps. diff refuses two manifests from different
zzop builds unless you pass --allow-tool-drift (which then discloses the drift), and tags a removal
attributable to a source that lost coverage as blindnessSuspect rather than calling it a deletion.
That refusal gives the manifest a lifetime of one release. tool carries the release number, all
eight parser fingerprints and the engine fingerprint, so it moves on every release — including one that
changed nothing you use. A committed baseline therefore expires at your next upgrade, and re-running
zzop manifest to re-commit it is part of upgrading. Reach for --allow-tool-drift for a one-off
comparison you have decided to interpret yourself, not as a habit: it is also the only check that the
two files share a manifest FORMAT, since nothing else compares their row shape.
facts is the other uncapped lane, and the consumer half of the custom-rule extension point: when the
DSL cannot express your rule, zzop emits everything it knows after assembly and the cross-layer join —
each tree's whole CommonIr plus every join bucket, verbatim — and your own program decides what counts
as a problem. zzop neither runs your program nor reads its findings back; see
docs/modules/facade.md for the shape.
manifest, diff, facts, graph, explain and init are CLI-only lanes with no MCP tool twin.
The rest of the surface: analyze-envelope, validate-envelope, validate-rule-pack, endpoint,
file (everything zzop knows about ONE file — its tree, symbols, io facts, dependency edges both
ways, and every finding anchored there; its verdict says whether the file was ANALYZED, so an empty
findings list is never mistaken for "clean" on a file nothing structural ran on),
init (write the annotated starter zzop.config.jsonc; the same document MCP serves as the
config-template resource), contract, explain, version, help. See
packages/README.md for the full CLI and config reference.
To embed the engine instead of running either binary, call the zzop-facade/zzop-summary crates'
JSON-in/JSON-out contract directly — they are workspace-internal and not published to crates.io, so an
in-process Rust dependency means vendoring this workspace, not cargo add. Shelling out to the CLI's
JSON subcommands needs no linkage at all:
let report: serde_json::Value =
serde_json::from_str(&zzop_facade::analyze_json(r#"{"root":"."}"#)?)?;Result (abridged)
This is what the two binaries above actually print — findings is a census object, not an array,
because the reply is a shaped summary rather than a raw dump. The numbers below are a real run against
this repository's own cases/trees/api-be fixture, measured with a zzop built from this
checkout: zzop analyze --config cases/trees/api-be/zzop.config.jsonc.
Which binary you reproduce them with is part of the claim. zzop version prints the release
number alone, and main keeps that number between releases — so an installed @zzop/cli and a build
of this checkout can both answer 0.35.0 and legitimately report different findings, because they
are different builds of one version string. zzop version --verbose is what tells them apart: it
prints each parser's fingerprint and the engine hash. If your counts differ from the block below,
compare that line before assuming either side is wrong.
(To the next editor: these numbers move whenever cases/trees/api-be changes and whenever a
release changes what is measured — and that second one includes every rule change, not only the
loud ones. Re-run the command and re-measure; never patch one number in isolation. This block went
stale four times before a machine held it (2026-08-11, 08-15, 08-21, 09-02), and the fourth shipped
in v0.34.0 with every guard green. It is held now: scripts/measure/readme-result-block.sh builds a
zzop from this checkout, runs it, and compares every number in the block, so a partial edit
reds in CI instead of shipping. Do not hand-patch and trust the read — re-run it.
An outside reader reported 87/73 for this block on the same day and was NOT reading a stale README:
they measured with the published @zzop/cli 0.33.0, a different BUILD of the same version string,
and got a legitimately different answer. That is what the paragraph above this one is for. The
disclosure line below is deliberately no longer one of these numbers: see its own comment.)
pain never travels alone. painMeasuredWeight / painTotalWeight is how much of the weight table this
tree could actually be measured on, and pain: null means no metric had a population at all — absence of
data, never a clean bill.
And pain is not a defect score. It contains no rule findings whatever: the run below reports 101
findings, 7 of them critical, while its defect pain is 0. painByAxis splits the number so that is
visible instead of implied — defect (import cycles, the only entry), opinion (barrel discipline, FSD
layering, SDP/Main Sequence, Newman modularity, LOC ceilings — a project that deliberately does the
opposite is not wrong, it scores low), and history (rename churn, bus factor). The three sit on pain's
own scale and sum to it. Read findings for defects; read pain for how much zzop disagrees with how the
code is arranged.
{
"fileCount": 98,
"findings": {
"total": 101,
"bySeverity": { "critical": 7, "warning": 59, "info": 35 },
"byRule": { "security/weak-crypto": 6, "db/unawaited-write": 1 },
"shown": [ /* 50 here — the listed slice, capped by --limit; each entry has ruleId, severity, file, line, message */ ]
},
"architecture": { "pain": 7.5, "painMeasuredWeight": 13.8, "painTotalWeight": 18.6,
"painByAxis": [ { "axis": "defect", "pain": 0.0, "totalWeight": 3.0 },
{ "axis": "opinion", "pain": 7.4, "totalWeight": 15.0 },
{ "axis": "history", "pain": 0.0, "totalWeight": 0.6 } ],
"topRecommendation": null, "criticalTop": [],
/* + painMeaning / topRecommendationMeaning / criticalTopMeaning: the sentences
that say what each of the three above is, and is NOT, on the wire */ },
"coverage": { /* how much of the tree zzop actually saw, per extension */ },
"coverageGaps": { /* which principal extensions reached no resolved import edge, always present —
each row's `kind` ("source", "data-config", "unclassified") says whether the zero
means a missing parser or a filetype you have to open to judge */ },
"disclosure": { /* the census of zzop's OWN known silent-failure classes, keyed
classes / asserted / partial / notYetDetected, plus the `note`,
`command` and `resource` that lead to the full text. No counts are
copied here: the reply carries its own, and the two that were copied
here went stale while the two beside them stayed right — which is
indistinguishable from correct until someone re-runs it. Read them
with `zzop contract disclosure-classes`, the command the field itself
names. */ },
"warnings": [ /* anything this run could not provide */ ]
}Every finding carries a rule id, severity, a file:line location, and a message — the records
themselves ride in shown. That message is usually a POINTER rather than the rule's prose: a finding
whose text its own rule already owns carries a messageBy field instead, and you read the full text by
that finding's ruleId (zzop explain <ruleId>, or the MCP resource zzop://rule/{id}), which is
also where the config key that silences it is spelled. Check the messageBy field, not the sentence.
The reply says so itself in findings.messageByIdMeaning. bySeverity/byRule always count the WHOLE
run, so a --limit that shortens that list never changes them; that split is why findings is an object.
The per-metric scores block, the health object and recommendations are not on this wire: the
shaped summary folds them into the compact architecture object above. They exist in full only in the
zzop-facade embedding lane (zzop_facade::analyze_json, the snippet just above) — see
docs/modules/facade.md for that lane's own shape.
analyzeTrees (multi-tree) additionally returns crossLayerFindings — frontend fetch <-> backend
route joins — which has no single-tree equivalent.
How it works
Each repository is parsed into one language-neutral IR, so a Python route and a TypeScript fetch end
up as the same kind of fact. The headline move is the cross-repo join: frontend calls are exact-matched
against backend routes across the repo boundary, and the leftovers are named rather than dropped — a
casing or base-path difference, a version drift, a method mismatch each come back as a near-miss finding
naming the dimension that differs, instead of a diff you have to do by hand. A near miss is judged on
those axes, never on spelling: a plural or a typo (/api/userss against /api/users) is reported as an
unmatched call, not paired with the route it probably meant — the per-rule scope is in
the catalog. Alongside the join, the same engine runs a layered rule system (native
whole-graph analyses plus declarative JSON rule packs) over each repo individually, adding structural
findings, dependency/dead-code analysis, and health scores to the same JSON document.
Every run is deterministic — same code in, same findings out, byte-stable enough to diff two runs
against each other. That is what makes zzop usable as a CI gate (fail a PR on contract drift by reading
the JSON severity counts) and as a substrate an agent can re-run without chasing a moving answer. Just
as important, a run reports its own blind spots: warnings and the per-tree coverage census say what
did not run, so a short findings list can be told apart from a blind engine.
Full design: docs/ARCHITECTURE.md. Reading the output, severity semantics and
suppression: docs/getting-started.md. When your stack does not match the
defaults — a house extension, your own guard names, a gateway prefix, a rule that does not exist yet —
docs/extending.md lists every plug-in point in the order you hit them.
Supported languages
Language | Support |
TypeScript / JavaScript ( | Native, full AST (swc): symbols, imports, calls, HTTP routes/egress, TypeORM |
Python ( | Native, full AST (ruff, Python 3 — Python-2-only syntax falls back to lexical): symbols, imports, FastAPI route provides, Django URLconf route provides ( |
Rust ( | Native, full AST (syn 2): symbols, imports/ |
Go ( | Native, full CST (tree-sitter-go 0.25): symbols, imports/dep graph ( |
Java ( | Native, full CST (tree-sitter-java 0.23.5, Java 21 grammar): symbols (incl. nested types, dot-qualified method names, real visibility), imports/dep graph ( |
C# ( | Native, full CST (tree-sitter-c-sharp 0.23.5): symbols (incl. nested types, dot-qualified method names, |
Prisma schema ( | Native, lexical schema: models/fields (structural + usage-aware schema rules) + |
SQL ( | Native, lexical: |
Anything else (Ruby, JSP, ...) | Lexical fallback in-tree: the files are walked and counted, and only |
Full precision-tier breakdown — exactly what each native parser extracts, Python's v1 scope note, and
each parser's fingerprint — in docs/ARCHITECTURE.md. (Those
fingerprints are not in zzop version's default output, which prints the bare release number so scripts
can parse one token; zzop version --verbose — and zzop-mcp version --verbose, the identical string —
prints them, and zzop manifest's tool field carries the same string inside the artifact.)
Rust carries one reporting rule no other language has: a finding whose line sits inside a
#[cfg(test)]/#[test]-gated item is dropped, because Rust's unit tests live inside the shipping file
where the path-shaped test exclusion every other language relies on (foo.test.ts, tests/test_foo.py)
cannot see them. The credential-at-rest rules opt out and keep judging those regions — a committed key is
leaked whether or not the compiler keeps it — and each says so in its own catalog row. Both halves:
docs/rules/catalog.md.
A normal-sized file whose extension has no native parser also self-reports in the output's warnings
— naming the extension, a file count, and a path sample — instead of vanishing silently; point it at an
adapter (overlays: [...] in zzop.config.jsonc) if that language matters for the analysis.
Versioning & stability
zzop is pre-1.0 (0.x) and unstable — any release may change behavior, output, rules, or
defaults, so pin an exact version (not a ^/~ range) and re-test before upgrading. Semantic
Versioning begins at 1.0.0. What is promised before then is narrower, and is a promise about the
record rather than the rate: a break to one of the surfaces VERSIONING.md names is
written down, old and new spelling both, in CHANGELOG.md. VERSIONING.md is also
where the properties inside those surfaces that are still moving get named — rule ids,
SourceSymbol.id uniqueness, native id namespaces — rather than left for you to infer from a run.
Layout & development
Contributing, or just want the crate map? CONTRIBUTING.md carries both — the
per-crate responsibility list and the build/test/measure commands. They live there rather than here
because this page is read to DECIDE whether to adopt zzop, and a dependency graph answers a question
nobody asks before installing.
License
MIT — see LICENSE.
This server cannot be deployed
Related MCP Connectors
AI-powered codebase analysis — call graphs, security, dead code, complexity. 150+ tools.
Hosted code graph over MCP: exact callers, dependencies, and cross-repo blast radius for AI agents.
Repo intel for AI coding agents: overview, PRs, contributors, hot files, CI, deps. Remote MCP.
Ask a codebase what calls what: search, blast radius, paths between symbols, and diffs.
Related MCP Servers
- AlicenseAqualityCmaintenanceIndexes a mono-repo into a knowledge graph and provides MCP tools to query code structure—packages, components, routes, HTTP calls—without file reads or grep round-trips.722 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables coding agents to perform deterministic, model-free repository analysis and patch operations—tree-sitter repo maps, symbol/reference lookup, and SEARCH/REPLACE parsing/validation—via a CLI or MCP stdio server, without ever calling a language model.290 PyPI1MIT
- AlicenseNot gradedqualityCmaintenanceEnables people and AI agents to query local code repositories as graphs through stdio MCP tools for project listing, search, dependency tracing, impact analysis, bridge detection, node inspection, bounded code reading, and optional LSP definition/reference lookups.MIT
- AlicenseBqualityCmaintenanceIndexes a Python/JS/TS repository into a local SQLite knowledge graph so agents can trace callers, callees, imports, subclasses and covering tests, measure impact radius, rank diff risk, and summarize architecture without grepping files. Exposes these queries as MCP tools alongside an equivalent CLI, with no daemon, embeddings, or network access.10MIT