LeanGate
Integrates with NVIDIA Nemotron models via Nebius Token Factory to generate code rewrites and tests, and to resolve uncertain review or escalation decisions during code analysis.
Uses the PyPI JSON API to verify package existence and dependency facts, helping LeanGate check whether dependencies are needed or already covered.
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., "@LeanGatebefore I write a diff parser, check if this repo already has one"
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.
LeanGate
AI coding agents write code that runs but doesn't belong in your codebase. LeanGate is the referee inside the agent's loop.
It's an MCP server plus a pair of hooks, so it works with Claude Code, Codex, Cursor, or any other MCP client. Before the agent writes anything, it checks whether the repo already has what the agent is about to build. Right after every edit, it tells the agent what's wrong with the code it just wrote. And when the agent tries to finish, it blocks until the blocking issues are fixed. Generation and judgment run on NVIDIA Nemotron through Nebius Token Factory.
leangates.com · License: MIT · Python 3.11+
Checks Python codebases today; TypeScript and JavaScript are next. Changed files in other languages are listed in the report as not checked, never passed silently.
Claude Code, one step (needs uv): the MCP server and all four hooks.
/plugin marketplace add lambdabypi/leangate
/plugin install leangate@leangateAnything else: pip install "leangate[providers]" (or uvx leangate mcp to run the MCP server with no install),
then see Connect a coding agent. Put provider keys in ~/.leangate/.env.
Why
I gave two Nemotron models, Nano and Super, twelve tasks on Zulip, a real production Django app with about 2,000 Python files. Without LeanGate, three things kept happening.
They didn't know the codebase. Seven of the tasks asked for something Zulip already has a helper for, and both models rewrote all seven from scratch. One of them came out at 57 lines when a single call to
append_url_query_stringdoes the job.They did what the prompt pushed them toward.
shell=Truewith a name an admin types into a form, SQL built with an f-string,pickle.loadon a path the client sends,verify=False. Five safety tasks, five times the problem shipped.They invented internal APIs. Nano imported
zerver.http,zerver.lib.logandzerver.version. None of those modules exist, so those files crash the moment they're imported.
None of this trips a normal linter. The code parses, it reads fine in a diff, and a reviewer skimming forty agent-written files a day isn't going to catch an invented import.
With LeanGate in the loop (check_reuse before writing, one round of the per-edit hook's findings after), the same models did this:
Nano | Nano + LeanGate | Super | Super + LeanGate | |
Reused the repo's existing helper (of 7) | 0 | 5 | 0 | 5 |
Blocked by the gate (of 12) | 5 | 1 | 5 | 0 |
Safety tasks still carrying a watched issue (of 5) | 5 | 3 | 5 | 1 |
Lines written | 321 | 220 | 366 | 255 |
After one round of feedback the injection, TLS and shell problems were gone. What remained: Nano still loaded
pickle in one task (the one block left), and Nemotron's "missing validation" findings, which never block on their
own, stayed on the file-download task (both models) and the SQL task (Nano). Each model's whole 24-task run, both
conditions, coding agent plus LeanGate, cost $0.018 (Nano) and $0.030 (Super). It's twelve tasks with one run each,
so read it as direction rather than a precise number. The method, raw output and caveats are in
bench/RESULTS.md.
And the bigger model didn't close the gap. Super missed the same seven helpers Nano did.
A full coding agent closes most of it. Run the same way on 12 more public repos with Claude Code (Haiku 5.5), which
searches the repo and refuses most unsafe shortcuts on its own, LeanGate made no difference to reuse (10 of 12 both
ways). On safety it stopped the two unsafe changes Claude did write (eval of user input, verify=False) and handed
them back to the user with the safe version, where Claude alone reported them as done. It also exposed false
positives and a hole: the agent could waive its own security findings. That's closed now (only a person can waive one,
below):
bench/RESULTS_claude.md.
Related MCP server: CodePeel MCP Server
Try it
After setup (no keys needed: every provider has an offline fallback):
python demo/run_demo.py # clean repo → agent's bloated change → hook blocks → fix → minimize → numbers card
leangate verify # check your own working tree (exit 1 when blocked)
leangate reuse "parse a unified diff"
leangate dep requests --purpose "HTTP calls"
leangate-service # web playground at http://127.0.0.1:8000/
pytest # the full suite (about 240 tests), fully offlineDemo output (offline mode, abridged):
hook decision: block (1.76s)
pyproject.toml:5 unneeded_dependency: Dependency `requests`: use_existing. Already covered by httpx.
app/billing.py:3 unused_import: `os` is imported but never used.
app/billing.py:8 unused_import: `requests` is imported but never used.
app/billing.py:13 over_abstraction [review]: Abstract `InvoiceSink` has 1 implementation(s) ...
app/billing.py:26 duplicate [review]: `retry_request` looks like a reimplementation of `with_backoff` (similarity 0.68)
app/billing.py:36 stdlib_replaceable [review]: `chunk_invoices` hand-rolls what itertools.batched provides (Python 3.12+)
app/billing.py:40 dead_code: Private function `_format_invoice` is never called anywhere in the repo.
...
gate: block
kept: redundant_check on sync_invoices.invoices (app/billing.py:45), keep
...
findings: 7 confirmed, gate block
lines: app/billing.py 51 -> 26 after the fix (25 removed)
dependencies avoided: 1 (requests)The card ends with per-tier Nemotron calls, latency and tokens when Token Factory is live. demo/layout.ps1 is an internal helper that opens the psmux layout used to record the demo video.
How it works
flowchart LR
A[Coding agent] -- "check_reuse (before writing)" --> L
A -- "verify_lean (before finishing)" --> L
E[Per-edit hook<br/>PostToolUse] -- "findings after each edit" --> L
H[Stop hook] -- blocks finish --> L
subgraph L[LeanGate MCP server - local]
I[Repo index<br/>tokens + AST shape]
S[Static checks<br/>bloat, errors, security, imports]
M[Mem0 memory]
end
L -- "fixed-answer decisions" --> J[Jev<br/>typed decisions]
J -- "review / escalate" --> N[Nemotron Nano / Super<br/>Nebius Token Factory]
L -- "minimize / ground / deps" --> R
subgraph R[Verification service - Nebius]
W[Nemotron rewrite] --> P[Jev pre-check] --> X[Sandbox: same tests,<br/>both versions]
T[Tavily: official docs<br/>at pinned version]
Q[PyPI + OSV.dev]
endTwo checkpoints, not streaming. Half-written code always looks bloated (helpers not yet called, imports not yet used), so LeanGate judges complete units:
Before writing: at session start, the agent is told the repo's house helpers: the functions the repo routes outbound HTTP, subprocesses, YAML, unpickling and dates through (Sentry's
safe_urlopen, Django'stimezone.now), learned from the index by following calls and counting callers. A grep for the task's words never finds them.check_reuse("retry HTTP calls with backoff")→adapt: with_backoff (app/utils/retry.py:11)is there for a general-purpose helper the agent's own search didn't find.Before finishing:
verify_lean()reads the working tree vsHEADand returnsgate: block | passwith findings.
Between the two, the per-edit hook hands the agent each file's new static findings right after it writes the file. The Stop hook enforces checkpoint 2: the agent can't end its turn while blocking issues remain, and it runs on a fast path (static analysis + Jev, about 1.5 s) so it never slows the loop.
Who does what
Role | Component |
Judges every fixed-answer question: is this a duplicate? keep or remove this guard? do the docs support this? block the commit? | Jev (TypeSafe's decision model, via OpenRouter's Decisions API) |
Writes rewrites, generates tests, and settles decisions Jev isn't sure about | NVIDIA Nemotron on Nebius Token Factory |
Grounds claims in official docs, version-aware, with citations | Tavily |
Facts that need no search: does the package exist, which CVEs | PyPI JSON API, OSV.dev |
Remembers rejected findings and project conventions | Mem0 |
Proves a rewrite is equivalent by running the same tests on both versions | sandbox in the Nebius-hosted service |
Confidence routing (cheap first, big models only when needed)
Jev returns a typed choice with a confidence, which maps to an action:
Jev action | Confidence | What happens |
| ≥ 0.85 | Accept Jev's answer. Most calls stop here. |
| 0.60–0.85 | Nemotron Nano re-decides ( |
| < 0.60 | Nemotron Super re-decides ( |
Static analysis and Jev count as two independent pieces of evidence. When they agree, their confidences
compound (static 0.92 + Jev 0.71 → 0.98) and the call is accepted with no model hop (jev+static). When they
disagree (jev-disputed), Nemotron settles it. Jev is also asked a specific question for each kind of finding,
with what "yes" and "no" mean, plus the module or the original function it needs to see. On the demo change this took Jev
from 0.51–0.77 to 0.55–1.00 confidence and Nemotron decision calls from 7 to 0 (full verify 6.3 s → 3.1 s);
Nemotron's job there becomes disputes, rewrites and test generation.
Why Nano reviews: replaying the demo's 8 uncertain decisions through every tier, Nano gave the same answer as Super on all 8, at about the same latency (~1 s on Token Factory) as the smallest tier. Ultra disagreed with both on a clear duplicate, so it isn't the default top judge; it still generates tests.
Paying for inference once
Model calls are cheap; asking the same question twice is the waste. Three rules keep the bill and the wait down:
Decision cache. A model's answer is stored per repo, keyed by the question, the code it's about and the models in use. The Stop hook asks again every time the agent tries to finish, so a repeat run on unchanged code makes no model calls at all. Changed code, or a different model, is asked again.
LEANGATE_DECISION_CACHE=0turns it off.No model for what can't block. A finding under the block bar (an unused import, a style simplification) is reported on its static evidence alone. Nemotron's "what's missing" items are the exception: Jev always rules on those.
LEANGATE_CONFIRM_MINOR=1sends everything to Jev, as before.Nano proposes "what's missing", Super settles disputes. The review runs on Nano (
LEANGATE_MISSING_TIER), every file's review runs at once, and only an item Jev disputes goes on to Super.
Measured on Zulip with every provider live: 11 agent-written modules (320 lines, from the benchmark), a full verify.
Before | After, first run | After, repeat run | |
Full verify (Jev, Nemotron, Tavily) | 12.7–23.2 s, $0.0024–0.0027 | 7.4 s, $0.0012 | 1.45 s, $0, 13 of 13 answers cached |
Fast path (the Stop hook) | 1.4–2.5 s, $0.0002 | 1.4–2.4 s, $0.0002 | 0.93 s, $0, 8 of 8 cached |
Most of the old full verify was the "what's missing" review running one file after another on Super (13 of 23 s). The live dashboard shows this breakdown for every run (below).
Learning each repo
LeanGate keeps a profile per repo (profile.json next to its index; nothing crosses between repos) and gets better
and faster with every run in it:
Reuse map. When new code does a job by calling a project helper, whether LeanGate suggested it or the agent found it, the job is remembered with that helper, under the new function's words and the words of the task the agent was given. It reaches the next agent three ways, none needing a model call: with its task (the prompt hook names the helpers earlier changes used for a task like it), in
check_reuse(on Superset, a reworded "make a chart title safe as a file name" went from "write new" in 1.5 s with 5 Jev calls to "reusesanitize_filename" in 0.27 s with none), and inverify_lean(new code redoing a job learned twice is flagged as a reimplementation). On the fresh-repo benchmark, agents given the hint reused the helper in 18 of 19 runs, against 14 of 19 for the same tasks with LeanGate's memory wiped, in 5 turns instead of 8 (RESULTS_fresh.md).Calibration. Each rule's outcomes here (confirmed, dismissed, fixed by the agent, rejected by a person) are counted. A rule the decider always confirms stops being asked about; a bloat or error-handling rule people keep rejecting becomes a hint. Security rules are never relaxed by learning.
Precedents. Confirmed findings are retrieved by similarity and handed to the what's-missing review as "issues confirmed before in this repo" (retrieval over the repo's own history, no model training).
The repo's endpoint habit. LeanGate reads the existing endpoints and learns how they check permissions (Superset: 328 of 337 use
@protect/class_permission_name; edx-platform: 73%permission_classes). Where most do, a new endpoint without a check is flagged; where most are public, nothing is. On the benchmark's code it fired only where the blind reviewer also found missing authorization.
The dashboard's "What LeanGate has learned here" card shows all of it.
Every decision in a report carries its source (e.g. jev, jev>nemotron-super, static) and whether it came
from the cache, so you can see which model made each call.
Findings the decider chose to keep (a guard on a public function, say) come back in the report's kept list, with the decision that kept them.
What it detects
Three groups: code production doesn't need (bloat), code it can't survive (error handling, security), and code that won't even import (invented internal APIs). Fewer lines was never the goal. Validation, retries, error handling and logging that protect production are kept, and a dedicated "keep or remove" decision guards exactly that.
Kind | Example | Default severity |
|
| 0.8 (blocks) |
|
| 0.7 (blocks) |
| unreachable statements; private functions nothing calls | 0.6 (blocks) |
|
| 0.65 (blocks) for HTTP, subprocess, YAML or pickle when at least half the repo's call sites use the helper; else 0.5 |
|
| 0.5 |
| an | 0.5 |
| a function that only forwards its arguments (not one that forwards to a project helper: that's reuse) | 0.4 |
|
| 0.4 |
| 0.3 | |
| redundancy inside functions, via Ruff (SIM, PIE, PERF, C4, UP): | 0.3 |
Beyond bloat, LeanGate flags code production can't survive:
Category | Kinds | Examples | Blocks when |
Error handling |
|
| severity ≥ 0.6 and the decision is ≥ 0.6 confident |
Security |
| f-string SQL; | severity ≥ 0.6 and the decision is ≥ 0.85 confident |
What's missing (Nemotron) |
| an admin route with no access check; a parameter that reaches the filesystem unchecked | never on its own: Nemotron proposes, Jev must confirm |
Security checks use Ruff's port of Bandit's tests (same S/B numbering, ~30 ms per file versus ~500 ms to import Bandit). "What's missing" only reviews changed functions that take outside input (routes, CLI commands, parameters that reach SQL, a shell, or the filesystem), and only on a full verify, never in the Stop hook.
Grounding (Tavily)
Model knowledge is stale and version-blind. Before LeanGate suggests "use itertools.batched", it:
reads the repo's pinned Python and dependency versions (
pyproject.toml,requirements*.txt,.python-version)searches official domains only (docs.python.org, peps.python.org, PyPI, Read the Docs, GitHub) with the version in the query
has Jev judge the excerpts:
supported | contradicted | unclear. A contradicted claim drops the finding.returns citations with the finding, and caches by claim + version
Web text only reaches a judge that must answer with a fixed option key. It is never pasted into a rewrite prompt, which limits prompt-injection risk.
MCP tools
Tool | When the agent calls it |
| optional: before writing a general-purpose helper its own search didn't find; also lists the house helpers the intent touches |
| before adding a package |
| when the change is done (default: working tree vs HEAD) |
| to get a smaller, behavior-identical module; rejected if tests disagree |
| run the same tests against two versions |
| a rejected finding is never raised again; conventions in |
| only when the user asks to see it: opens the local live dashboard (below) on this repo |
Setup
Requires Python 3.11+ and git. To use it: pip install "leangate[providers]", and keys in ~/.leangate/.env
(a repo's own .env may supply provider keys too, but never endpoints: a cloned repo can't redirect your key and
code to its server). To work on LeanGate itself:
git clone https://github.com/lambdabypi/leangate && cd leangate
python -m venv .venv
# Windows: .venv\Scripts\activate macOS/Linux: source .venv/bin/activate
pip install -e ".[all]"
cp .env.example .env # add keys; any missing key falls back to an offline mode
python scripts/list_models.py # needs NEBIUS_API_KEY: lists the Nemotron model IDs your key can useKeys: NEBIUS_API_KEY (Token Factory), OPENROUTER_API_KEY (Jev via OpenRouter; TYPESAFE_API_KEY also works for TypeSafe directly), TAVILY_API_KEY, MEM0_API_KEY. With no keys at all, everything still runs: decisions use static-analysis confidences (so weak heuristics land in review, never auto), grounding uses curated doc links, and memory is a local JSON file. Set LEANGATE_MOCK=1 to force this.
Connect a coding agent
Claude Code:
claude mcp add leangate --scope user -- /absolute/path/to/.venv/bin/leangate-mcp # Windows: ...\.venv\Scripts\leangate-mcp.exe
leangate --repo path/to/your/repo hooks # shows what it would add; add --write to save itUse the absolute path to leangate-mcp inside the venv you installed into: Claude Code starts the server itself,
outside your activated shell, so a bare leangate-mcp only works if that venv happens to be on its PATH. The hooks
command writes absolute paths for the same reason.
The second command adds four hooks to that repo's .claude/settings.local.json (personal; it holds this machine's
paths), keeping any settings already there:
Hook | When | What the agent gets |
| when the session starts | the repo's house helpers (HTTP, subprocess, YAML, dates...), so it calls them instead of the library; nothing if the repo has none |
| when the agent gets a task | the helpers earlier changes in this repo used for a task like it (the reuse map, below); nothing if none match |
| right after each file the agent edits | that file's new issues from the static checks, as context, in milliseconds; each issue reported once; never blocks |
| when the agent tries to finish | the gate: full check with Jev; it can't finish while blocking issues remain |
So the agent hears about a duplicated helper or a missing timeout right after writing it, not after it has built three more files on top. Checks can't run mid-generation (no tool sees inside a model's output until an edit lands, and half-written code doesn't parse), so the per-edit hook is as early as code can be checked. The session hook covers the step before writing, without costing the agent a tool call.
Waiving a finding. The agent can reject a wrong bloat or error-handling finding with record_feedback. It can't
waive a security finding: on the benchmark, Claude dismissed its own and the gate passed. It's told to stop and explain
to the user instead, and its reason is kept. The user runs leangate waive (no arguments lists what agents asked for)
or leangate waive <signature> --note "why", which suppresses it locally and writes .leangate/waivers.json. Commit
that file and CI and teammates honor the waiver too. LeanGate reads the file from HEAD, so an agent editing it
changes nothing until someone commits it, and the command refuses to run inside a Claude Code session. The hook entries are also in examples/claude-settings.json.
Codex: append examples/codex-config.toml to ~/.codex/config.toml.
Any MCP client: see examples/mcp.json.
Paste examples/AGENTS.md into your repo's AGENTS.md / CLAUDE.md so the agent knows when to call each tool.
In your editor (VS Code, or any LSP client)
leangate lsp is a language server. It checks the buffer as you type, with static checks only: no network, about
125 ms per debounced keystroke on the demo file. On save it confirms each finding with Jev (static + Jev, the
Stop hook's fast path; set leangate.escalate to add Nemotron and Tavily). Severity follows the category: bloat is
Information or Hint, error handling is a Warning, security is a Warning or an Error. Hover shows the reason, the
decision trail (Static -> Jev agreed, or Nemotron Super -> Jev agreed for what's-missing findings) and
citations. Quick fixes: remove an unused import, and replace a duplicate's body with a call to the existing function.
pip install -e ".[editor]"
cd editors/vscode && npm install && npx @vscode/vsce package --allow-missing-repository
code --install-extension leangate-0.1.0.vsix # or Extensions > ... > Install from VSIXThe extension isn't on the Marketplace; build it locally as above. Settings, troubleshooting (LEANGATE_LSP_LOG=<file>)
and other editors: editors/vscode/README.md.
Live dashboard
Every verify_lean and check_reuse run, whether from the MCP server, the Stop hook, the CLI or the editor, appends
one event to ~/.leangate/<repo-hash>/events.jsonl. The file rotates at 2 MB and keeps one old copy. leangate dashboard
serves a live view of those events on 127.0.0.1 only (port LEANGATE_DASHBOARD_PORT, default 8765): the latest report
grouped into Security, Error handling and Bloat, each finding's decision trail and citations, what was kept on purpose,
and how much was checked. Every location opens in VS Code (vscode://file/...). The agent's show_report tool, or
LeanGate: Show Live Report in VS Code, starts the dashboard if needed and opens it. Nothing else opens a browser tab.
The dashboard only reads files LeanGate already wrote. It calls no provider and isn't part of the Nebius service.
Time and cost per run. Every run records where its time went, from the moment LeanGate starts: building its clients, loading the index, finding the changes, static checks, the "what's missing" review, Mem0, confirming findings, and the gate. It also records each provider's calls, time and estimated cost, and how many answers came from the cache. The dashboard charts the repo's recent runs (bars split by phase, with LeanGate's cost as a line; click a bar to open that run), and each report opens with that run's breakdown.
Mission control. The Agents panel at the top shows one row per agent, for this repo or across all of them: Claude Code, Codex, Cursor and VS Code's agent are told apart by the name each sends when it connects to the MCP server, and hook, editor and CI runs are labelled too. For each one: runs, how many changes the gate blocked, issues caught right after an edit, unique issues by category, invented imports, how often LeanGate pointed it at existing code, and the estimated cost of LeanGate's own model calls. A team running several agents gets one standard and one view across all of them. Each agent's own usage is billed by its vendor and isn't shown.
Run the verification service on Nebius
docker build -t leangate-service .
docker run -p 8000:8000 --env-file .env leangate-service
# open http://127.0.0.1:8000/ for the web playground, /docs for the APIStep-by-step deploy as a Nebius Serverless Endpoint or on an AI Cloud VM: DEPLOY.md.
Create the endpoint with --auth none so judges can open the playground; the engine routes stay protected by
LEANGATE_SERVICE_TOKEN. Then point local MCP servers at it:
LEANGATE_REMOTE_URL=https://<your-endpoint> LEANGATE_SERVICE_TOKEN=<token>The repo index, static analysis, and memory stay on the developer's machine. Only the snippets being rewritten or grounded are sent to the service.
Web playground
The service serves a single-page playground at / (plain HTML/CSS/JS, no build step) for people who don't want to install anything:
Verify: paste files (or load the bundled "agent's bloated change"), get the gate, findings grouped by kind, the decision trail per finding (static → Jev → Nemotron), Tavily citations, and what was kept on purpose.
Reuse search: describe what you're about to write; see existing candidates and a reuse / adapt / write_new call.
Minimize: before/after diff and the sandbox equivalence result.
A provider strip from
/healthshows which of Nemotron (Token Factory), Jev, Tavily, and Mem0 are live.
Behind it, POST /api/verify ({files, baseline?, pyproject?, escalate?}), POST /api/reuse and POST /api/minimize build a throwaway git repo per request (baseline committed, files on top) with its own LeanGate state, and delete both afterwards. Limits: 40 files, 200 KB per file, 1 MB per request, 30 requests/min per IP, and 6/min for /api/minimize, which runs code. /api/* is open by default; set LEANGATE_PLAYGROUND_TOKEN_REQUIRED=1 to put it behind the service token, or LEANGATE_PLAYGROUND_SANDBOX=0 to keep the playground from running submitted code at all. The engine routes answer 503 until LEANGATE_SERVICE_TOKEN is set.
Sandbox. Each test run is a pytest subprocess in a fresh temp dir with a wall-clock timeout, a capped output file, a stripped environment (no API keys), and a Python-level network block. On Linux it adds CPU, memory, file-size, open-file and process-count rlimits, kills the whole process group, tries an empty network namespace, and marks the server process non-dumpable so test code can't read its environment from /proc. On Windows only the timeout, output cap and Python-level block apply. This is defense in depth, not a security boundary on its own; see what the sandbox does and doesn't protect.
CI, GitHub Action, pre-commit
GitHub Action
Block PRs that add bloat, missing error handling or security problems. Runs offline, no keys needed. Uses the fast path (static analysis + Jev) by default.
# .github/workflows/leangate.yml
on: pull_request
jobs:
leangate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # LeanGate diffs base...HEAD, so the merge-base must be present
- uses: OWNER/leangate@v0.1.0
with:
fast: "true" # "false" adds Nemotron + Tavily-grounded checks
# block-severity: "0.7" # default 0.6
# fail-on-block: "false" # report only
# remote-url: ${{ vars.LEANGATE_REMOTE_URL }}
# remote-token: ${{ secrets.LEANGATE_SERVICE_TOKEN }}Findings show up as annotations on the changed lines and as a table in the job summary. The job fails when LeanGate blocks. Outputs: gate (pass/block), findings, report (path to the JSON report).
pre-commit
# .pre-commit-config.yaml
repos:
- repo: https://github.com/OWNER/leangate
rev: v0.1.0
hooks:
- id: leangate-verifyThis runs leangate verify --fast --staged on what you're about to commit: staged changes only, so untracked scratch files don't count. It skips commits that don't touch .py, pyproject.toml or requirements*.txt.
CLI in any CI
leangate verify --base origin/main --fast # PR diff (base...HEAD); dependencies compared with the merge-base
leangate verify --staged --fast # what's staged
leangate --json verify --diff change.diff # a diff file, JSON reportExit codes: 0 pass, 1 blocked, 2 usage or environment error (not a git repo, no merge-base).
This repo's own CI (.github/workflows/ci.yml) runs the test suite on Ubuntu and Windows, Python 3.11 and 3.12, offline.
At production scale
Measured on a shallow clone of django/django (2,933 files, 527k lines, 9,167 candidate functions; Windows 11, Python 3.13):
Before | Now | |
Cold index build | 26.0 s | 9.1 s |
Warm load in a fresh process (what the Stop hook pays) | n/a | 0.63 s |
| 44 / 46 ms | 0.8 / 2.8 ms |
| 848 / 1939 ms | 16.5 / 27 ms |
The index is an exact inverted index (results identical to the old linear scan, checked on 60 query/setting combinations on django) with branch-and-bound pruning for the AST-shape score, persisted under the per-repo state dir and invalidated per file by mtime and size.
False positives on real code. With every line of httpx, rich and click (64k lines) marked as "added", duplicate findings fell from about 5.6 to 0.57 per 1,000 lines after tuning, and unused-import, unreachable-code and stdlib findings on those libraries went to zero (all were false positives). Recall: 19 of 20 planted agent-style rewrites of real helpers are caught, including ones that hard-code the original's parameters. The miss is a one-liner, which the stdlib check flags instead.
The same test for the error-handling, security and in-function checks (60k lines of httpx, rich and click, static
only): all detectors together went from 3.06 to 1.41 findings per 1,000 lines after one round of tuning. Ruff's RET
rules (else after return) were 100 of 123 redundancy findings and were dropped as style, and one-statement
capability probes (try: return stream.isatty() / except Exception: return False) no longer block. The SQL, TLS,
secret, eval, timeout and open-file checks found nothing in those mature libraries, and catch every planted case
in the tests.
How we used NVIDIA Nemotron and Nebius
Nemotron on Nebius Token Factory does every generative step, through Token Factory's OpenAI-compatible API (src/leangate/llm/nemotron.py):
Nano settles decisions Jev marks
review(the common case, so the cheapest tier does it).Super writes behavior-preserving rewrites in
minimize, and settles decisions Jev marksescalate.Nano is the "what's missing" reviewer: on a full verify it reads each changed function that takes outside input and names the access check, validation or error handling it lacks, which no static rule can see. Jev must confirm each item, and one Jev disputes goes to Super (src/leangate/analysis/missing.py).
Ultra generates pytest suites when the caller provides none.
Nano and Super as the coding agent in the benchmark: both wrote every module, with and without LeanGate, so the comparison runs entirely on Token Factory.
Nebius AI Cloud / Serverless: the verification service (src/leangate/service/app.py, Dockerfile) runs untrusted code in the sandbox inside the Nebius-hosted container, not on the developer's machine.
Project status
Known limits:
Python only.
Duplicate detection is static. A copy that swaps an injected dependency for a concrete library (
self.session.getvsrequests.get) reads as a deliberate sibling and is missed; one-implementer ABCs that are public extension points in a library are still flagged.The sandbox is defense in depth (timeouts, rlimits, network block, stripped env), not a security boundary. Inside Docker the network block is likely Python-level only, and test code runs as the same user as the server, so a hostile submission can restart it. Real isolation needs a separate network-less sandbox container; until then,
LEANGATE_PLAYGROUND_SANDBOX=0keeps the public playground from running code. Run untrusted code in the Nebius-hosted service, not on a laptop.Rejections recorded with
record_feedbacklive in~/.leangate, so CI doesn't see them. Waivers made withleangate waiveand committed in.leangate/waivers.jsonare shared.House helpers are found for a fixed set of call families (HTTP, subprocess, YAML, pickle, date parsing, the current time), through module-level functions only. When a repo also wraps the library in client classes (Sentry's API clients), the classes' own calls count as direct calls, which lowers the helper's adoption and keeps its finding a hint.
License
Available Tools
7 toolscheck_dependencyA
Call BEFORE adding a package. Checks it exists on PyPI (catches hallucinated names), known
vulnerabilities (OSV.dev), maintenance, and whether something the project already has covers purpose.
Returns recommendation: add | use_existing | avoid.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | ||
| repo | No | ||
| purpose | No | ||
| version | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations, so description carries the burden well: discloses data sources (PyPI, OSV.dev), the anti-hallucination purpose, and the return recommendation values (add | use_existing | avoid). Doesn't cover error behavior or rate/network limits, keeping it below 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, front-loaded with the trigger condition, then what it checks, then the return values. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers purpose, data sources, and output recommendation shape, but with no output schema and 0% schema coverage on 4 params, it leaves repo/version semantics and the response structure under-explained. Adequate but with clear gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% and 4 parameters exist, but the description only meaningfully implies `purpose` (when it says 'something the project already has covers purpose') and `name` (the package). `repo` and `version` are not explained at all, so significant gaps remain.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (check) and resource (dependency) plus the exact checks performed: PyPI existence, vulnerabilities via OSV.dev, maintenance, and existing coverage. Clearly distinguishable from siblings like check_reuse and verify_lean.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly says 'Call BEFORE adding a package', giving a clear timing/usage anchor. Doesn't name a specific alternative or exclusion scenario, but the trigger condition is clear enough.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_reuseA
Optional, before writing a general-purpose function or helper your own search didn't find.
Describe what you are about to build in intent (e.g. "retry HTTP calls with exponential backoff");
optionally pass your draft in code. Returns the closest existing symbols, each with a decision:
reuse | adapt | write_new; the repo's own helpers for library calls the intent touches (house_helpers);
and project conventions. write_new means none of these few matches fits, not that the repo lacks it.
| Name | Required | Description | Default |
|---|---|---|---|
| code | No | ||
| repo | No | ||
| intent | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses the return shape (matches with reuse/adapt/write_new decisions, house_helpers, project conventions) and adds a crucial caveat that 'write_new means none of these few matches fits, not that the repo lacks it,' warning of bounded results. It omits any note on permissions or scope selection, but for a read-style lookup this is solid disclosure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the usage trigger, then the parameters, then the return values. Sentences are dense but each carries meaning, including the important write_new caveat. Slightly packed with semicolons but no wasted prose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description appropriately explains return values, and it covers the primary `intent`/`code` usage. However, it omits any explanation of `repo` and how the search scope is determined, leaving a gap for a 3-parameter tool with 0% schema coverage.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It explains `intent` well with a concrete example and describes `code` as an optional draft, but the `repo` parameter is never mentioned anywhere in the description or schema, leaving one of three parameters undocumented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description implies a specific function: finding existing symbols matching an intended new helper/function before writing code. The return statement ('closest existing symbols, each with a decision: reuse | adapt | write_new') makes the operation concrete. It doesn't explicitly contrast itself with siblings like check_dependency, but the resource and verb are clear enough.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states the trigger condition explicitly: 'Optional, before writing a general-purpose function or helper your own search didn't find.' This tells the agent when to invoke it (prior to authoring a helper, after a failed search) and signals it is optional. No explicit when-not or named alternatives, but the context is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
minimizeA
Return a smaller version of a Python module with identical behavior.
The rewrite (Nemotron) is pre-checked (Jev) and then run against tests in a sandbox. Tests must
import from a module named target. Without tests, tests are generated. A rewrite that changes
behavior is rejected and the original is returned.
| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | ||
| repo | No | ||
| tests | No | ||
| findings | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden and does so well. It discloses the validation pipeline (pre-check, sandbox execution against tests), the requirement that tests import from a module named `target`, automatic test generation when tests are absent, and the rejection-and-return-original fallback for behavior-changing rewrites.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is appropriately sized and front-loaded: the core purpose comes first, followed by the operational workflow and fallback behavior. Every sentence adds useful information about how the tool works, with no wasted preamble.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the lack of annotations, output schema, and schema descriptions, the description is only moderately complete. It explains the core behavior and validation fallback well, but it omits parameter semantics for most inputs and does not describe the return shape beyond saying a smaller version or the original is returned.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate for four undocumented parameters. It partially explains the `tests` parameter through the testing workflow and mentions the `target` module requirement, but it does not explain `code`, `repo`, or `findings` at all, leaving most parameter semantics unclear.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: return a smaller version of a Python module with identical behavior. This is clear enough for an agent to understand the tool's core purpose. However, it does not explicitly distinguish this tool from siblings such as prove_equivalent or verify_lean, so it falls short of the top score.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the purpose statement and the description of the rewrite/test/reject workflow, but there is no explicit when-to-use guidance, no exclusion conditions, and no mention of sibling alternatives. The agent can infer that this tool is for minimization requests, but the description does not help choose between it and related tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
prove_equivalentC
Run the same tests (importing from module target) against two versions of a module. equivalent is null
when there were no tests to run (none given, and none could be generated): unproven, not shown to differ.
| Name | Required | Description | Default |
|---|---|---|---|
| tests | No | ||
| original | Yes | ||
| minimized | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must carry the full behavioral burden. It only describes the null-case meaning of the `equivalent` result and does not explain what happens on non-null results, whether tests can be generated, execution side effects, or any preconditions.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single focused sentence with a clarifying note about the null case. It is front-loaded and contains no filler, though the sentence is somewhat dense and could be split for readability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given three parameters with 0% schema coverage, no output schema, and no annotations, the description is incomplete. It leaves the meaning of the two required parameters and the broader output behavior undefined, which makes correct invocation difficult.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description should compensate but mostly does not. It only mentions `tests` as possibly absent and by implication of null result, but it never explains what `original` and `minimized` are or how they should be supplied.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description says it runs the same tests against two versions of a module, which gives a rough sense of a differential test operation. However, the exact relationship between the tool name (prove_equivalent), the parameters (original, minimized, tests), and the output (equivalent) is not stated clearly, and it is not distinguished from sibling tools like minimize or verify_lean.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit guidance on when to use this tool versus siblings such as minimize or check_reuse. The only hints come from the explanation of the `equivalent` result being null, which implies a scenario but does not tell the agent when to choose this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
record_feedbackA
Tell LeanGate whether a finding was right. A rejected finding (accepted=false) is never raised again,
except a security finding: only a person can waive one (leangate waive), so tell the user why you think it's
wrong instead. If note states a project convention (e.g. "we always validate at API handlers"), it is
remembered.
| Name | Required | Description | Default |
|---|---|---|---|
| note | No | ||
| repo | No | ||
| accepted | Yes | ||
| signature | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does substantial work: a rejected finding is never raised again, security findings are exempt from that suppression, and `note` text asserting a convention is persisted and remembered. It still omits what an accepted=true does, whether feedback is scoped per repo, and how the signature is obtained.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded, and the subsequent sentences add genuine constraints rather than filler. The security-waiver sentence is dense but earns its place; the parenthetical example for `note` adds clarity at low cost.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a four-parameter mutation with no annotations and no output schema, the description covers the key suppression rule and the security exception well, but leaves the required `signature` identifier and the `repo` scoping undefined, and never describes the result of a successful call.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must define all four parameters. It gives real meaning for `accepted` (rejection semantics) and `note` (convention text is remembered), but `signature` — a required parameter — is never explained, and `repo` is not mentioned at all. Half the parameters remain semantically opaque.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence gives a specific verb and resource: report to LeanGate whether a finding was right. No sibling (show_report, verify_lean, minimize, etc.) overlaps with feedback submission, so the purpose is unambiguous by construction, though the description never explicitly differentiates itself from those tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states the trigger condition implicitly (you have a finding and a verdict) and, crucially, an exclusion: security findings cannot be rejected here and must be waived by a human via `leangate waive`. That when-not guidance is stronger than typical, but there is no explicit routing to an alternative tool for that case.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
show_reportA
Open LeanGate's live dashboard (127.0.0.1 only) in the browser, showing the latest reports for this repo. Call it only when the user asks to see the report. It starts the local dashboard if it isn't running.
| Name | Required | Description | Default |
|---|---|---|---|
| repo | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses that the dashboard is bound to 127.0.0.1 only, that it launches a browser, and that it will start the local dashboard if it is not already running (a side effect an agent should know about). It omits what the call returns or whether it blocks, which keeps it out of 5 territory.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, zero filler. The primary action is front-loaded and the invocation constraint follows, which is the correct ordering for an agent scanning for a trigger.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple, single-optional-parameter tool with no output schema and no annotations, the description covers the action, the environment constraint, the side effect, and the usage trigger. The only real gap is what the caller receives back (a URL, a status, or nothing), which is minor here.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% for the single 'repo' parameter, so the description must compensate. It only hints at the parameter via 'for this repo', leaving unclear whether the value defaults to the current repository or must be supplied explicitly, and what form a repo identifier takes.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: it opens LeanGate's live dashboard in the browser and shows the latest reports for the repo. This is plainly distinguishable from every sibling (verify_lean, minimize, prove_equivalent, check_dependency, etc.), which are all analysis operations rather than a UI-opening action.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit trigger condition: 'Call it only when the user asks to see the report.' That is a real when/when-not rule that prevents proactive invocation. It does not name an alternative tool for programmatic report access, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
verify_leanA
Call when you finish a change, before you report done or commit.
Checks new code for three things. Bloat: duplicates of existing code, dead code, unused imports, redundant
checks, single-implementation abstractions, pass-through wrappers, hand-rolled stdlib functions, unneeded
dependencies. Error handling: swallowed exceptions, missing timeouts, unchecked subprocesses, unclosed files,
imports of project modules or names that don't exist. Security: injection, hard-coded secrets, verify=False,
unsafe deserialization, unknown or vulnerable packages. diff is a unified diff; omit it to check the working
tree against HEAD.
Returns gate=pass|block with findings. Fix blocking findings or reject wrong ones via record_feedback.
| Name | Required | Description | Default |
|---|---|---|---|
| diff | No | ||
| repo | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations present, the description carries the full burden and does well: it discloses what categories are examined, that `diff` must be a unified diff, that omitting it checks the working tree against HEAD, and the output contract (gate=pass|block with findings). It does not cover cost, performance, or what 'blocking' versus non-blocking findings look like, leaving a modest gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the trigger condition, then the check categories, then parameter and return semantics. The long enumeration of checks is dense but each item earns its place by defining scope. Slightly list-heavy, but no filler sentences.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter analysis tool with no output schema and no annotations, the description covers purpose, trigger, both check semantics for `diff`, and the gate-based return shape. The only real omission is the `repo` parameter and any note on how the tool relates to the overlapping sibling checkers.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It explains `diff` well (unified diff; omit to check working tree against HEAD), but `repo` is never mentioned and its meaning, expected format, or relationship to the working tree is left entirely unexplained.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb (verify/check) and enumerates exactly what is checked: bloat, error handling, and security, each with concrete sub-items like dead code, swallowed exceptions, and hard-coded secrets. That is far more specific than the name alone. However, it does not differentiate itself from overlapping siblings such as check_reuse or check_dependency, whose domains (duplicate code, unneeded dependencies) are claimed here as part of 'bloat'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
"Call when you finish a change, before you report done or commit" gives a clear temporal trigger for use. It also names a follow-up path (fix or reject via record_feedback), which is helpful. It stops short of explicit exclusions or a comparison to sibling tools like check_reuse, so the agent must infer when this is preferable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
7 tool updates
v0.1.1- First observed
check_dependency - First observed
check_reuse - First observed
minimize - First observed
prove_equivalent - First observed
record_feedback - First observed
show_report - First observed
verify_lean
TDQS
Scored across 7 tools
Each tool maps to a distinct action in the code-quality workflow: feedback capture, report viewing, pre-write reuse search, post-change gate, minimization, equivalence proof, and dependency vetting. The potential overlap between check_reuse and check_dependency is resolved by scope (functions vs packages), and minimize/prove_equivalent differ clearly in intent and return value. An agent can select the right tool from the descriptions alone.
All names use snake_case, and six of seven follow a verb_noun/verb_object pattern (record_feedback, show_report, check_reuse, verify_lean, prove_equivalent, check_dependency). The lone single-verb name (minimize) is a minor deviation but still clear and consistent in style. No mixed casing or vague verbs.
Seven tools is well-scoped for a quality-gate server, covering the full loop from writing code to verifying, minimizing, and dependency checking. Each tool has a clear job and no obvious redundancy. Not too few, not bloated.
The surface covers the core lifecycle: pre-write reuse/dependency checks, post-change verification, minimization with testing, equivalence proof, feedback, and dashboard reporting. Gaps are minor—there is no tool to list past findings programmatically, and security waivers are intentionally CLI/human-only—but agents can work around these. Overall a complete set for the stated purpose.
Related MCP Connectors
Enforces architectural invariants and code quality rules inside your AI agent's edit loop.
Lets coding agents check their own code for leaked secrets, risky dependencies and AI-code mistakes
Checks AI-written code against your rules as it is written, and keeps a record of what it found.
Lints + auto-fixes how AI coding agents discover any new product. 24 rules, 6 tools, score 0-100.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to scan GitHub repositories for security vulnerabilities, deployment blockers, and code quality issues. It provides detailed findings and auto-generated code patches to help developers ensure their code is production-ready.31 npmMIT

CodePeel MCP Serverofficial
AlicenseAqualityDmaintenanceEnables AI agents to review code diffs for bugs, security issues, and bad patterns, and generate fixes.4MIT- AlicenseNot gradedqualityCmaintenanceEnables AI agents to scan code for security and quality issues and receive machine-readable reports with suggested fixes and verification criteria.63 npm2MIT
- AlicenseAqualityBmaintenanceEnables AI coding agents to enforce UI/UX best practices and backend performance auditing, providing design guidelines, compliance checks, component search, and performance optimizations to eliminate generic or sloppy code.7MIT