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.
| Runtime | One-liner |
|---|---|
| Claude Code | suvra init claude-code — installs a PreToolUse hook into .claude/settings.json |
| OpenAI Codex | suvra init codex — installs a PreToolUse hook into .codex/hooks.json |
| Hermes | suvra init hermes — merges a pre_tool_call shell hook into ~/.hermes/config.yaml |
| OpenClaw | OpenClawGuard(suvra) wraps the OpenClaw runtime |
| Claude Agent SDK | suvra_hook(suvra) as a PreToolUse HookMatcher |
| Any MCP server | suvra 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)— asyncPreToolUsehook. Rejects tool calls whose decision isn'tallow. On Suvra errors, returns empty unlessfail_closed=True.suvra_agent_options(suvra, *, fail_closed=False)— convenience dict you can merge intoClaudeAgentOptions(...)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.
Related
- Quickstart — the five-minute path
- Human approvals & Slack — what happens when a decision is
needs_approval - Policy Model — constrain exactly what each runtime may do