Include hook block reason and hook type in tool_decision OTEL event

Resolved 💬 3 comments Opened Mar 24, 2026 by zach-weisner Closed Apr 22, 2026

Problem

When a PreToolUse hook blocks a tool call, the claude_code.tool_decision OTEL event only includes:

  • tool_name
  • decision: "reject"
  • source: "hook"

The permissionDecisionReason from the hook's response is not included in the telemetry event, even though Claude Code has it (it's displayed in the terminal). There's also no way to distinguish which hook caused the block when multiple hooks are configured (e.g., a command hook and a prompt hook on the same event).

Use Case

We're building a SOC platform that monitors Claude Code agent deployments via OTEL telemetry. We have two types of PreToolUse hooks:

  1. Command hook — regex blocklist (blocks rm -rf /, cat .env, etc.)
  2. Prompt hook — LLM-based intent evaluation (blocks obfuscated commands)

When a tool call is blocked, we need to surface the block in our security dashboard with:

  • Which hook blocked it (regex vs LLM guard)
  • Why it was blocked (the permissionDecisionReason)

Currently all we get is source: "hook" with no reason and no hook identifier, so we can't distinguish between the two or show the analyst why it was blocked.

Proposed Solution

Add to the claude_code.tool_decision event attributes:

| Attribute | Description | Example |
|-----------|-------------|---------|
| reason | The permissionDecisionReason from the hook response | "Command uses variable concatenation to hide cat /etc/shadow" |
| hook_type | The hook type that made the decision | "command", "prompt", "http" |
| hook_index | Which hook in the array (optional) | 0, 1 |

This would make the telemetry actionable for security monitoring without requiring workarounds like timestamp-based anti-joins between OTEL events and custom telemetry.

Current Workaround

We emit separate policy_violation events from our command hook and use a timestamp-based anti-join to infer which blocks came from the prompt hook (no matching policy_violation within 2 seconds). This is fragile and breaks when timing is off or agents retry.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗