argot

argot

A local guardrail MCP server that checks agent-written code against a repo's own patterns — learned statistically from its git history, no LLM.

Category
Visit Server

README

<p align="center"> <img src="docs/argot-logo.svg" alt="argot" width="200" /> </p>

<p align="center"> <strong>Your codebase has a voice. argot makes AI code speak it.</strong><br/> <em>AI writes the code. argot harnesses it with the one thing that can't hallucinate: your repo's own history. Statistics, not a second LLM — flagging a dependency you've never used, a function you already wrote, an import that breaks your layering, a test quietly weakened, a convention only your team knows. 100% local, replayable.</em> </p>

<p align="center"> <a href="https://argot.tmonier.com"><strong>argot.tmonier.com</strong></a>  ·  <a href="https://argot.tmonier.com/docs/">Documentation</a>  ·  <a href="https://argot.tmonier.com/benchmarks">Benchmarks</a>  ·  <a href="https://argot.tmonier.com/caught-in-the-wild">Caught in the wild</a>  ·  <a href="docs/research/README.md">Research log</a> </p>

<p align="center"> <a href="https://github.com/get-tmonier/argot/releases/latest"><img src="https://img.shields.io/github/v/release/get-tmonier/argot?color=E67E45" alt="Release" /></a> <a href="https://www.npmjs.com/package/@tmonier/argot"><img src="https://img.shields.io/npm/v/@tmonier/argot?logo=npm" alt="npm" /></a> <a href="https://github.com/get-tmonier/argot/actions/workflows/ci.yml"><img src="https://github.com/get-tmonier/argot/actions/workflows/ci.yml/badge.svg" alt="CI" /></a> <a href="https://github.com/get-tmonier/argot/blob/main/LICENSE"><img src="https://img.shields.io/github/license/get-tmonier/argot?color=E67E45" alt="License" /></a> <img src="https://img.shields.io/badge/status-alpha-5B8DEF" alt="Status: alpha" /> <img src="https://img.shields.io/badge/rust-single%20static%20binary-DEA584?logo=rust&logoColor=white" alt="Rust" /> <img src="https://img.shields.io/badge/100%25-local%20%C2%B7%20no%20cloud-brightgreen" alt="100% local, no cloud" />  · <a href="https://argot.tmonier.com/docs/languages/">11 languages →</a> </p>

<p align="center"> <a href="https://argot.tmonier.com/#film"><img src="landing/public/argot-film-poster.jpg" alt="Watch the argot launch film" width="220" /></a> <br/> <em>🎬 <a href="https://argot.tmonier.com/#film">Watch the 45-second launch film</a></em> </p>


Type checkers ask "is this valid?" argot asks the question that used to live in code review: "is this how we do it here?" — and catches AI code that's flawless, type-correct, lint-clean, and still doesn't belong. It answers with statistics on your repo's own history — the statistical core deterministic and replayable, everything local — never a second LLM judging the first.

It also asks a second question no other tool asks: did the AI play fair? When an agent can't make a failing test pass, the cheapest path to "done" is to make the test stop looking. argot reads both sides of every diff and pairs a weakened, disabled, or deleted test with the production change it covers.

Five learned detectors — plus the rules only your repo could write

Rule It catches
🚫 foreign-import + friends a dependency, API, or idiom your repo has never used "we don't do it this way here"
♻️ redundant a new function that reinvents one you already have "you already have this"
📍 misplaced the right code, filed in the wrong place "this doesn't belong here"
🧱 layering an internal import that reverses your architecture "we never cross this boundary"
🧪 test-deleted + friends a test quietly weakened, disabled, or removed alongside the prod change it covers "don't game the tests"
📜 your own rules the conventions only your repo has — scripted, no recompile "here's exactly how we do it"

The first five are learned from your git history. The sixth is written by you — and it's the part of every linter config your team actually cares about.

Get started

# install (single static binary — no Python, no Node)
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/get-tmonier/argot/releases/latest/download/argot-installer.sh | sh

Windows: powershell -c "irm https://github.com/get-tmonier/argot/releases/latest/download/argot-installer.ps1 | iex" · npm: npm install -g @tmonier/argot

Sixty seconds of proof, zero setup, on your own history:

argot audit      # ⏪ what did AI sneak into your last 50 commits?

audit fits the voice as it was 50 commits ago (in a temp worktree — your tree stays untouched), rescores everything since, and attributes every finding to its introducing commit — ai-assisted / human / unknown, from concrete commit markers only:

━━ argot audit ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  last 50 commits · 52% carry AI markers · 1 finding would have met review

  Worst offender — commit cae8349 · ai-assisted
  ! landing/src/pages/llms-full.txt.ts:L1-32 · foreign-import
      ↳ astro (L1), astro:content (L2) — 0 of 49 module specifiers…
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

The terminal card ends with a copy-paste share caption, and argot audit --format html is a screenshot-ready card — post your score.

Then fit today's voice so check raises these before they merge:

argot init       # learn this repo's voice (~25 s on a 1,100-file repo)
argot check      # score your working changes against it

Accuracy is a function of setup — argot learns from what it's allowed to see. Best path: npx skills add get-tmonier/argot, then /argot-setup in your coding agent (Claude Code, Cursor, 70+ agents) reads your repo, excludes what shouldn't shape the voice, and verifies the catch. Full guide: Setup · Getting started.

Claude Code — one install for everything. The argot plugin bundles the five skills, the MCP server (argot mcp — proactive voice context while your agent writes), and an opt-in, non-blocking pre-write guardrail that asks before a foreign dependency lands:

/plugin marketplace add get-tmonier/argot
/plugin install argot@argot

Other agents (Cursor, Codex, 70+): npx skills add get-tmonier/argot.

Demo

<p align="center"> <img src="docs/demo/demo.gif" alt="argot check flagging a foreign Django-style view in an all-FastAPI codebase" width="760" /> </p>

A PR adds a Django-style view to an all-FastAPI codebase. mypy and ruff are silent — the framework it reaches for is one this repo has never imported:

argot check · 1 hunk above threshold (1 foreign)

fastapi/receipts.py
  !  L1-L10         1.00  foreign  · staged · foreign-import [94a92c256ea1]
     ↳ django (L1) — 0 of 74 module specifiers in repo
       common here: fastapi (357×), pydantic (129×), typing (129×) (+7 more)
  1 | from django.views import View
             ^^^^^^

redundant names the function you already have (↳ duplicates slugify (src/utils/text.py:14) — similarity 0.86), misplaced names where the code belongs, layering names the direction an import reverses, and every line is your repo's own evidence. Full anatomy: Reading the output · What it catches.

Your conventions, as rules

Every team has conventions no generic linter ships: "presentational components take props — they don't fetch", "files are parsed through our loader, never a raw JSON.parse", "one HTTP client per repo". They live in review comments and onboarding docs — until an AI agent, who read neither, merges around them. With argot they're repo-local rules: a TOML manifest + a small sandboxed script in .argot/rules/, versioned with your code, loaded at run time — no plugin build, no recompile, one rule format across all 11 languages.

And they can do what no classic linter structurally can. A linter sees one version of one file; argot hands your rule both sides of the diff — so you can write rules about what a change removed:

# .argot/rules/no-dropped-endpoints/rule.toml
[rule]
schema = 1
name = "no-dropped-endpoints"
description = "removing a public endpoint requires a deprecation cycle — catch the route that silently disappears in a diff"
severity = "error"
languages = ["typescript", "javascript"]
// check.rhai — a route that existed before this change, and is gone now
const ROUTES = "(call_expression function: (member_expression property: (property_identifier) @verb)
                arguments: (arguments (string (string_fragment) @path)))";
let now = [];
for m in ts_query(ROUTES) { if m.capture == "path" { now.push(m.text); } }
for m in ts_query_old(ROUTES) {
    if m.capture == "path" && !now.contains(m.text) {
        report(m.line, "endpoint '" + m.text + "' removed without a deprecation cycle — see docs/api-lifecycle.md");
    }
}

No ESLint plugin can express that rule — there is no "old side" in a linter. And because argot fits your history, a rule's allowlist can be your own git log: import_attested("moment") asks "has this repo ever used this date library?" — no list to hardcode, no list to maintain. Rules run on changed files only (adopting one creates zero backlog noise), and their findings are suppressed, configured, and rendered exactly like built-in rules. argot rules test is the red/green authoring loop. Full reference + worked examples: Custom rules.

Rules an agent can't game

An AI agent that can't satisfy a check will reach for the next-cheapest green: mute the rule, downgrade it in a local config, --rule it=off, or — for a custom rule — just rewrite the script that caught it. Lock the rule and every one of those doors closes:

[rules]
layering = { severity = "error", locked = true }
custom   = { severity = "error", locked = true }   # lock every repo-local rule

A locked rule (opt-in, from the committed argot.toml only):

  • freezes its severityargot.local.toml and --rule overrides are refused;
  • refuses every suppression surface for its findings — inline # argot: ignore, [[mute]], and [exclude].paths don't apply;
  • and — the teeth — weakening the lock is itself a finding. rule-tampered (group governance, pinned error, unsuppressable) reads both sides of the diff being checked and fires when the change removes a lock, downgrades a locked severity, adds a [[mute]] on a locked rule, or edits a locked custom rule's script — with a loud run-level warning your CI surfaces (a PR annotation under --format github).

Tamper-evidence, not tamper-proofing — the same philosophy as the test-integrity rules: an agent can touch the alarm, but touching the alarm is the alarm. The one quiet way to relax a locked rule is a committed argot.toml diff a human reviews. Guide: Locked rules.

Configure it like any linter

Every rule (built-in or yours) defaults through argot.toml [rules]error / warn / off, per rule or per group — or per run via --rule layering=warn. A [rules] entry can also scope a rule to paths (layering = { include = ["src/**"] }). Excludes are gitignore-style [exclude].paths; inline # argot: ignore-next-line rule=… — reason and argot mute <hash> give line-level and durable committed acceptances. Guides: Configure · The commands.

argot vs. the tools you already run

Type checker Linter Copilot · SAST argot
Catches invalid code ~
Flags what's foreign to this repo
Flags a function you already have
Flags code filed in the wrong place
Flags an import that breaks your layering
Flags a test quietly weakened to game a failing suite
Enforces your team's own conventions, cross-language, on the diff ~
Locked rules an agent can't mute, override, or rewrite unnoticed
Audits merged history · attributes findings AI vs human
Learns from your history · runs 100% local

argot is additive: it sits after your type checker and linter and catches the one thing they can't — code that's valid and lint-clean but unlike anything your team has written. It's the harness around AI output, built from the one thing that can't hallucinate: your repo's own history.

Benchmarks

Honest, leak-free numbers, measured by the real fit → check pipeline — foreign fixtures spliced into real host files; false alarms counted on a temporal holdout the model never saw:

  • Foreign catch — 595/605 (98%) when the foreign symbol is visible in the diff · false alarms 0.29% of 22,513 real hunks (worst corpus 1.46%)
  • Architecture — 244/252 (96.8%) caught · 0/140 controls flagged · ≤2.7% over-fire on replayed real history
  • Reinvention — median 89% at ≤4.5% false fires per hunk · Misplacement — 85–99% (median 96%) at ≤1.2%, where the repo has separable architecture
  • Test-integrity — 144/153 (94.1%) gaming tactics caught · 0/102 legitimate-refactor controls · 1.12% of 5,268 replayed accepted test-touching commits flagged at gating severity

One documented limit: masked foreign — a foreign symbol whose name collides with one you already use — is statistically invisible to a voice model. We publish that number rather than hide it. And the method, stated plainly: catch rates are measured on fixtures we authored under a pre-registered rubric frozen before scoring; false alarms are counted on real commits the model never saw. Independent validation is welcome — that's what the reproducible harness is for. Per-language and per-corpus tables, methodology, confidence intervals: benchmarks page (CI-fed, can't drift from what ships). Want a language validated? Open an issue.

Numbers are one thing; real diffs are another. Caught in the wild collects verified findings from running argot over the last year of history on 33 real open-source repos (dagster, hono, rich, saleor, faker, and more) — each one adversarially attacked by a reviewer briefed to disprove it, and every one survived.

CI

- uses: get-tmonier/argot@main   # non-blocking voice score on every PR

--format github prints inline PR annotations; --format sarif feeds code scanning; --format json is a stable schema. Add publish-badge: true (with contents: write) for a live README badge — argot · N% in-voice, updated on every push. Copy-paste setups incl. pre-commit: the CI guide.

How it works

Five learned detectors, one source of truth — your git history — plus the rules you script yourself. A statistical voice model (two frequency tables + a callee-cluster partition — no neural net) catches foreign imports, callees, and token shapes; a local code-embedding model (jina-code via statically-linked llama.cpp) catches reinvention and misplacement; a module-dependency graph catches layering reversals; a test-inventory diff catches gamed tests. Fit in seconds, check in milliseconds, nothing leaves your machine — and nothing generates: every verdict is a statistic you can replay. Full detail: How it works · The scoring model · Performance · experiment log in docs/research/.

Contributing

Issues and PRs welcome — start with CONTRIBUTING.md:

git clone https://github.com/get-tmonier/argot && cd argot
just build       # cargo build --release -p argot → target/release/argot
just verify      # cargo fmt --check + clippy -D warnings + cargo test

Acknowledgements

argot is benchmarked against real repositories used as read-only corpora — cloned at benchmark time, never redistributed, each under its own license, none affiliated with argot: FastAPI, rich, faker, Saleor, Wagtail, Dagster, Scrapy, Hono, Ink, faker-js, Excalidraw, Outline, Express, Commander.js, ESLint, GitHub CLI, Hugo, ripgrep, bat, Guava, JUnit 5, PowerShell, Jellyfin, redis, curl, RocksDB, fmt, Homebrew, RuboCop, Laravel, and Composer.

Built on tree-sitter (11 grammars), libgit2 via git2, HuggingFace tokenizers (UnixCoder BPE), Rhai (scripted rules), clap, Serde, and cargo-dist. The semantic layer links llama.cpp (MIT) statically via llama-cpp-2; its model is jina-embeddings-v2-base-code by Jina AI (Apache-2.0), fetched on first use as a Q4_K_M GGUF quantization (a derivative work under Apache-2.0 §4) from the semantic-model-v1 release. argot is not affiliated with, nor endorsed by, Jina AI.

Privacy

argot runs 100% locally — no telemetry, no account, your code never leaves your machine. Full policy: argot.tmonier.com/privacy.

License

MIT

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured