Scanner CLI reference
The scanner ships as the ringzero-mcp CLI in the @ringzero/mcp-scan npm
package. It is offline-first: discovery, checks, policy, and auditing all run
locally with zero runtime dependencies. Platform upload is optional.
Install
Section titled “Install”npm install -g @ringzero/mcp-scanOr run it ad-hoc (CI-friendly):
npx @ringzero/mcp-scan@latest scanConnect to your organization
Section titled “Connect to your organization”The CLI works fully local with no account. Connecting it to RingZero attributes scans and usage records to your org and enables uploads:
-
Grab your org id (
org_…) from Settings → General and set it in your policy file:.ringzero/mcp-policy.json { "version": 1, "organization": "org_9f3k", "mode": "warn", "allow": [] } -
Create an API key in Settings → API keys with the
mcp:uploadscope (that scope is all the CLI needs — keys can never do role-gated things). -
Export it where the CLI runs:
Terminal window export RINGZERO_API_KEY=rz_key_5f2a…
Quick start
Section titled “Quick start”ringzero-mcp scanScans the current directory tree, prints a per-server trust report, and exits non-zero when gating findings exist — see exit codes.
What gets discovered
Section titled “What gets discovered”Repo scope walks the tree (skipping node_modules, .git, build output):
Directoryacme-web/
- .mcp.json Claude Code (project)
Directory.vscode/
- mcp.json VS Code
Directory.cursor/
- mcp.json Cursor
- opencode.json OpenCode (also .jsonc)
Directoryservices/
Directorybilling/
- .mcp.json nested configs are found too
--machine additionally scans user-level configs:
| File | Harness |
|---|---|
~/.claude.json | Claude Code (user + per-project servers) |
%APPDATA%\Claude\claude_desktop_config.json (Windows), ~/Library/Application Support/Claude/… (macOS), ~/.config/Claude/… (Linux) | Claude Desktop |
~/.cursor/mcp.json | Cursor |
~/.config/opencode/opencode.json | OpenCode |
Editor-flavored JSON (comments, trailing commas) is handled.
Commands
Section titled “Commands”scan [path]
Section titled “scan [path]”Discover → check → apply policy → report.
| Flag | Does |
|---|---|
--json | Machine-readable output (schemaVersion: 1) |
--machine | Add user-level config locations |
--policy <file> | Explicit policy file (default: walk-up for .ringzero/mcp-policy.json) |
--fail-on <sev> | Gate on critical, high (default), medium, low, or never |
--upload | Push results to RingZero (needs mcp:upload key; skips gracefully without one) |
--quiet | Suppress finding summaries in text output |
Enforce-mode Unapproved server findings and requirePinned escalations gate
regardless of --fail-on.
validate
Section titled “validate”Lint the policy file: schema, glob syntax, duplicate entries, unknown keys, invalid regexes. Exit 0 valid · 1 invalid · 2 no file found.
Stdin JSON handler for Claude Code hooks — see Claude Code integration. Always exits 0 (fail-open); decisions ride the JSON output.
audit report | tail | sync
Section titled “audit report | tail | sync”Summarize, follow, or upload the usage audit log — see Usage auditing.
registry sync | status | list | validate | add | remove | pull
Section titled “registry sync | status | list | validate | add | remove | pull”The MCP Registry command group — apply the org
catalog to harness configs, report drift (sync --check exits 1 for CI),
and propose internal servers. No catalog present → sync/status are
silent exit-0 no-ops.
Scaffold .ringzero/mcp-policy.json with every discovered server as a
proposed allow entry (the propose → review → allow bootstrap), or print
harness wiring with --print-hooks claude-code | opencode.
version
Section titled “version”Print the CLI version.
JSON output
Section titled “JSON output”{ "schemaVersion": 1, "scannedAt": "2026-07-26T14:03:11.402Z", "root": "/work/acme-web", "organization": "org_9f3k", "policyPath": "/work/acme-web/.ringzero/mcp-policy.json", "policyMode": "warn", "servers": [ { "server": { "name": "slack", "source": "npm:slack-mpc", "configPath": ".mcp.json", "harness": "claude-code", "transport": "stdio" }, "trust": "flagged", "findings": [ { "id": "typosquat.slack", "category": "Typosquat", "severity": "critical", "title": "Possible typosquat of @modelcontextprotocol/server-slack", "summary": "Unscoped package 'slack-mpc' shadows the official '@modelcontextprotocol/server-slack' — a classic typosquat shape.", "confidence": "high", "gating": true } ] } ], "checks": [ { "category": "Typosquat", "evaluated": true }, { "category": "Prompt injection", "evaluated": false, "reason": "requires connecting to the server — tool descriptions are runtime data; platform check, coming soon" } ], "summary": { "servers": 8, "configFiles": 4, "counts": { "critical": 1, "high": 0, "medium": 2, "low": 0 }, "trust": { "verified": 5, "unverified": 2, "flagged": 1 }, "trustScore": 65, "gating": 1 }}Exit codes
Section titled “Exit codes”| Code | Meaning |
|---|---|
| 0 | Clean, or warn-only findings below the --fail-on threshold |
| 1 | Gating findings present, or (for validate) invalid policy |
| 2 | Usage or configuration error |
hook always exits 0 — a broken guard must never lock a dev out.
Environment variables
Section titled “Environment variables”| Variable | Does |
|---|---|
RINGZERO_MCP_POLICY | Policy file path (flag --policy wins) |
RINGZERO_MCP_AUDIT_DIR | Audit log directory (default ~/.ringzero/mcp-audit/) |
RINGZERO_MCP_MODE | Override policy mode: warn or enforce |
RINGZERO_MCP_LOG_ARGS | 1 opts into redacted raw args in audit records |
RINGZERO_MCP_DISABLE | 1 = kill switch: allow everything, still audit |
RINGZERO_MCP_REGISTRY | Registry catalog path (flag --registry wins) |
RINGZERO_MCP_REGISTRY_AUTOSYNC | 0 disables the OpenCode plugin’s startup sync |
RINGZERO_API_KEY | Platform key (mcp:upload) for --upload / audit sync |
RINGZERO_API_URL | API base (default https://api.ringzero.tech/v1) |
RINGZERO_ORGANIZATION | Org id override (policy organization otherwise) |
Precedence: flag > env > policy file.
Check reference
Section titled “Check reference”Typosquat critical
Section titled “Typosquat ”Damerau-Levenshtein distance against a curated list of registry-official
servers, plus unscoped-package shadowing (slack-mpc vs
@modelcontextprotocol/server-slack). Fix — swap to the pinned official
package:
"args": ["-y", "slack-mpc"]"args": ["-y", "@modelcontextprotocol/server-slack@2025.3.2"]Secrets in config critical
Section titled “Secrets in config ”Known token prefixes (ghp_, xoxb-, AKIA, rz_key_, …), connection-string
passwords, and URL userinfo in env, args, or URLs. ${…} references are
exempt — that is the fix. High-entropy literals report with
"confidence": "low".
"NOTION_TOKEN": "ntn_509a72b1c4d8e6f3a2b5c8d1e4f7a0b3""NOTION_TOKEN": "${env:NOTION_TOKEN}"Excessive scope high
Section titled “Excessive scope ”Filesystem roots at /, drive roots, $HOME, ~/.ssh; host-root container
mounts; --dangerously-* safety overrides; wildcard env passthrough.
"args": ["-y", "@modelcontextprotocol/server-filesystem@2025.1.14", "/Users/dev"]"args": ["-y", "@modelcontextprotocol/server-filesystem@2025.1.14", "/Users/dev/acme-web"]Unpinned version medium
Section titled “Unpinned version ”npx/bunx specs without an exact version (or with @latest), uvx without
==, docker :latest or untagged, git without a commit SHA. Remote URLs are
exempt — there is nothing to pin client-side.
"args": ["-y", "mcp-web-search-pro"]"args": ["-y", "mcp-web-search-pro@1.4.2"]Unapproved server medium
Section titled “Unapproved server ”Allowlist match against your policy. Without a
policy file the check reports evaluated: false and suggests
ringzero-mcp init.
Prompt injection & Unsigned package Coming soon
Section titled “Prompt injection & Unsigned package ”Both need data the local CLI does not fetch (live tool descriptions; npm
provenance attestations). They are platform checks and appear in the checks
manifest with evaluated: false and the reason.