Skip to content

MCP security

Teams wire AI agents to MCP servers faster than anyone reviews them. Every .mcp.json entry is third-party code (or a third-party endpoint) running with the agent’s privileges: typosquatted packages one transposition away from the official server, live secrets committed in server env config, filesystem roots that hand every session the whole home directory, unpinned versions that re-resolve latest on each launch, and remote endpoints nobody put through review.

RingZero treats the MCP config as a first-class security surface.

Scan

ringzero-mcp scan discovers every MCP server configured across your repos — Claude Code, OpenCode, VS Code, Cursor, Claude Desktop — and runs the canonical security checks against each one.

Policy & allowlist

A reviewable org allowlist in .ringzero/mcp-policy.json: propose → review → allow. Warn first, enforce when you’re ready.

MCP Registry

An org-controlled catalog that auto-configures approved MCP servers in every harness — pinned versions, no inline secrets, dev contributions via PR, drift caught in CI.

Usage auditing

Harness hooks record supported MCP tool calls to an append-only local audit log — who called which tool on which server, and what the policy decided.

Harness guards

A Claude Code hook and an OpenCode plugin evaluate each call against your policy live — denying dangerous commands and unapproved servers in enforce mode.

HarnessConfig discoveryUsage recordingBlockingIntegration
Claude Code.mcp.json, ~/.claude.json✅ PreToolUse / PostToolUse hooksBest-effort (see caveat)ringzero-mcp hook
OpenCodeopencode.json[c]tool.execute.before/afterEnforce mode, primary session@ringzero/opencode-mcp-guard
VS Code.vscode/mcp.jsonScan-onlydiscovery only
Cursor.cursor/mcp.json, ~/.cursor/mcp.jsonScan-onlydiscovery only
Claude Desktopclaude_desktop_config.jsonScan-onlydiscovery only

All five harnesses are also MCP Registry sync targets — the org catalog auto-configures approved servers in each one’s config format.

Trust is derived from open findings, never stored — a server’s status can never disagree with its finding list:

StatusMeaning
FlaggedAny open critical or high finding
UnverifiedOnly medium/low findings open
VerifiedNothing open

Verified describes the evaluated checks, not a certification of server safety. Unevaluated checks and unsupported harness traffic remain outside this result.

The org-wide fleet trust score is max(0, 100 − Σ weights) over all open findings, with weights critical 25 · high 15 · medium 5 · low 2 — deterministic and explainable from the current finding set alone, and it visibly recovers as you remediate.

CategorySeverityWhat it looks forEvaluated by the CLI
TyposquatcriticalPackage-name distance vs registry-official servers
Secrets in configcriticalCredentials inline in server env, args, or URLs
Excessive scopehighCapability broader than the project needs (e.g. filesystem root at $HOME)
Prompt injectionhighHidden instructions in tool descriptions, aimed at the calling agentComing soon (platform)
Unpinned versionmediumServer re-resolves latest on every session launch
Unapproved servermediumNot approved by policy or catalog✅ (needs a policy or a strict registry catalog; manifest caveat)
Unsigned packagelowResolved version published without npm provenance attestationComing soon (platform)

The two platform checks need live registry/server data the local CLI does not fetch; scan output always lists them in its checks manifest with evaluated: false and the reason — never silently missing.

GET /v1/mcp/servers

Required scope: mcp:read

The org-wide MCP server inventory, derived from the org’s latest uploaded scan (ringzero-mcp scan --upload): each discovered server with its trust status and findings, plus the provenance of the scan it came from. An org that hasn’t uploaded yet gets an empty inventory (scanId/scannedAt null), never a 404.

{
"scanId": "mscn_7g8h",
"scannedAt": "2026-08-16T10:00:00Z",
"servers": [
{
"id": "web/.mcp.json github",
"name": "github",
"source": "npm:@modelcontextprotocol/server-github@2025.4.8",
"configPath": "web/.mcp.json",
"transport": "stdio",
"trust": "unverified",
"findings": [
{
"id": "unapproved-server:github",
"title": "Server is not on the org allowlist",
"severity": "medium",
"category": "Unapproved server",
"summary": "github is not covered by the org policy or registry catalog.",
"status": "open"
}
]
}
]
}

id is a stable identifier derived from configPath + name, so a client’s selection survives re-scans (a bare name collides across config files). trust is the derived status (verified | unverified | flagged, per the trust model above); finding status is always open today — the fix-PR pipeline that moves findings to fixing/fixed is Coming soon, as are the platform-enriched finding fields (AI context and the recommended config diff) and usage metadata (usedBy, toolCount, scopes) — absent fields are simply omitted, never faked.