[FEATURE] Mods API: an observe-only event for every final tool denial, with its source and protected paths

Status Open
Reported on v2.1.296
Maintainer reply None cached
Activity 0 comments · opened Oct 11, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

Claude Code 2.1.296, macOS. The Arrester mod opens a "block" when a hook, rule or the auto-mode classifier refuses a tool call, then stops detours to the same files. Two denials escape it:

  1. Managed-settings hooks. The types say: "The chain is [managed settings hooks, ...hooks modules, the other settings hooks as core], so a managed block ends it above every module." A managed JSON deny reaches no mod.
  2. Recursive searches. A denied grep -r, Grep or Glob names no file. PermissionDeniedHookInput carries only tool_name, tool_input, tool_use_id, reason and mcp_server?. ToolCheckResult.hook is "Absent on a $.tool.check query, which runs no classic hook", so the mod cannot ask either.

Proposed Solution

  1. An observe-only event, raised after the final decision refuses a tool call from any source, managed hooks included. Its return value is ignored, so managed policy still wins.
'tool.denied': {
  tool: string; tool_use_id: string; agentId?: string; input: unknown;
  source: 'rule' | 'mode' | 'classifier' | 'hook' | 'plugin' | 'user';
  scope?: SettingsSource | 'plugin'; hook?: string; rule?: string;
  reason: string; paths?: string[];
}
  1. An optional paths: string[] in a PreToolUse deny's hookSpecificOutput and in the classifier verdict, passed on tool.denied and classic.PermissionDenied.

Alternative Solutions

Arrester's documented workaround is an exit-code-2 block instead of a JSON deny. A user cannot change a managed hook.

Priority

Medium - Would be very helpful

Feature Category

Developer tools/SDK

Use Case Example

  1. A managed hook denies cat .env with JSON.
  2. Claude tries cp .env /tmp/x && cat /tmp/x.
  3. With tool.denied, Arrester blocks .env and stops the copy. Today it sees nothing.
  4. A hook denies grep -r TOKEN . with paths: [".env"]. Arrester then stops head .env.

Additional Context

Closes two Arrester "Known limits". Related: #72589 (denial reason and type on PermissionDenied, classifier only, no paths) and #96026 (deciding rule, SDK side).

View original on GitHub ↗