ait-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@ait-mcpRun static analysis on src/ and show evidence-backed findings with fixes."
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.
Runs on the standard library AST alone:no runtime dependencies and no network access. Witnesses are built as text and syntax-checked only; the code it flags is never executed.
๐ฏ The problem it solves
A regular linter points at a line but not at the path that made the value None.
You open the file, walk backwards to the origin, and guess whether your patch will break something else.
โ ๏ธ The question left open | โก What AiT-test answers in one result |
Where did this | Causation trace: every step carries |
How do I reproduce it | Repro witness: the smallest code that triggers the symptom, plus a |
Will the fix break something else | Suggested fix: a unified diff that passes |
Which file should I fix first | A DEFECT needs evidence: without concurrency or type evidence the finding drops to WARNING instead of guessing |
Related MCP server: ocr-mcp-server
โก Getting started
Python 3.10 or newer. Nothing to install:
python cli.py srcOr install the ait / ait-mcp commands (still no dependencies):
pip install -e .
ait srcThe distribution is named ait-test (pip show ait-test). The engine name reported by --version and by MCP serverInfo is ait-engine 1.0.0. Same project at two layers, not two packages.
The full transcript lives in docs/demo.txt, and the GIF above was recorded from the real run.
$ python cli.py demo.py
================== [AIT-N01] Unshielded NoneType Dereference ===================
File: demo.py:10:10
Severity: DEFECT (Confidence: 0.90)
Function: render()
Symbol: value
`value` can be None at this point but is dereferenced without a guard
--- Causation Trace ---
Step 1 | demo.py:9:12
| `lookup()` is declared to return `Optional[str]`, so the result can be None
Step 2 | demo.py:9:4
| `value` receives the return value of `lookup()`
Step 3 | demo.py:10:10
| `value` is dereferenced here without an `is None` guard
--- Suggested Fix ---
--- a/demo.py
+++ b/demo.py
@@ -10,1 +10,3 @@
+ if value is None:
+ raise RuntimeError(f"value is unexpectedly None")
print(value.upper())
========================= 1 failed, 0 passed in 2.59ms =========================
DEFECT 1 ยท WARNING 0 ยท ADVICE 0
exit code: 1The code used for the demo:
from typing import Optional
def lookup(key: str) -> Optional[str]:
return None
def render() -> None:
value = lookup("alpha")
print(value.upper())๐งญ The 13 rules
ID | Level | What it catches | Evidence it requires |
| DEFECT |
| a pipe exists and the child is waited on without a prior |
| DEFECT | a child handle is dropped without | the variable resolves to a process handle |
| WARNING | a process/thread spawned at module scope without | the call really sits at module level |
| DEFECT / WARNING | read-modify-write on shared state with no lock around it | concurrency evidence (spawn site, |
| WARNING | a condition checked outside the lock and again inside it | a real lock region and the same condition |
| DEFECT / WARNING | a blocking call inside |
|
| WARNING |
| not stored in a variable, not awaited, no done callback |
| DEFECT | a coroutine called without | the callee resolves to a coroutine in the same file or to a known API |
| DEFECT | mutating a caller's parameter in place ( | the write targets the parameter name directly |
| DEFECT | a mutable default argument ( | a real mutable literal or constructor |
| DEFECT | dereferencing a value that may be | evidence the value is optional (annotation, |
| WARNING | a bare |
|
| ADVICE | more than four levels of nested control flow | counts real control nodes only and never crosses a scope |
Severity policy: a DEFECT needs supporting evidence. When concurrency or type evidence is missing, the finding drops to WARNING instead of guessing, because one false finding is enough to make people stop reading the report.
๐ฅ๏ธ CLI
python cli.py [target] [options]Flag | Effect |
| per-file detail |
| summary only |
| stop at the first file carrying a DEFECT |
| scan only the files that failed last run (needs the cache) |
| show a diff and ask one finding at a time (needs a TTY) |
| skip both cache reads and writes |
| show at most N failure blocks |
| disable ANSI (or set |
| print the version |
Exit code: 0 clean ยท 1 DEFECT present ยท 2 interrupted ยท 3 parse or internal error ยท 4 usage error
When--lf finds a cache it cannot open, no index, or no earlier run, it scans every file and says so on stderr.
The alternative is analysing zero files and reporting "clean" without having read anything.
every patch is passed through
ast.parseagain before it is offeredfindings are asked one at a time (
y/N/afor all /qto quit)before writing, the original is copied to
.ait_backup/<timestamp>-<pid>/the file is written to a temporary path and moved with
os.replace(atomic), as bytes, so line endings are preservedthe file is written back with its original codec (
utf-8-sigkeeps the BOM, a BOM-less UTF-8 file stays BOM-less,cp874stayscp874) if a patch contains characters the original codec cannot encode, the write is refused rather than silently converting the file to UTF-8that file is rescanned; any new finding triggers an immediate rollback
the whole target is rescanned at the end to confirm the final state; a Ctrl-C mid-way prints how many fixes were applied and where the backup lives
Inline directives work with or without the AIT- prefix (people tend to remember the rule number from the output):
value.upper() # ait: disable=AIT-N01except: # ait: disable=H01
passexcept: # ait: disable=ALL
passA suppressed finding is not printed but is still counted in the summary line as N suppressed, so nothing disappears quietly.
Or put globs in .aitignore at the project root:
legacy_*.py
build/๐ค MCP
mcp.py speaks JSON-RPC 2.0 over stdio and works with any MCP client. stdout carries protocol frames only; logs go to stderr.
{
"mcpServers": {
"ait": {
"command": "python",
"args": ["L:\\path\\to\\AiT-Test\\mcp.py"]
}
}
}opencode (opencode.jsonc) uses mcp.servers.<name> with command as an array, not the mcpServers shape above:
{
"mcp": {
"servers": {
"ait": {
"type": "local",
"command": ["python", "L:\\path\\to\\AiT-Test\\mcp.py"]
}
}
}
}ait-mcp --help and ait-mcp --version check the install without entering the serve loop.
Tool | What it does |
| scans a file or directory and returns findings with causation, witness and diff |
| returns source, variable table, calls and callers for one symbol (token-efficient) |
| compares candidate code against the baseline: what is resolved and what is newly introduced |
Tool | Arguments |
|
|
|
|
|
|
isError: falsewhen the tool ran successfully even if it found a DEFECT, because a finding is a usable result, not a tool failure (isError: trueis reserved for the tool genuinely failing, for example a candidate that does not parse)an argument the schema does not declare, a wrong type, or an out-of-range value gets
-32602instead of being ignored or silently clampedan unexpected exception (say
RecursionError) is returned as-32603and the process keeps serving requestsshutdownandnotifications/exitstop the serve loop;initializedoes not advertise aloggingcapability because none is usedstructuredContentholds the object once;content[0].textis a compact JSON mirror (no indentation) so the payload does not double in size
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{
"name":"ait_verify_fix",
"arguments":{
"file_path":"demo.py",
"candidate_code":"from typing import Optional\n\ndef lookup(k: str) -> Optional[str]:\n return None\n\ndef render() -> None:\n value = lookup('a')\n if value is None:\n return\n print(value.upper())\n"
}
}}ait_verify_fix re-checks AST-level invariants; it is not a mathematical proof.
All it confirms is that the 13 rules find nothing in the new code, which does not mean the program is correct in every case.
๐ Capability and evidence
0 runtime deps ยท offline ยท 13 rules ยท trace + witness on every finding ยท patches only where a deterministic fix exists (A01/A02/H02/M02/R01 have none) ยท digest-aware cache
Dimension | Status | Evidence in the project |
Runtime dependencies | โ 0 | every import comes from the standard library; |
Works offline | โ yes | no HTTP client, no telemetry |
Causation trace | โ every finding |
|
Repro witness | โ text + syntax check | never executed; deadlock/recursion demos carry |
Suggested patch | โ re-parsed and rescanned | a patch that introduces a new finding is rejected |
Cache | โ sqlite3 WAL | key = path + size + mtime_ns + digest + ruleset bytecode + schema |
Suppressed finding count | โ | the summary line reports |
Cross-file type knowledge | โ ๏ธ limited | annotations, literals and producers in the same file, plus hard-coded APIs |
Proves the program correct | โ not the goal | 13 static rules, not formal verification |
๐ Measured on this machine
Measured on Windows 11 (AMD64) and WSL2 Ubuntu across three interpreters.
Scenario | 3.12.10 (Windows) | 3.14.4 (WSL2 Ubuntu) | 3.15.0rc2 (Windows) |
Scan | 642 ms | 725 ms | 510 ms |
Rescan (warm cache) | 12.3 ms | 270 ms* | 9.9 ms |
Test suite ( | 6.9 s OK | 17.3 s OK | 6.8 s OK |
DEFECT / WARNING / ADVICE on | 0 / 0 / 0 | 0 / 0 / 0 | 0 / 0 / 0 |
* WSL reads the repo through /mnt/l (drvfs), which slows every sqlite round trip; the engine itself is not the slow part.
Engine size (src/ait, 23 files) 7,083 lines ยท Runtime dependencies 0
Numbers shift with the machine and file sizes; the cold/warm ratio says more than the absolute values.
The code-size badge at the top counts every line of code in the repository as bytes, tests included, so it measures the whole project rather than src/ait.
๐ง How it works
discover โโโบ parse โโโบ scope tree โโโบ call graph โโโบ data flow
โ โ
โโโโโโโโโโโโโโบ 13 rules โโโโโโโโโโโโโโโโโโ
โ
findings โโโโโโโผโโโโโโโบ causation trace
โโโโโโโโบ repro witness
โโโโโโโโบ suggested patch (re-parsed)Cache is sqlite3 (WAL), keyed on
path + size + mtime_ns + BLAKE2b digest + ruleset fingerprint + schema. The digest is compared on lookup, so a rewrite that restores the same size and mtime (git checkout,touch -r, a restored backup) does not resurrect results for older content. The ruleset fingerprint hashes the bytecode of every rule, not just its name, so editing a detector invalidates old results. A corrupt DB file (bad magic header) is discarded and recreated once; if that fails too, the cache is switched off instead of crashing or looping.Regression diff compares finding counts per
(rule, symbol), not line numbers, so shifted lines do not produce phantom findings.Witness is generated as text and parsed for syntax only; nothing is executed (deadlock/recursion demos carry
safe_to_execute: false).
๐งฑ Project structure
AiT-Test/
โโโ cli.py # pytest-style entrypoint
โโโ mcp.py # JSON-RPC over stdio entrypoint
โโโ pyproject.toml # packaging (license = MIT)
โโโ LICENSE # MIT license text
โโโ .github/workflows/
โ โโโ ci.yml # 4 jobs: test matrix (12) + package + lint + security
โ โโโ codeql.yml # CodeQL, security-and-quality, weekly
โโโ .github/codeql/
โ โโโ codeql-config.yml # query suite + tests/fixtures excluded
โโโ .github/dependabot.yml # SHA-pinned actions, updated weekly
โโโ src/ait/
โ โโโ engine.py # discover / scan / compare_findings / verify_fix
โ โโโ models.py # Diagnostic, Patch, Witness, ScanResult
โ โโโ context.py # guard detection, suppression, patch builders
โ โโโ cache.py # sqlite3 cache + corruption recovery
โ โโโ reporter.py # terminal renderer
โ โโโ slicer.py # ait_slice_context
โ โโโ flow/ # scope tree, call graph, data flow, witness
โ โโโ rules/ # 13 rules
โโโ tests/ # unittest + fixtures
โโโ docs/ # demo.gif, demo.txt, assets/Layer | File | Responsibility |
Frontend |
| flag parsing, rendering, the |
Frontend |
| JSON-RPC framing, tool schema and validation, stdio loop |
Core |
| discover โ parse โ analyze โ cache โ aggregate |
Core |
| guard/dominance lookup, suppression, evidence and patch builders |
Flow |
| symbol table, lexical scopes, binding resolution |
Flow |
| call sites, lock regions, spawn sites |
Flow |
| backward tracing of optional/ |
Flow |
| minimal repro text and syntax validation |
Rules |
| 13 detectors (Z/R/A/M/N/H) |
Infra |
| sqlite3 WAL, digest and ruleset keyed, prune, |
Infra |
| immutable Diagnostic/Patch/Witness, exit codes, SCHEMA_VERSION |
CI |
| test matrix, self-scan, stdlib-only run, integration tests |
CI |
| CodeQL security-and-quality, |
CI |
| drives the real |
๐งช Tests
No pytest required:
python -m unittest discover -s tests -vThe fixtures come in pairs: bad_*.py (must be caught) and clean_*.py (must stay silent).
The suite also asserts that the engine imports nothing outside the standard library (python -S still runs it).
Ran 268 tests in ~7s
OKConfirmed on 8 interpreters (7 on Windows 11, 1 on WSL2 Ubuntu): every one returns 268 tests OK, a clean src/, and exit 0.
Interpreter | Source |
CPython 3.10.21 | uv-managed (Windows 11) |
CPython 3.11.16 | uv-managed (Windows 11) |
CPython 3.12.10 | Windows 11 (system) |
CPython 3.12.14 |
|
CPython 3.13.15 |
|
CPython 3.14.7 |
|
CPython 3.15.0rc2 |
|
CPython 3.14.4 | WSL2 Ubuntu |
3.10.21 โ Ran 268 tests in 7.152s OK
3.11.16 โ Ran 268 tests in 6.990s OK
3.12.10 โ Ran 268 tests in 6.928s OK
3.12.14 โ Ran 268 tests in 6.698s OK
3.13.15 โ Ran 268 tests in 6.724s OK
3.14.4 (WSL2 Ubuntu) โ Ran 268 tests in 17.332s OK
3.14.7 โ Ran 268 tests in 7.319s OK
3.15.0rc2 โ Ran 268 tests in 6.769s OKAll 8 scan the 23 files under src/ without a finding and exit 0.
tests/fixtures exits 1 as it should (DEFECT 11 ยท WARNING 4 ยท ADVICE 1).
On 3.15 the suite also passes with -W error::DeprecationWarning for all 268 tests,
which means the engine does not touch APIs scheduled for removal, and python -S (no site) scans normally.
๐ CI (GitHub Actions)
CI runs on GitHub Actions with every action pinned to a commit SHA (not a floating tag), and Dependabot updates them weekly.
CodeQL is a workflow of its own (codeql.yml) on the same pinning policy.
Step / test | What it checks | Pass condition |
| 268 tests on that interpreter | must be |
| whether it uses APIs scheduled for removal | must be |
| the engine scanning itself | exit 0 ยท DEFECT/WARNING/ADVICE = 0 |
| no site-packages at all | exit 0, so nothing third-party is required |
| a corpus that is meant to fail | exit 1 and a |
| real JSON-RPC ( |
|
tests/test_cli_integration.py and tests/test_mcp_protocol.py call cli.py / mcp.py through subprocess the way a user would, instead of importing internal functions.
That catches regressions at the entrypoint level: a wrong exit code, logs leaking into stdout, a broken protocol frame.
Job | Tool | What it checks | Pass condition |
|
| builds the wheel, installs it with | exit 0 for every command |
|
| metadata must have no |
|
| Ruff 0.17.2 |
| 0 findings |
| Bandit 1.9.4 | medium and above in | 0 findings |
|
| every module compiles | exit 0 |
| actionlint 2.2.0 | workflow syntax and expressions | exit 0 |
| OSV-Scanner 2.7.0 | the dependency surface against the OSV database |
|
| shell guard | any | no lockfile found |
Ruff found something the engine itself cannot: F821 Undefined name 'List' in rules/registry.py,
hidden under from __future__ import annotations so it never failed at runtime, but ready to break the moment anyone calls get_type_hints. Fixed.
Bandit reports two B101 assert sites (cache.py, cli.py) at low severity.
Both asserts are internal invariants after type narrowing, so the threshold is -ll (medium and above) and the low findings stay by design.
CodeQL runs in its own workflow (.github/workflows/codeql.yml) on every push to main, on a weekly schedule and on demand.
It uses the security-and-quality suite, and github/codeql-action is pinned to v4.39.1 by commit SHA like every other action in the repo.
security-events: write is granted to the analysis job only.
The first analysis reported 9 results. All nine were fixed rather than dismissed: three empty except ...: pass blocks became contextlib.suppress(...),
one duplicate import, one unused import and two unused local bindings were cleaned up,
and the last two pointed at tests/fixtures, a corpus that is defective on purpose.
That directory is now excluded through .github/codeql/codeql-config.yml instead of being dismissed alert by alert.
dependency-review stays off. It needs the dependency graph API, and this package declares no dependencies at all, so it would review an empty set.
The OSV badge is not on this page. OSV publishes no official badge, and a static "0 issues" shield would keep saying 0 after the first advisory,
so the status lives in the security job of CI where it is actually refreshed. The CodeQL shield at the top of the page is different: it is GitHub's live status for that workflow.
โ ๏ธ Limits
Read this before relying on it. The tool answers a narrow set of questions well, and does not replace a type checker, a test suite or code review.
It is static analysis: it never runs the code, so it cannot know real runtime values.
There is no full type inference, so it knows annotations, literals, producers in the same file, and hard-coded APIs.
Cross-module: an optional value that comes from a file outside the scan is invisible.
AIT-R01reports only when concurrency evidence exists; a project that spawns threads through an unusual abstraction may slip past.A witness is "an example that triggers the symptom", not a test case ready for CI.
A patch is a syntactic suggestion and may not match your architecture (whether to
raiseorreturndepends on context).
If false positives worry you more than false negatives, start with --maxfail 5 and go one at a time.
๐ Credits and titles
Title | Basis |
๐๏ธ Architect of the 13 Rules | designed the 13-rule set (Z/R/A/M/N/H) and the "a DEFECT needs evidence or it drops to WARNING" policy |
๐งช Eight-Interpreter Verifier | confirmed 268 tests on CPython 3.10 โ 3.15 across Windows 11 and WSL2 Ubuntu |
๐ก๏ธ Zero-Dependency Keeper | 23 engine files, 7,083 lines, no runtime dependency and fully offline |
๐ค MCP Toolsmith | JSON-RPC 2.0 contract for three tools with validation, |
๐ CI Gatekeeper | 4 jobs / 15 runs: test matrix on 12 environments + package + lint (ruff, bandit, actionlint) + OSV, plus a CodeQL workflow sitting at 0 open alerts |
๐ License
MIT. The license expression is declared in pyproject.toml (license = "MIT"), and the full text is in LICENSE.
Available Tools
3 toolsait_reviewA
Run the 13-rule AST catalog over a Python file or directory and return machine-readable findings: rule_id, severity (DEFECT/WARNING/ADVICE), location, symbol, confidence, causation trace, and where available a repro witness and a unified diff. USE WHEN: the user asks to review, audit, lint, or hunt bugs, race conditions, null dereferences, or swallowed errors in .py code, or before proposing a patch. DO NOT USE: to inspect one known symbol (use ait_slice_context) or to test a patch you already wrote (use ait_verify_fix). Note: use_cache=true (the default) writes /.ait_cache; findings are bounded by max_findings and severity_min.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | File or directory to analyse, e.g. src/ait/mcp.py or src/ait. | |
| exitfirst | No | Stop at the first file carrying a DEFECT. | |
| use_cache | No | Reuse the BLAKE2b cache; set false for one-shot scans that must not write into the tree. | |
| max_findings | No | Cap the returned findings (sorted by path,line); the tally still counts every finding. | |
| severity_min | No | Drop findings below this severity, e.g. DEFECT to return only defects. | ADVICE |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description does real work: it discloses the cache side effect (use_cache=true writes <target>/.ait_cache), that findings are bounded by max_findings and severity_min, and enumerates returned fields including a causation trace and repro witness. It stops short of confirming whether the emitted unified diff is applied or merely shown, which leaves one behavioral question open.
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 action and return shape, then cleanly sectioned into USE WHEN / DO NOT USE / NOTE. Dense but every clause carries information; the output-field enumeration is the only part that could be trimmed if an output schema existed.
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?
No output schema exists, but the description enumerates the returned fields (rule_id, severity, location, symbol, confidence, causation trace, repro witness, diff) and covers the cache side effect and result caps. An agent has everything needed to decide, invoke, and interpret the result.
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 100% so the baseline is 3, and the description adds genuine context on top by explaining the default-on cache write and the interaction between max_findings bounding the returned list and severity_min filtering. It does not add anything beyond the schema for path or exitfirst, but that is acceptable given full coverage.
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+resource (run the 13-rule AST catalog over a Python file or directory) plus the exact output shape. The scope boundary ('a Python file or directory') makes it immediately distinguishable from the symbol-level and patch-level siblings.
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?
Explicit USE WHEN list enumerates the triggering tasks (review, audit, lint, bug hunt, before proposing a patch). Explicit DO NOT USE clause routes to the correct alternatives, ait_slice_context for one known symbol and ait_verify_fix for testing an already-written patch.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ait_slice_contextA
Return a token-efficient structural slice of exactly one symbol: its source text, signature, docstring, variable table, outgoing calls, callers and optionally the bodies of its callees. USE WHEN: you know the symbol name and need its implementation without reading the whole file. DO NOT USE: for whole-file or repository review (use ait_review), or when the symbol name is unknown โ locate it first.
| Name | Required | Description | Default |
|---|---|---|---|
| depth | No | Call-graph hops to expand (0 = signature only, 3 = maximum). Out-of-range values are rejected, not clamped. | |
| file_path | Yes | Existing Python file that contains the symbol, e.g. src/ait/slicer.py. | |
| symbol_name | Yes | Function or class name; qualify it as Class.method when ambiguous, e.g. Engine.scan. |
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 disclose the payload shape and the optional callee-body expansion tied to depth, which reads as a pure read operation. Gaps remain: nothing about behavior when the symbol is not found, ambiguity resolution failure, or whether the slice can still be large on deep call graphs.
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 verb and scope, then a compact USE WHEN / DO NOT USE pair; every clause (output contents, sibling routing, unknown-symbol precondition) earns its place with no filler.
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?
No output schema exists, so the enumeration of returned fields is essential and present, and all three parameters are documented in-schema. What is missing is error/edge behavior (missing symbol, nonexistent file) that an agent would want before calling.
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 100%, so baseline is 3, but the description adds real meaning: 'exactly one symbol' constrains symbol_name cardinality, and 'optionally the bodies of its callees' explains what depth actually expands. It does not restate the 0-3 range or the Class.method qualification, which the schema already covers.
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?
Specific verb+resource: 'Return a token-efficient structural slice of exactly one symbol', with the returned contents enumerated (source, signature, docstring, variable table, outgoing calls, callers, callee bodies). The 'exactly one symbol' scoping distinguishes it plainly from the whole-file sibling ait_review.
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?
Explicit USE WHEN (symbol name known, need implementation without reading the file) and DO NOT USE (whole-file/repository review -> ait_review; unknown symbol -> locate it first). The alternative sibling is named with the condition that selects it, leaving little to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ait_verify_fixA
Re-run the 13-rule catalog over a candidate source string and diff the result against the file as it currently exists on disk: returns resolved, introduced, remaining_defects, regression and clean. USE WHEN: you have a proposed patch for a file that is still unmodified on disk, to prove the target finding is gone and nothing new appeared. DO NOT USE: after writing the patch โ the baseline would then equal the candidate and the diff is meaningless โ or as a semantic proof; this is AST re-verification of 13 rules, not correctness.
| Name | Required | Description | Default |
|---|---|---|---|
| file_path | Yes | Unmodified baseline file on disk, e.g. src/ait/engine.py. | |
| candidate_code | Yes | Full replacement source text (not a diff), e.g. 'def f(x):\n return x\n'. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so the description carries the full burden, and it does: it discloses that this is read-only verification against an on-disk baseline, names the five verdict fields, and sets an explicit expectation that this is AST re-verification of 13 rules rather than a correctness proof.
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 core operation, then structured with USE WHEN and DO NOT USE blocks. Dense but every clause, including the 'after writing the patch' exclusion, carries decision-relevant information.
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 tool with no output schema and no annotations, it supplies the verifier's return categories, the precondition on the file state, and an explicit limitation on what the verdict means. Nothing an agent needs to invoke it correctly is missing.
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 already 100%, so the baseline is 3; the description adds meaning by requiring the file be the unmodified on-disk baseline and by stressing a full replacement source string rather than a diff, which prevents a realistic misuse.
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 ('re-run the 13-rule catalog over a candidate source string and diff the result against the file on disk') and enumerates the outcome categories, so the operation is unmistakable.
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?
Explicit USE WHEN and DO NOT USE clauses: use before writing a patch while the file is unmodified, and do not use afterwards because the baseline would equal the candidate. It also excludes the semantic-proof use case, which routes the agent away from ait_review-style tools.
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.
3 tool updates
v1.0.0- First observed
ait_review - First observed
ait_slice_context - First observed
ait_verify_fix
TDQS
Scored across 3 tools
Each tool has a clearly distinct role: whole-file/directory review, single-symbol extraction, and post-patch AST verification. The explicit USE WHEN / DO NOT USE clauses directly prevent the main overlaps, leaving little room for misselection.
The `ait_` prefix is consistent across all three tools, and the action suffixes are readable. The pattern is slightly uneven (`review` vs. `slice_context` vs. `verify_fix`), but the naming remains predictable enough for an agent.
Three tools are appropriate for a focused static-analysis workflow: audit, inspect one symbol, and verify a candidate fix. Each tool earns its place without redundancy or unnecessary surface area.
The core review-inspect-verify lifecycle is covered. Minor gaps remain, such as a tool to list rules or locate a symbol by name, but the descriptions acknowledge that symbol lookup is out of scope and workable around.
Maintenance
Related MCP Connectors
Statically audits MCP tool surfaces for token cost, schema quality, and design issues.
Security + bug + perf + refactor audit for Python. Returns 0-10 score + MD report.
MCP server for static security analysis of Android source code
Free MCP tools: the only MCP linter, health checks, cost estimation, and trust evaluation.
Related MCP Servers
- AlicenseAqualityAmaintenanceEnables MCP clients to run deterministic AST safety audits, secret scanning, and sandboxed test execution on codebases, with human approval required before any remediation action.5MIT
- FlicenseAqualityCmaintenanceExposes Semgrep as deterministic, rule-matching analysis tools over MCP so a code-review agent can scan only the lines changed in a diff, scan whole files on demand, and check analyzer availability, version, ruleset, and limits. Results are returned as compact per-finding lines with source snippets suitable as comment anchors, along with counts of suppressed out-of-diff matches.3-
- FlicenseNot gradedqualityBmaintenanceEnables static analysis of code snippets and public GitHub repositories using ruff and semgrep through a secure MCP SSE server.-
- AlicenseNot gradedqualityCmaintenanceEnables coding agents to select bounded Python source context, run configured checks, and retrieve compact failure reports, diagnostic comparisons, and freshness evidence tied to the checked code.MIT