Skip to content

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.

Terminal window
npm install -g @ringzero/mcp-scan

Or run it ad-hoc (CI-friendly):

Terminal window
npx @ringzero/mcp-scan@latest scan

The CLI works fully local with no account. Connecting it to RingZero attributes scans and usage records to your org and enables uploads:

  1. 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": [] }
  2. Create an API key in Settings → API keys with the mcp:upload scope (that scope is all the CLI needs — keys can never do role-gated things).

  3. Export it where the CLI runs:

    Terminal window
    export RINGZERO_API_KEY=rz_key_5f2a
Terminal window
ringzero-mcp scan

Scans the current directory tree, prints a per-server trust report, and exits non-zero when gating findings exist — see exit codes.

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:

FileHarness
~/.claude.jsonClaude Code (user + per-project servers)
%APPDATA%\Claude\claude_desktop_config.json (Windows), ~/Library/Application Support/Claude/… (macOS), ~/.config/Claude/… (Linux)Claude Desktop
~/.cursor/mcp.jsonCursor
~/.config/opencode/opencode.jsonOpenCode

Editor-flavored JSON (comments, trailing commas) is handled.

Discover → check → apply policy → report.

FlagDoes
--jsonMachine-readable output (schemaVersion: 1)
--machineAdd 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
--uploadPush results to RingZero (needs mcp:upload key; skips gracefully without one)
--quietSuppress finding summaries in text output

Enforce-mode Unapproved server findings and requirePinned escalations gate regardless of --fail-on.

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.

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.

Print the CLI version.

{
"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
}
}
CodeMeaning
0Clean, or warn-only findings below the --fail-on threshold
1Gating findings present, or (for validate) invalid policy
2Usage or configuration error

hook always exits 0 — a broken guard must never lock a dev out.

VariableDoes
RINGZERO_MCP_POLICYPolicy file path (flag --policy wins)
RINGZERO_MCP_AUDIT_DIRAudit log directory (default ~/.ringzero/mcp-audit/)
RINGZERO_MCP_MODEOverride policy mode: warn or enforce
RINGZERO_MCP_LOG_ARGS1 opts into redacted raw args in audit records
RINGZERO_MCP_DISABLE1 = kill switch: allow everything, still audit
RINGZERO_MCP_REGISTRYRegistry catalog path (flag --registry wins)
RINGZERO_MCP_REGISTRY_AUTOSYNC0 disables the OpenCode plugin’s startup sync
RINGZERO_API_KEYPlatform key (mcp:upload) for --upload / audit sync
RINGZERO_API_URLAPI base (default https://api.ringzero.tech/v1)
RINGZERO_ORGANIZATIONOrg id override (policy organization otherwise)

Precedence: flag > env > policy file.

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:

.mcp.json
"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".

.mcp.json
"NOTION_TOKEN": "ntn_509a72b1c4d8e6f3a2b5c8d1e4f7a0b3"
"NOTION_TOKEN": "${env:NOTION_TOKEN}"

Filesystem roots at /, drive roots, $HOME, ~/.ssh; host-root container mounts; --dangerously-* safety overrides; wildcard env passthrough.

.mcp.json
"args": ["-y", "@modelcontextprotocol/server-filesystem@2025.1.14", "/Users/dev"]
"args": ["-y", "@modelcontextprotocol/server-filesystem@2025.1.14", "/Users/dev/acme-web"]

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.

.mcp.json
"args": ["-y", "mcp-web-search-pro"]
"args": ["-y", "mcp-web-search-pro@1.4.2"]

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.