Skip to main content
Glama

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@leangate

Anything 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.

  1. 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_string does the job.

  2. They did what the prompt pushed them toward. shell=True with a name an admin types into a form, SQL built with an f-string, pickle.load on a path the client sends, verify=False. Five safety tasks, five times the problem shipped.

  3. They invented internal APIs. Nano imported zerver.http, zerver.lib.log and zerver.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 offline

Demo 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]
    end

Two checkpoints, not streaming. Half-written code always looks bloated (helpers not yet called, imports not yet used), so LeanGate judges complete units:

  1. 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's timezone.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.

  2. Before finishing: verify_lean() reads the working tree vs HEAD and returns gate: block | pass with 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

auto

≥ 0.85

Accept Jev's answer. Most calls stop here.

review

0.60–0.85

Nemotron Nano re-decides (LEANGATE_REVIEW_TIER).

escalate

< 0.60

Nemotron Super re-decides (LEANGATE_ESCALATE_TIER). Rewrites also go through the sandbox.

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=0 turns 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=1 sends 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 "reuse sanitize_filename" in 0.27 s with none), and in verify_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

duplicate

retry_request ≈ existing with_backoff (AST-structure + vocabulary similarity)

0.8 (blocks)

unneeded_dependency

requests when httpx is already a dependency

0.7 (blocks)

dead_code

unreachable statements; private functions nothing calls

0.6 (blocks)

bypassed_helper

requests.post(url) in Sentry, which sends outbound HTTP through safe_urlopen (it blocks internal addresses)

0.65 (blocks) for HTTP, subprocess, YAML or pickle when at least half the repo's call sites use the helper; else 0.5

stdlib_replaceable

chunk_invoices → itertools.batched, only if the repo's pinned Python has it, grounded in docs.python.org

0.5

over_abstraction

an ABC with one implementation

0.5

trivial_wrapper

a function that only forwards its arguments (not one that forwards to a project helper: that's reuse)

0.4

redundant_check

if x is None on a non-Optional parameter (kept on public functions by default)

0.4

unused_import

0.3

redundant_code, unused_variable

redundancy inside functions, via Ruff (SIM, PIE, PERF, C4, UP): key in d.keys(), a dead sys.version_info branch for the pinned Python, for x in s: yield x

0.3

Beyond bloat, LeanGate flags code production can't survive:

Category

Kinds

Examples

Blocks when

Error handling

swallowed_exception, missing_timeout, unchecked_subprocess, unmanaged_resource, unresolved_import

except Exception: pass; requests.get(url) with no timeout; subprocess.run(...) whose failure is ignored; from zerver.http import get_json when no such module exists

severity ≥ 0.6 and the decision is ≥ 0.6 confident

Security

sql_injection, shell_injection, insecure_tls, hardcoded_secret, unsafe_deserialization, code_execution, insecure_call, unknown_package, vulnerable_dependency

f-string SQL; shell=True with runtime values; verify=False on any call; pickle.loads; a dependency that doesn't exist on PyPI (a squatter could register it) or has known CVEs (OSV.dev)

severity ≥ 0.6 and the decision is ≥ 0.85 confident

What's missing (Nemotron)

missing_validation, missing_auth, missing_error_handling

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:

  1. reads the repo's pinned Python and dependency versions (pyproject.toml, requirements*.txt, .python-version)

  2. searches official domains only (docs.python.org, peps.python.org, PyPI, Read the Docs, GitHub) with the version in the query

  3. has Jev judge the excerpts: supported | contradicted | unclear. A contradicted claim drops the finding.

  4. 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

check_reuse(intent, code?)

optional: before writing a general-purpose helper its own search didn't find; also lists the house helpers the intent touches

check_dependency(name, purpose)

before adding a package

verify_lean(diff?)

when the change is done (default: working tree vs HEAD)

minimize(code, tests?)

to get a smaller, behavior-identical module; rejected if tests disagree

prove_equivalent(original, minimized, tests?)

run the same tests against two versions

record_feedback(signature, accepted, note)

a rejected finding is never raised again; conventions in note go to Mem0. Not for security findings: only a person can waive those (leangate waive)

show_report()

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 use

Keys: 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 it

Use 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

SessionStart

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

UserPromptSubmit

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

PostToolUse on Edit/Write/MultiEdit

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

Stop

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 VSIX

The 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 API

Step-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 /health shows 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-verify

This 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 report

Exit 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

search (reuse intent), p50 / p95

44 / 46 ms

0.8 / 2.8 ms

search_code (duplicate check), p50 / p95

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 marks escalate.

    • 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.get vs requests.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=0 keeps the public playground from running code. Run untrusted code in the Nebius-hosted service, not on a laptop.

  • Rejections recorded with record_feedback live in ~/.leangate, so CI doesn't see them. Waivers made with leangate waive and committed in .leangate/waivers.json are 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

MIT

Available Tools

7 tools
check_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.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameYes
repoNo
purposeNo
versionNo

TDQS

A4.1/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
codeNo
repoNo
intentYes

TDQS

A3.8/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
codeYes
repoNo
testsNo
findingsNo

TDQS

A3.7/5.0
Behavior5/5

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.

Conciseness5/5

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.

Completeness3/5

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.

Parameters2/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
testsNo
originalYes
minimizedYes

TDQS

C2.4/5.0
Behavior2/5

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.

Conciseness4/5

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.

Completeness2/5

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.

Parameters2/5

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.

Purpose3/5

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.

Usage Guidelines2/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
noteNo
repoNo
acceptedYes
signatureYes

TDQS

A3.6/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters2/5

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.

Purpose4/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
repoNo

TDQS

A4.2/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
diffNo
repoNo

TDQS

A3.9/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines4/5

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.

  1. 7 tool updatesv0.1.1
    • First observedcheck_dependency
    • First observedcheck_reuse
    • First observedminimize
    • First observedprove_equivalent
    • First observedrecord_feedback
    • First observedshow_report
    • First observedverify_lean

TDQS

A3.6/5.0

Scored across 7 tools

Disambiguation5/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness4/5

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

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables 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 npm
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Enables 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.
    7
    MIT