Skip to content

Claude Code integration

Claude Code exposes lifecycle hooks around every tool call. MCP tools are named mcp__<server>__<tool> (e.g. mcp__github__create_issue), so one matcher wires the RingZero guard around every MCP server at once — no proxy, nothing in the request path.

  • PreToolUse fires before a tool runs. Claude Code pipes the call as JSON to ringzero-mcp hook, which writes an audit record, evaluates your policy, and answers with a permission decision.
  • PostToolUse fires after — the guard records the outcome (event: "post").
  • Non-MCP tools pass through untouched, with no audit record.
  • The hook always exits 0 and fails open: a broken guard logs to stderr and allows. Enforcement posture comes from the policy file, not from crash behavior.
  1. Install the CLI where Claude Code runs:

    Terminal window
    npm install -g @ringzero/mcp-scan
  2. Connect your org — org id (org_9f3k from Settings → General) in the policy file, API key (Settings → API keys, scope mcp:upload) in RINGZERO_API_KEY. Optional but recommended: without it, auditing stays local-only (which also works).

  3. Make sure a policy file is reachable from your project (ringzero-mcp init to bootstrap).

  4. Print the hook wiring and merge it into .claude/settings.json (project, commit it for the whole team) or ~/.claude/settings.json (per-user):

    Terminal window
    ringzero-mcp init --print-hooks claude-code
    .claude/settings.json
    {
    "hooks": {
    "SessionStart": [
    {
    "hooks": [
    { "type": "command", "command": "npx -y @ringzero/mcp-scan registry sync --quiet" }
    ]
    }
    ],
    "PreToolUse": [
    {
    "matcher": "mcp__.*",
    "hooks": [
    { "type": "command", "command": "ringzero-mcp hook --harness claude-code --event pre" }
    ]
    }
    ],
    "PostToolUse": [
    {
    "matcher": "mcp__.*",
    "hooks": [
    { "type": "command", "command": "ringzero-mcp hook --harness claude-code --event post" }
    ]
    }
    ]
    }
    }

    The SessionStart hook is the MCP Registry deployment channel: every session applies the org catalog before work starts (silent no-op in repos without one).

  5. Verify: run any MCP tool in a Claude Code session, then

    Terminal window
    ringzero-mcp audit report --since 1h
Policy stateDangerous pattern matchUnapproved server
No policy fileaudited (allow)audited (allow)
mode: warnaudited as warn + a note in the sessionaudited as warn
mode: enforcedenieddenied when runtime.blockUnapproved: true

A denied call answers with permissionDecision: "deny" and a reason naming the rule, e.g. RingZero policy: server 'slack-mpc' is not on the org allowlist (.ringzero/mcp-policy.json). The guard never answers "allow" — approved calls fall through to your normal Claude Code permission flow, so the guard can only ever add protection.

Every MCP call produces a record like this in ~/.ringzero/mcp-audit/YYYY-MM-DD.jsonl (schema: Usage auditing):

{
"ts": "2026-07-26T14:03:11.402Z",
"org": "org_9f3k",
"harness": "claude-code",
"event": "pre",
"sessionId": "sess_01j9…",
"server": "github",
"tool": "create_issue",
"decision": "allow",
"rule": null,
"argsSha256": "9f2c8a41…",
"argsBytes": 412,
"args": null,
"cwd": "/work/acme-web",
"policyPath": "/work/acme-web/.ringzero/mcp-policy.json",
"policyMode": "warn"
}

pre events carry the decision; post events record that the call completed. Arguments are hashed, never stored raw, unless you opt in.

  1. Commit .claude/settings.json (hooks) and .ringzero/mcp-policy.json (allowlist) to the repo — every dev gets the guard on clone.
  2. Start in warn mode; review ringzero-mcp audit report weekly.
  3. Flip the committed policy to enforce (+ blockUnapproved) once the warn stream is quiet.
  4. Add the CI gate so config changes can’t outrun the allowlist.
  • A guard bug is blocking workRINGZERO_MCP_DISABLE=1 allows everything while still auditing; unset it after the fix.
  • Nothing appears in the audit log — check the hook is registered (/hooks in Claude Code), and that ringzero-mcp is on the PATH Claude Code uses.
  • Wrong policy picked up — the hook walks up from the session’s cwd; pin RINGZERO_MCP_POLICY when running outside the repo.