Suvra

Agent runtimes

Suvra supports exactly four agent runtimes directly, plus any MCP server through suvra mcp wrap. Frameworks are never installed as a dependency of Suvra — install the runtime you use, then wire in the matching hook. This page is the canonical per-runtime reference.

RuntimeOne-liner
Claude Codesuvra init claude-code — installs a PreToolUse hook into .claude/settings.json
OpenAI Codexsuvra init codex — installs a PreToolUse hook into .codex/hooks.json
Hermessuvra init hermes — merges a pre_tool_call shell hook into ~/.hermes/config.yaml
OpenClawOpenClawGuard(suvra) wraps the OpenClaw runtime
Claude Agent SDKsuvra_hook(suvra) as a PreToolUse HookMatcher
Any MCP serversuvra mcp wrap -- <cmd> — wraps any stdio MCP server, gates tools/call through the firewall

All four runtimes have been verified end-to-end against the real agent binaries — not mocks — including blocking a real rm -rf under Codex running with --dangerously-bypass-approvals.


Claude Code

suvra init claude-code

Writes a PreToolUse hook entry into .claude/settings.json in the current project so every tool call Claude Code makes is validated by Suvra first.

Wrote hook entry to .claude/settings.json
Claude Code tool calls in this project are now checked by Suvra before they run.
Run `suvra demo` any time to re-verify the classifier is active.

Re-run claude in this directory and try asking it to run something destructive in a scratch repo — the tool call should come back denied with a reason instead of executing.

OpenAI Codex

suvra init codex

Writes the same PreToolUse hook shape into .codex/hooks.json (use --user for ~/.codex/hooks.json instead). Codex uses the identical hookSpecificOutput / permissionDecision JSON contract as Claude Code — only the config file location and the matcher differ (Codex matchers are regexes, so Suvra installs .* as the catch-all instead of Claude's *).

suvra hook already emits Codex-compatible output, so the same command validates both runtimes' tool calls, including Codex's apply_patch file edits.

Hermes

suvra init hermes

Merges a hooks.pre_tool_call entry into ~/.hermes/config.yaml — Hermes's config is a global, per-user file with no project-local override, so this is the target regardless of the current directory (pass --config-path to override, e.g. for a $HERMES_HOME-based install):

hooks:
  pre_tool_call:
    - command: "suvra hook --runtime hermes"
      matcher: ".*"
      timeout: 30

suvra hook already emits Hermes-compatible output: alongside the usual nested hookSpecificOutput block it also emits a flat top-level {"decision": "block", "reason": "..."} (or nothing, on allow) — the exact shape Hermes's shell-hook bridge parses. Since this bridge has no async confirmation channel, Suvra's needs_approval decisions are mapped to block for Hermes (fail-closed).

The first time Hermes runs with this hook configured, it will either prompt for interactive consent at the TTY, or — for non-interactive runs — you must pass --accept-hooks or set HERMES_ACCEPT_HOOKS=1 (or hooks_auto_accept: true in config.yaml) so the hook registers without a prompt.

OpenClaw

from suvra import Suvra
from suvra.integrations.openclaw import OpenClawGuard

suvra = Suvra(policy="policy.yaml", agent_id="research-agent")
guard = OpenClawGuard(suvra)

guard.execute("fs.write_file", {"path": "workspace/notes.txt", "content": "hi"})

OpenClawGuard(suvra, default_actor="openclaw") wraps a Suvra SDK instance with execute/validate methods shaped for OpenClaw's action-execution model.

Claude Agent SDK

from suvra.integrations.anthropic import suvra_hook, suvra_agent_options

hook = suvra_hook(suvra, fail_closed=True)
options = suvra_agent_options(suvra, fail_closed=True)
  • suvra_hook(suvra, *, fail_closed=False) — async PreToolUse hook. Rejects tool calls whose decision isn't allow. On Suvra errors, returns empty unless fail_closed=True.
  • suvra_agent_options(suvra, *, fail_closed=False) — convenience dict you can merge into ClaudeAgentOptions(...) so the hook is registered.

Any MCP server

suvra mcp wrap -- npx -y @modelcontextprotocol/server-filesystem .

suvra mcp wrap launches the given command as a subprocess, speaks stdio JSON-RPC to it exactly like a normal MCP client would, and gates every tools/call request through the firewall before it reaches the wrapped server. Everything else (initialize, tools/list, etc.) passes through unmodified.

suvra: wrapping `npx -y @modelcontextprotocol/server-filesystem .`
suvra: firewall active — tools/call requests will be validated before execution

Point your MCP client (Claude Desktop, Claude Code, etc.) at the suvra mcp wrap command instead of the raw server command, and every tool call gets evaluated first. Wrapped tools appear to policy as mcp.<tool_name> action types, so rules can match mcp.* or individual tools.

If you're running a long-lived server over HTTP/SSE instead of stdio, see the MCP gateway mode instead.

Direct SDK usage (any other runtime)

For runtimes without a dedicated suvra init command, call Suvra's decision logic directly wherever your agent is about to run a tool. suvra.hooks.claude_code (the same module backing suvra hook and all the init commands above) exposes decide_pretooluse, which works for any tool-call-shaped payload:

from suvra.hooks.claude_code import decide_pretooluse

result = decide_pretooluse({"tool_name": "Bash", "tool_input": {"command": "rm -rf /"}})
# {"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", ...},
#  "decision": "block", "reason": "guardrails: recursive force delete ..."}

if result["hookSpecificOutput"]["permissionDecision"] != "allow":
    raise RuntimeError(f"Suvra blocked this tool call: {result['hookSpecificOutput']['permissionDecisionReason']}")

The output always includes the nested hookSpecificOutput.permissionDecision (allow/deny/ask); on deny or ask it additionally includes a flat top-level decision: "block" + reason mirror for runtimes (like Hermes) that only check a top-level key.

Connecting to a shared control plane

Everything above works standalone with the local firewall. If you're running a shared control plane (Team / Enterprise), agents additionally register against it so decisions, approvals, and audit land in one place — see Agents → Registration for the SDK and API registration schema.