File layout
Data flow
Key components
BaseScanner
Abstract base class inscanner/base.py. Every scanner extends this and implements a single method:
| Attribute | Example | Description |
|---|---|---|
name | "injection" | CLI name used for --checks filtering |
owasp_id | "MCP05" | OWASP MCP Top 10 category |
description | "Tests for command injection..." | Human-readable description |
Scanner registry (registry.py)
A static dictionary mapping CLI name strings to scanner classes:
--checks option filters by these names. get_scanner(name) instantiates a single scanner; get_all_scanners() returns instances of all ten.
Orchestrator (orchestrator.py)
run_scan(conn, check_names) coordinates the full scan lifecycle:
- Calls
enumerate_server(conn)to populate aScanContext - Resolves the scanner list from the registry (all, or filtered by
check_names) - Runs each scanner’s
scan()sequentially, collecting findings - Catches and logs per-scanner errors without aborting the run
- Returns a
ScanResultwith all findings and metadata
ScanResult
Dataclass aggregating scan output:| Field | Type | Description |
|---|---|---|
findings | list[Finding] | All findings from all scanners |
server_info | dict[str, Any] | Server metadata from MCP handshake |
tools_scanned | int | Number of tools tested |
scanners_run | list[str] | Names of scanners that executed |
started_at | datetime | Scan start time |
finished_at | datetime | None | Scan completion time |
errors | list[dict[str, str]] | Per-scanner errors |
Reporting pipeline
Three output formats, all generated from the sameScanResult:
| Format | File | Use case |
|---|---|---|
| JSON | json_report.py | Base format — machine-readable findings with full metadata |
| HTML | html_report.py | Human-readable report with severity summaries and finding details |
| SARIF | sarif_report.py | Static Analysis Results Interchange Format for CI/CD integration |