Skip to main content
Glama

IMPORTANT

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 None come from

Causation trace: every step carries file:line:col from the producer to the point of failure

How do I reproduce it

Repro witness: the smallest code that triggers the symptom, plus a safe_to_execute flag

Will the fix break something else

Suggested fix: a unified diff that passes ast.parse and is rescanned for new findings

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 src

Or install the ait / ait-mcp commands (still no dependencies):

pip install -e .
ait src

The 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: 1

The 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

AIT-Z01

DEFECT

Popen(..., stdout=PIPE) followed by wait() without draining the pipe

a pipe exists and the child is waited on without a prior communicate()/read()

AIT-Z02

DEFECT

a child handle is dropped without wait/poll/returning ownership

the variable resolves to a process handle

AIT-Z03

WARNING

a process/thread spawned at module scope without if __name__ == "__main__":

the call really sits at module level

AIT-R01

DEFECT / WARNING

read-modify-write on shared state with no lock around it

concurrency evidence (spawn site, async def, or a lock in the class); otherwise not reported

AIT-R02

WARNING

a condition checked outside the lock and again inside it

a real lock region and the same condition

AIT-A01

DEFECT / WARNING

a blocking call inside async def

time.sleep/network/process = DEFECT, file I/O = WARNING; offloading through to_thread/executor is not reported

AIT-A02

WARNING

create_task() with nothing holding a reference

not stored in a variable, not awaited, no done callback

AIT-A03

DEFECT

a coroutine called without await

the callee resolves to a coroutine in the same file or to a known API

AIT-M01

DEFECT

mutating a caller's parameter in place (append, pop, update, ...)

the write targets the parameter name directly

AIT-M02

DEFECT

a mutable default argument ([], {}, set(), ...)

a real mutable literal or constructor

AIT-N01

DEFECT

dereferencing a value that may be None without a guard

evidence the value is optional (annotation, None literal, a producer returning optional, dict.get)

AIT-H01

WARNING

a bare except: that swallows everything

except: raise (bare re-raise) is not reported because nothing is swallowed

AIT-H02

ADVICE

more than four levels of nested control flow

counts real control nodes only and never crosses a scope

NOTE

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

-v, --verbose

per-file detail

-q, --quiet

summary only

-x, --exitfirst

stop at the first file carrying a DEFECT

--lf, --last-failed

scan only the files that failed last run (needs the cache)

--fix

show a diff and ask one finding at a time (needs a TTY)

--no-cache

skip both cache reads and writes

--maxfail N

show at most N failure blocks

--no-color

disable ANSI (or set NO_COLOR=1)

--version

print the version

Exit code: 0 clean ยท 1 DEFECT present ยท 2 interrupted ยท 3 parse or internal error ยท 4 usage error

TIP

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.

  1. every patch is passed through ast.parse again before it is offered

  2. findings are asked one at a time (y / N / a for all / q to quit)

  3. before writing, the original is copied to .ait_backup/<timestamp>-<pid>/

  4. the file is written to a temporary path and moved with os.replace (atomic), as bytes, so line endings are preserved

  5. the file is written back with its original codec (utf-8-sig keeps the BOM, a BOM-less UTF-8 file stays BOM-less, cp874 stays cp874) if a patch contains characters the original codec cannot encode, the write is refused rather than silently converting the file to UTF-8

  6. that file is rescanned; any new finding triggers an immediate rollback

  7. 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-N01
except:  # ait: disable=H01
    pass
except:  # ait: disable=ALL
    pass

A 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

ait_review

scans a file or directory and returns findings with causation, witness and diff

ait_slice_context

returns source, variable table, calls and callers for one symbol (token-efficient)

ait_verify_fix

compares candidate code against the baseline: what is resolved and what is newly introduced

Tool

Arguments

ait_review

path (required), severity_min = ADVICE/WARNING/DEFECT, max_findings = 1โ€“500 (default 50), exitfirst, use_cache

ait_slice_context

file_path, symbol_name (required), depth = 0โ€“3 (default 1)

ait_verify_fix

file_path, candidate_code (required)

  • isError: false when the tool ran successfully even if it found a DEFECT, because a finding is a usable result, not a tool failure (isError: true is 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 -32602 instead of being ignored or silently clamped

  • an unexpected exception (say RecursionError) is returned as -32603 and the process keeps serving requests

  • shutdown and notifications/exit stop the serve loop; initialize does not advertise a logging capability because none is used

  • structuredContent holds the object once; content[0].text is 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; python -S still scans

Works offline

โœ… yes

no HTTP client, no telemetry

Causation trace

โœ… every finding

causation[] with file:line:col from producer to failure point

Repro witness

โœ… text + syntax check

never executed; deadlock/recursion demos carry safe_to_execute: false

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 N suppressed

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 src/ (23 files, cold, --no-cache)

642 ms

725 ms

510 ms

Rescan (warm cache)

12.3 ms

270 ms*

9.9 ms

Test suite (unittest, 268 tests)

6.9 s OK

17.3 s OK

6.8 s OK

DEFECT / WARNING / ADVICE on src/

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

cli.py

flag parsing, rendering, the --fix session, exit codes

Frontend

mcp.py

JSON-RPC framing, tool schema and validation, stdio loop

Core

engine.py

discover โ†’ parse โ†’ analyze โ†’ cache โ†’ aggregate

Core

context.py

guard/dominance lookup, suppression, evidence and patch builders

Flow

flow/scope_tree.py

symbol table, lexical scopes, binding resolution

Flow

flow/call_graph.py

call sites, lock regions, spawn sites

Flow

flow/data_flow.py

backward tracing of optional/None sources

Flow

flow/witness.py

minimal repro text and syntax validation

Rules

rules/*.py

13 detectors (Z/R/A/M/N/H)

Infra

cache.py

sqlite3 WAL, digest and ruleset keyed, prune, --lf index

Infra

models.py

immutable Diagnostic/Patch/Witness, exit codes, SCHEMA_VERSION

CI

.github/workflows/ci.yml

test matrix, self-scan, stdlib-only run, integration tests

CI

.github/workflows/codeql.yml

CodeQL security-and-quality, tests/fixtures excluded

CI

tests/test_cli_integration.py

drives the real cli.py / mcp.py through subprocess and asserts exit codes and JSON


๐Ÿงช Tests

No pytest required:

python -m unittest discover -s tests -v

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

Confirmed 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

L:\.Python312

CPython 3.13.15

L:\.Python313

CPython 3.14.7

L:\.Python314

CPython 3.15.0rc2

L:\.Python315

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 OK

All 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

python -m unittest discover -s tests

268 tests on that interpreter

must be OK

-W error::DeprecationWarning (3.15 only)

whether it uses APIs scheduled for removal

must be OK

python cli.py src --no-cache

the engine scanning itself

exit 0 ยท DEFECT/WARNING/ADVICE = 0

python -S cli.py src --no-cache

no site-packages at all

exit 0, so nothing third-party is required

tests/test_cli_integration.py (scans tests/fixtures through cli.py)

a corpus that is meant to fail

exit 1 and a DEFECT in the output

tests/test_mcp_protocol.py (mcp.py over stdio)

real JSON-RPC (initialize โ†’ tools/call โ†’ shutdown)

isError:false ยท 0 findings ยท shutdown answered

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

package

pip wheel + a clean venv

builds the wheel, installs it with --no-index --no-deps, runs ait --version, ait sample.py and ait-mcp outside the source tree

exit 0 for every command

package

importlib.metadata

metadata must have no Requires-Dist and all 5 modules must import

requires == None

lint

Ruff 0.17.2

E4/E7/E9/F (undefined names, unused imports, syntax)

0 findings

lint

Bandit 1.9.4

medium and above in src, cli.py, mcp.py

0 findings

lint

compileall

every module compiles

exit 0

lint

actionlint 2.2.0

workflow syntax and expressions

exit 0

security

OSV-Scanner 2.7.0

the dependency surface against the OSV database

No issues found

security

shell guard

any requirements*.txt / uv.lock / poetry.lock must stop the run for a real scan

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

WARNING

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-R01 reports 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 raise or return depends 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, structuredContent and a lifecycle that really closes

๐Ÿ” 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 tools
ait_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.

NameRequiredDescriptionDefault
pathYesFile or directory to analyse, e.g. src/ait/mcp.py or src/ait.
exitfirstNoStop at the first file carrying a DEFECT.
use_cacheNoReuse the BLAKE2b cache; set false for one-shot scans that must not write into the tree.
max_findingsNoCap the returned findings (sorted by path,line); the tally still counts every finding.
severity_minNoDrop findings below this severity, e.g. DEFECT to return only defects.ADVICE

TDQS

A4.6/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

NameRequiredDescriptionDefault
depthNoCall-graph hops to expand (0 = signature only, 3 = maximum). Out-of-range values are rejected, not clamped.
file_pathYesExisting Python file that contains the symbol, e.g. src/ait/slicer.py.
symbol_nameYesFunction or class name; qualify it as Class.method when ambiguous, e.g. Engine.scan.

TDQS

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

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

NameRequiredDescriptionDefault
file_pathYesUnmodified baseline file on disk, e.g. src/ait/engine.py.
candidate_codeYesFull replacement source text (not a diff), e.g. 'def f(x):\n return x\n'.

TDQS

A4.8/5.0
Behavior5/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

  1. 3 tool updatesv1.0.0
    • First observedait_review
    • First observedait_slice_context
    • First observedait_verify_fix

TDQS

A4.6/5.0

Scored across 3 tools

Disambiguation5/5

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.

Naming Consistency4/5

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.

Tool Count5/5

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.

Completeness4/5

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

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    A
    quality
    C
    maintenance
    Exposes 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
    -
  • F
    license
    Not graded
    quality
    B
    maintenance
    Enables static analysis of code snippets and public GitHub repositories using ruff and semgrep through a secure MCP SSE server.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables 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