← All docs

Generic pre-tool-use hook

How any coding agent with a pre-tool-use hook can call agentjail-hook to enforce policy before a tool call runs.

agentjail is agent-agnostic at the protocol level. agentjail-hook reads a Claude Code-style PreToolUse JSON event from stdin and writes a decision to stdout. Any agent that can run a pre-tool-use command and feed that JSON shape can in principle use it — but the only turnkey installer today is Claude Code (macOS only). For other agents, this page describes the hook protocol so you can wire it manually once your agent supports it.

The protocol

agentjail-hook is a stdin/stdout process:

  • Input (stdin): a JSON object with at minimum:

    {
      "hook_event_name": "PreToolUse",
      "tool_name": "Bash",
      "tool_input": { "command": "rm -rf /" }
    }

    tool_name is the tool schema name as your agent knows it (e.g. Bash, Write, Read). tool_input is the tool’s input as a JSON object; for shell tools it is typically {"command": "..."}.

  • Output (stdout): a JSON object:

    {
      "hookSpecificOutput": {
        "hookEventName": "PreToolUse",
        "permissionDecision": "allow",
        "permissionDecisionReason": "..."
      }
    }

    permissionDecision is "allow", "ask", or "deny".

  • Exit codes:

    • Exit 0 — allow or ask. The agent proceeds normally.
    • Exit 2 — deny. The agent should stop the call and surface the reason.

The evaluation is local and offline: no network round-trip, no model in the loop. The same input always produces the same verdict in milliseconds.

Hook lifecycle

The sequence at every tool call:

agent decides to call a tool
        |
        v
pre-tool-use hook fires
        |
        v
agentjail-hook reads JSON from stdin
        |
   exit 0?
   /      \
 yes       no (exit 2)
  |         |
  v         v
tool      blocked:
runs      agent receives
          structured reason,
          explains itself

The agent never reaches the tool on a denial. The block happens before any command is handed to the shell, filesystem, or network. See How it works for the full lifecycle.

Testing a rule before you wire it up

You can test agentjail-hook directly from your terminal without a running agent. The daemon must be running (agentjail status). Pipe a synthetic event and inspect the output:

# should deny (targets sensitive path)
echo '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"cat ~/.ssh/id_rsa"}}' \
  | agentjail-hook

# should allow (safe read)
echo '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"ls ./src"}}' \
  | agentjail-hook

Check the exit code ($?) and the printed JSON to confirm your rules behave as expected before wiring them in.

Next steps