For the complete documentation index, see llms.txt. This page is also available as Markdown.

Claude CLI Hooks

Akto Guardrails for Claude CLI provides security validation for AI interactions. It intercepts prompts before sending to Claude, MCP tool calls before they execute, and responses after generation — validating each against security policies, blocking risky behavior, and reporting events to your Akto dashboard.

Key Features

  • Zero Installation - No standalone apps to install

  • Transparent Integration - Uses Claude CLI's native hook mechanism

  • Real-time Protection - Validates every prompt, MCP tool call and response

  • MCP Coverage - Tool calls are reported as JSON-RPC tools/call, so MCP servers and tools show up in your inventory

  • Centralized Monitoring - All events reported to Akto dashboard

  • Flexible Deployment - Supports Argus and Atlas modes

  • Configurable Behavior - Blocking or observation modes

How It Works

Claude CLI's hook system executes custom scripts at four critical points — two around the conversation, and two around every tool call the agent makes:

4 Hook Points:

  1. UserPromptSubmit — Validates prompts before sending to Claude API

  2. PreToolUse — Validates MCP tool input before execution. The only hook that can block a tool call: it returns permissionDecision: "deny" with a reason

  3. PostToolUse — Captures MCP tool results for observability and response guardrails

  4. Stop — Validates responses when Claude finishes generating

How MCP tool calls are recognised. Claude Code names MCP tools mcp__<server>__<tool>. The PreToolUse hook parses that shape, and for a match reports the call to Akto as a JSON-RPC tools/call on the /mcp path — so it lands in your MCP inventory with the server and tool broken out. Tool calls that are not MCP (Bash, Read, Edit, …) still pass through the same hook; they are mirrored to /tool/<tool-name> instead. Ingestion of non-MCP tool calls is off by default — set AKTO_INGEST_NON_MCP_TOOLS=true to enable it.

File Structure

Key Files:

  • Wrapper scripts (.sh): Set environment variables, invoke Python scripts

    • ⚠️ Contains AKTO_DATA_INGESTION_URL placeholder - Must be replaced with your Akto instance URL

  • Python scripts (.py): Core validation logic and Akto API communication

  • akto-validate-mcp-request.py: Validates every tool call before it runs. Reports MCP calls (mcp__<server>__<tool>) as JSON-RPC tools/call on /mcp; can deny the call

  • akto-validate-mcp-response.py: Captures the tool result as a JSON-RPC result. Observational — it cannot block

  • akto_ingestion_utility.py, akto_machine_id.py, akto_heartbeat.py: shared modules imported by the hook scripts. All three live in the shared/ GitHub directory, not claude-cli-hooks/, so they are fetched from SHARED_BASE — miss any one and the hooks fail at import with ModuleNotFoundError

  • akto_heartbeat.py: Registers this device with Akto every 30s. Required — without a heartbeat record, mini-runtime cannot resolve the device to a user and drops every hook event before indexing, so the endpoint never appears under LLM observability / traces even though ingestion succeeds

  • akto_machine_id.py: Generates unique device identifiers for Atlas mode

  • settings.json: Links hooks to wrapper scripts

Setup Guide

Prerequisites

  • Claude CLI installed (Installation Guide)

  • Akto instance URL

  • Python 3.7+ (standard library only — the hooks need no third-party packages)

  • macOS, Linux, or Windows with bash/zsh

Installation Steps

1

Create Directories

2

Download Hook Scripts

akto_ingestion_utility.py must land in the same directory as the hook scripts — they import it as a plain top-level module, resolved from the script's own directory. Skipping this download makes every hook fail with ModuleNotFoundError: No module named 'akto_ingestion_utility'.

3

Configure Akto Ingestion URL, API Token and Device ID ⚠️ CRITICAL STEP

Automated replacement:

Manual replacement (alternative):

Edit each wrapper script and replace:

With:

Files to update:

  • akto-validate-prompt-wrapper.sh

  • akto-validate-response-wrapper.sh

  • akto-validate-mcp-request-wrapper.sh

  • akto-validate-mcp-response-wrapper.sh

4

Configure Hooks

Create Claude CLI settings configuration:

5

Configure Hook Behavior (Optional)

Edit wrapper scripts to customize:

Mode Options:

  • Argus: Standard validation and reporting

  • Atlas: Includes device-specific metadata

Sync Mode:

  • true: Blocks threats

  • false: Reports but allows execution

6

Verify Installation

Check logs to confirm hooks are working:

Test by running a Claude command:

You should see log entries indicating validation occurred.

Configuration Reference

Wrapper Script Variables

MCP Tool Hook Variables

Read by akto-validate-mcp-request.py / akto-validate-mcp-response.py. All optional — the defaults match what the enterprise installer configures.

How DEVICE_ID is reported: the hooks send <DEVICE_ID>.ai-agent.claudecli as the request host, and the dashboard uses the first label of that host as the device name. If DEVICE_ID is empty the hooks fall back to the lowercased computer name (or, where that cannot be resolved, the raw machine UUID — which is why a device sometimes shows up as a bare hex string).

Environment Variables (Optional)

Override defaults via environment variables or config file:

Option 1: Environment variables

Option 2: Config file

Troubleshooting

ModuleNotFoundError: No module named 'akto_ingestion_utility'

The shared ingestion utility was not downloaded, or landed outside ~/.claude/hooks/. It lives in the shared/ directory on GitHub, not under HOOKS_BASE, so it needs its own curl.

If the file is present and the import still fails, check that PYTHONSAFEPATH is unset — it suppresses the script-directory entry on sys.path that this import relies on.

MCP Tool Calls Not Appearing

PreToolUse fires for every tool call, but only names matching mcp__<server>__<tool> are reported as MCP tools/call on /mcp. Built-in tools (Bash, Read, Edit, …) take the non-MCP path and are not ingested unless you opt in.

If the log shows Validating built-in / non-MCP tool request, the call was not an MCP tool — that is expected for Claude's own tools.

Endpoint Appears in Inventory but Traces / LLM Observability Are Empty

Ingestion and trace indexing are separate paths. mini-runtime resolves each hook event's device to a user via the heartbeat record (moduleInfo.nameadditionalData.username); an event whose device has no heartbeat, and which carries no user_email header, is discarded before indexing. Claude Code's hook payloads never carry an email, so the heartbeat is the only thing that can satisfy this — the collection still shows up in inventory, which is why the endpoint looks connected.

If the file is missing, re-run the download step. If it is present but never sends, confirm DATABASE_ABSTRACTOR_SERVICE_URL points at a reachable abstractor (see the hint above) and that AKTO_AGENT_HEARTBEAT is not set — that flag is for machines where the Akto agent already publishes the heartbeat, and it disables publishing from the hook.

Hooks Not Executing

Ingestion URL Not Configured

Check Logs for Errors

Events Not in Dashboard

Uninstallation

To completely remove Akto hooks from Claude CLI:

Complete Removal

Selective Removal (Keep Logs)

If you want to preserve logs for audit purposes:

Backup Before Removal

Verify Removal

Restore Claude CLI to Default

After uninstallation, Claude CLI will operate without Akto security monitoring. No additional configuration is needed beyond removing the files. Test with:

Enterprise Deployment

Automated Deployment Script

Deploy to developers:

Quick Setup Summary

Resources

Last updated