On this page
Workflows

CLI Reference

Every aevrin command, its flags, and its exit codes.

All commands print human-readable status to stderr and results to stdout, so aevrin scan ... > report.txt captures just the report. Every command supports --help.

Every command at a glance

CommandJSONLoginNetworkDockerExit codes
scan--jsonrequiredrequiredrecommended0 / 1 / 2 / 3
scan mcp "<command>"--jsonrequiredrequiredrequired0 / 1 / 2 / 3
scan --remote--jsonrequiredrequirednot used0 / 1 / 2 / 3
agent scan--jsononly for --upload or --scan-serversonly for --upload or --scan-serversonly for --scan-servers0 / 2 / 3
loginnonot requiredrequirednot used0 / 2
logoutnonot requirednot usednot used0
versionnonot requirednot usednot used0
findings triagenorequiredrequirednot used0 / 2
mcp-headeralwaysrequirednot usednot used0 / non-zero
mcp-servernonot requiredrequiredrecommendedruns until stopped
registry publishnoadministrators onlyrequirednot used0 / 2

Docker is "recommended" rather than required for scan because the scan still runs without it: it just cannot run every scanner, which is reported as exit 3 and an incomplete result, never as a clean pass. logout is deliberately exit 0 whether or not you were logged in, so it is safe in a teardown script.

scan

aevrin scan TARGET [OPTIONS]

Runs the full scan pipeline against TARGET and prints a report.

ArgumentDescription
TARGETA GitHub URL, a local directory path, a live MCP server URL, or the literal word mcp followed by the command that starts a server. Required.
SERVER_COMMANDOnly after aevrin scan mcp: the command that starts the MCP server, for example "npx -y @playwright/mcp". A second argument after any other target is an error.
OptionDefaultDescription
--jsonoffMachine-readable JSON output instead of the terminal report.
--upload / --no-upload--uploadSave the result to your dashboard. On by default when logged in. Pass --no-upload for a purely local, ephemeral scan (e.g. in CI, where you don't want scan history persisted).
--fail-on SEVERITYhighMinimum finding severity that causes a non-zero exit code. One of critical, high, medium, low, info.
--remoteoffScan a local folder on Aevrin's servers instead of this machine. Local paths only. See scan --remote.
--engine-report PATHoffAlso write the scan engine's full JSON report to this file. It is written for a scan that could not finish too, and then may include a public report from an earlier scan of the same server, which is not this scan's result. Not with --remote: download that scan's report from the dashboard. A folder, a missing folder, or no report to write is exit 2.

The same checks run whatever the target is. One engine runs over the tools a live server actually returns, in five stages: resolving (work out the command that starts this server), launching (start it, in a container that assumes it is hostile), enumerating (complete the MCP handshake and read its tools), analyzing (run the rules over what was read) and grading (show the engine's grade for the whole server and write the summary). A repository and a live server differ only in how resolving works out what to start, not in which checks then run.

A live target must use public HTTPS; local, private, metadata, and credential-bearing URLs are rejected to prevent SSRF.

When the target is code that holds no MCP server at all (no server package and no MCP SDK), the report says "This is not an MCP server." and stops. A repository that uses an MCP SDK but publishes nothing Aevrin can start, or that holds several servers, is told exactly that instead, and is never called "not an MCP server".

When you upload, the dashboard also receives the engine's full report (up to 1 MB) and each finding's engine id, so you can download the report there and ask for an AI review of the findings.

Exit codes:

CodeMeaning
0Scan completed; no findings at or above --fail-on.
1Scan completed; at least one finding at or above --fail-on.
2Could not run at all: not logged in, quota exceeded, unreachable API, an invalid target or flag, or a target that could not be fetched (a repository that will not clone). Nothing is written to stdout, so a --json redirect leaves an empty file rather than a partial one.
3Scan ran but is incomplete: a stage could not finish (Docker not running, the engine image missing, no network). This fires regardless of --fail-on, because an incomplete scan is never treated as a pass. See Concepts → Incomplete scans.
Fail a CI job only on unresolved criticals, keep local scan history out of the dashboard
aevrin scan . --fail-on critical --no-upload

The CLI uses version-pinned, disposable Linux containers. Keep Docker Desktop running with at least 4 GB assigned, use Linux containers on Windows, and allow its temporary-directory bind mounts. Current errors include a redacted scanner stderr excerpt, so an image-pull, mount, network, timeout, or memory problem is distinguishable from a vulnerability finding.

scan mcp "<command>"

aevrin scan mcp "npx -y @playwright/mcp"

Scans a server by the command that starts it, rather than by a repository or a URL. Aevrin starts that command in an isolated container on this machine, reads the tools the running server actually registers, and runs the same checks against them. Docker is not optional here: with no sandbox to start the server in, the scan is reported incomplete with exit 3 and no grade, never as clean.

It exists because it is the only target that needs no guessing. Every other target has to be resolved to a launch command first, and resolving the wrong one grades somebody else's package under this one's name. If you know the command your agent will run, scan that command.

Quote the whole command as one argument. A second argument after any other target is rejected, and aevrin scan mcp with no command exits 2 rather than guessing.

The local mcp-server exposes this same form as one MCP tool, so an agent can check a server before you install it.

login

aevrin login

Starts the browser device-code flow and stores an API key at ~/.aevrin/credentials. Running it again while already logged in prints a warning and exits 0. Run aevrin logout first to switch accounts.

logout

aevrin logout

Removes the stored CLI credentials.

version

aevrin version

Prints the installed version. Equivalent to the top-level aevrin --version flag.

scan --remote

aevrin scan ./my-project --remote

Scans a local folder on Aevrin's servers instead of on this machine. The engine image is already there, so this needs no Docker locally - which is the whole reason it exists, since a scan without Docker is a scan that does not run.

The folder's source is uploaded. In a git repository the file list is git ls-files --cached --others --exclude-standard, so whatever .gitignore excludes is excluded here too; otherwise dependency directories, build output, and virtualenvs are pruned. .git is never sent, since its history carries every secret that was ever committed and later removed. A 62 MB folder typically packs to well under a megabyte of actual source.

The report is identical to a local scan, and the result is already on your dashboard because the server produced it. The uploaded copy is deleted when the scan finishes, and nothing in it is executed.

Only for local paths. A GitHub URL or a live MCP server is already reachable from Aevrin, so scan those from the dashboard.

agent scan

aevrin agent scan
aevrin agent scan --json
aevrin agent scan --project ./my-repo --verbose
aevrin agent scan --upload
aevrin agent scan --scan-servers

Reports what the AI coding agents on this machine have been allowed to do: permissions, MCP servers by scope, skills, plugins, hooks, and which credentials are within reach. Configuration is read and nothing else: no agent is started, and nothing from a config file is executed, unless you pass --scan-servers. Nothing leaves the machine unless you pass --upload.

--upload sends the snapshot to your dashboard, where it appears under AI security → Agents. It counts as one agent posture scan, not as a CLI or dashboard scan: no scanner runs on the server, it stores the document your machine already produced. Before it is sent, anything in a configured server's arguments or address that looks like a secret (--token X, --api-key=X, a password in a URL, a ?token= value) is replaced with [REDACTED]. Running it again from the same machine replaces that machine's record. The dashboard is read-only about your machines and cannot change any configuration on them.

--json returns a versioned snapshot (schema_version "1") that is stable across runs, so two snapshots can be diffed. Credential values are never included; only that one is present and where. See AI agent posture for what is discovered and how permissions are resolved.

--scan-servers

After the report, scans each MCP server your agents use, on this machine, the same way scan mcp does. It needs Docker and a login.

  • What is scanned. Each distinct server once: an npm package as npx -y <package>, a PyPI package as uvx <package>, and a public HTTPS address as itself. A Docker image, a local program or an entry with no package or address is listed as skipped, with the reason.
  • Only the package or address. The arguments and environment you configured are never used, because they often hold a password or a key, and a scan must never hand your secrets to the code it is testing.
  • You say yes first. Every command is printed, then it asks before starting. --yes (-y) skips the question; with no terminal to answer (as in CI) and no --yes, nothing is scanned.
  • At most 10 servers in one run. Scan the others one at a time with aevrin scan mcp.
  • Results. One line per server: its grade, or "incomplete" with what could not finish. With --upload, each scan is saved to your dashboard as a CLI scan, and the dashboard's agent pages then show that server's grade.
  • It cannot be combined with --json.

Exit codes with --scan-servers: 0 every scan finished, 3 at least one could not finish (if the sandbox cannot run at all, the rest are skipped), 2 your credits ran out (the rest are skipped) or --json was also given.

findings triage

aevrin findings triage FINDING_ID STATUS [--reason TEXT]

Updates a finding's triage status. This is the "false report" path. STATUS is one of open, fixed, or false_positive. False-positive reports require --reason; the reason and triage timestamp are retained with the dashboard finding and exported report. Marking a finding false_positive excludes it from open-risk summaries. FINDING_ID comes from a --json scan's findings[].id. The command uses the credential from aevrin login.

Dispute a finding
aevrin findings triage 3fa85f64-5717-4562-b3fc-2c963f66afa6 false_positive \
  --reason "Generated test fixture; cannot authenticate against any environment"

mcp-header

aevrin mcp-header

Prints the HTTP header that authenticates you to Aevrin MCP, as one line of JSON, and nothing else:

{"Authorization":"Bearer aevrin_..."}

It reads the same key as every other command: AEVRIN_API_KEY when it is set, otherwise the key aevrin login stored at ~/.aevrin/credentials. With no key it writes an error to stderr, prints nothing to stdout, and exits non-zero. It exists so Claude Code can run it as the server's headersHelper and the key never has to be pasted into Claude Code's configuration:

claude mcp add-json --scope user aevrin '{"type":"http","url":"https://api.mcp.aevrin.net/mcp","headersHelper":"aevrin mcp-header"}'

The output is your API key. Do not paste it into a chat, a log, or an issue.

mcp-server

pip install "aevrin[mcp]"
aevrin mcp-server

Runs a local MCP server over stdio with one tool, scan_mcp_server, so an agent can scan an MCP server by its launch command before you install it. It runs the same pipeline as aevrin scan mcp "<command>" and returns the same verdict: the grade, risk score, suggested policy, the tools found, one line per finding, and a plain-language summary. An incomplete scan comes back with no grade and says why, never as clean.

You don't run it by hand. Add it to your agent's configuration:

{ "mcpServers": { "aevrin-scan": { "command": "aevrin", "args": ["mcp-server"] } } }

The scan starts the server it scans, in an isolated container, so it runs only on your machine and needs Docker for full coverage. It is not part of the hosted Aevrin MCP. The hosted server has its own scan_mcp_server, a different tool: it takes only a public GitHub repository or a live https URL and scans it on Aevrin's servers (see Tools). The MCP SDK is an optional extra, so the base CLI that CI installs does not carry it.

registry publish

aevrin registry publish ./items/my-item

Administrators only. It publishes a Registry item that was authored as files, so an item can be reviewed in a pull request instead of being retyped into the admin panel. It is listed here because aevrin --help shows it; nothing about it is usable without administrator access.

It does not use the credential from aevrin login: the admin routes do not accept an API key, so the command reads an administrator's current session token from the environment for the one invocation and writes nothing to disk.

OptionDescription
--allTreat the path as a directory of item directories.
--draftCreate or update the item and leave it a draft. Nothing becomes discoverable.
--dry-runCheck the files and print what would be sent. No request, no credentials.
--reason TEXTRecorded in the item's history and the admin audit log.

Every file is read and checked before anything is sent, and every problem is reported in one run, so a mistake in the fourth item cannot leave the first three published. Exit 0 on success or a clean --dry-run, 2 for a bad path, a malformed item, a missing credential or a refusal.

It cannot approve a capability version, by design. Approval decides what code Aevrin runs in its own sandbox: it needs the administrator's authentication code presented with the request and stays in the admin panel. An item published here is discoverable; it becomes runnable only once a version is approved there. See Aevrin MCP tools for what the two outcomes mean to an agent.

Reading item.yaml needs an optional extra, for the same reason the MCP server does: a CI job that only scans has no use for a YAML parser.

pip install "aevrin[registry]"