AgentGuards

Claude Code — Hooks

Run AgentGuards checks as Claude Code hooks to enforce guardrails at the tool-execution boundary. Hooks fire before Claude executes shell commands and before Claude responds to user prompts.

Hook types

HookWhen it firesEnforcement
PreToolUse (Bash)Before any shell command executesHard block — command never runs
UserPromptSubmitAfter user submits a promptSoft block — Claude Code won't respond

Note: UserPromptSubmit hooks cannot intercept the LLM call to Anthropic — they only prevent Claude from showing a response. For message-level interception, use the proxy integration.

Setup

1. Install the hook script

Easiest: curl -fsSL https://agentguards.co/install.sh | sh sets up these hooks for you (on Windows: irm https://agentguards.co/install.ps1 | iex) — you can stop here. To wire them up by hand on macOS or Linux, download the script:

bash
curl -fsSL -o ~/.claude/agentguards_hook.py https://agentguards.co/api/plugin-hook/claude

On Windows, use the one command above: it sets up the PowerShell version of the hook. This Python script isn't supported on Windows.

2. Configure hooks in ~/.claude/settings.json

~/.claude/settings.json
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "AGENTGUARDS_URL=https://prod.agentguards.co AGENTGUARDS_API_KEY=ag_YOUR_TOKEN python3 ~/.claude/agentguards_hook.py UserPromptSubmit"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash|WebFetch|mcp__.+__.*(fetch|Fetch|browse|scrape|crawl|navigate|page_text|read_page|extract|web_|url|http).*",
        "hooks": [
          {
            "type": "command",
            "command": "AGENTGUARDS_URL=https://prod.agentguards.co AGENTGUARDS_API_KEY=ag_YOUR_TOKEN python3 ~/.claude/agentguards_hook.py PreToolUse"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Bash|WebFetch|WebSearch|Write|Edit|MultiEdit|mcp__.+__.*(fetch|Fetch|browse|scrape|crawl|navigate|page_text|read_page|extract|web_|url|http).*",
        "hooks": [
          {
            "type": "command",
            "command": "AGENTGUARDS_URL=https://prod.agentguards.co AGENTGUARDS_API_KEY=ag_YOUR_TOKEN python3 ~/.claude/agentguards_hook.py PostToolUse"
          }
        ]
      }
    ]
  }
}

Recommended: set the env vars in your shell profile so the API key is not visible in process listings.

~/.zshrc
# ~/.zshrc or ~/.bashrc
export AGENTGUARDS_URL=https://prod.agentguards.co
export AGENTGUARDS_API_KEY=ag_YOUR_TOKEN

Then the settings become:

~/.claude/settings.json
{
  "hooks": {
    "UserPromptSubmit": [
      { "hooks": [{ "type": "command", "command": "python3 ~/.claude/agentguards_hook.py UserPromptSubmit" }] }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash|WebFetch|mcp__.+__.*(fetch|Fetch|browse|scrape|crawl|navigate|page_text|read_page|extract|web_|url|http).*",
        "hooks": [{ "type": "command", "command": "python3 ~/.claude/agentguards_hook.py PreToolUse" }]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Bash|WebFetch|WebSearch|Write|Edit|MultiEdit|mcp__.+__.*(fetch|Fetch|browse|scrape|crawl|navigate|page_text|read_page|extract|web_|url|http).*",
        "hooks": [{ "type": "command", "command": "python3 ~/.claude/agentguards_hook.py PostToolUse" }]
      }
    ]
  }
}

3. Verify

Run a Claude Code session and try a blocked command. The hook calls POST /v1/actions/authorize and blocks the command before it executes.

test
User: Run rm -rf /

Understanding hook behaviour

Want a plain-language explanation of what each hook does, which commands are approved or blocked, and what the decisions mean?

How hooks work →

Combining with other integrations

Proxy + Hooks together cover both conversation-level and action-level threats — the recommended setup for API key users. For Claude Pro / Max subscribers (no proxy available), hooks are the primary enforcement mechanism for tool execution.

Fail-open behaviour

The hook script is designed to fail open: if the AgentGuards service is unreachable (network issue, timeout), the hook exits 0 and allows the action. This prevents the guardrail service from becoming a hard dependency that blocks your work.

To change this to fail-closed, edit the _allow() calls in the except blocks of agentguards_hook.py.