PreToolUse hooks: add 'intercept' decision to deny execution but provide replacement result

Resolved 💬 2 comments Opened Apr 15, 2026 by fwehrling Closed May 24, 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

The Gap Between "deny" and "allow"

The current PreToolUse hook system offers two main decisions:

  • allow (+optional updatedInput): the tool executes (with possibly modified inputs)
  • deny: the tool is blocked, and permissionDecisionReason is shown to Claude

There is no middle ground: block the tool from executing but provide replacement content that Claude treats as a successful tool result.

Real-World Impact: Token Compression Hooks

This gap critically affects token-optimization tools like RTK (Rust Token Killer), which intercepts large Read/Grep/Glob results to compress them (~60-90% token savings).

Current workaround: RTK uses deny with the compressed content embedded in permissionDecisionReason:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "[RTK:Read] src/app.ts -- 137L -> 19L (86% saved)\n\n<compressed content>"
  }
}

The problem: Claude interprets deny as a tool failure and actively works around it:

  • Re-reads the file with offset/limit parameters to bypass the hook
  • Falls back to Bash with cat/head commands
  • Tries alternative approaches to get the "real" content

Even with explicit CLAUDE.md instructions telling Claude to treat RTK denials as successful results, this behavior persists because Claude's core behavior treats deny = failure = retry with different approach.

Why Existing Features Don't Solve This

| Approach | Why It Fails |
|----------|-------------|
| deny + reason | Claude treats it as failure, retries/works around |
| allow + updatedInput | Tool still executes -- no token savings |
| PostToolUse + context | Tool already executed -- tokens already consumed |
| CLAUDE.md instructions | Unreliable -- Claude's deny-avoidance instinct overrides instructions |

Proposed Solution

Add a new permission decision: intercept (or replace/substitute).

When a PreToolUse hook returns intercept, the tool does NOT execute, but the provided content is presented to Claude as if the tool had returned it successfully.

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "intercept",
    "toolResult": "[RTK:Read] src/app.ts -- 137L -> 19L (86% saved)\n\nimport express from 'express';\n// ... compressed content ..."
  }
}

Claude would see this as a normal, successful tool call -- no retry behavior, no workarounds.

Use Cases

  1. Token compression (RTK, similar tools): Compress large file reads/search results before they enter the context window
  2. Caching: Return cached results for repeated tool calls without re-execution
  3. Content transformation: Redact secrets, filter noise, summarize large outputs -- all before execution
  4. Cost control: Enforce budget limits by returning summaries instead of full content
  5. Testing/mocking: Provide mock tool results during hook development

Alternatives Considered

  • Stronger CLAUDE.md instructions: Partially effective but unreliable. Claude's training to work around denied tools overrides prompt instructions, especially under cognitive load.
  • PostToolUse transformation (related: #18653): Doesn't prevent execution, so tokens are already consumed. Different use case (security sanitization vs. prevention).
  • allow + updatedInput with reduced parameters: Loses information (truncation vs. intelligent compression). Inferior to compression.

Related Issues

  • #18653 -- Tool result transform hook (PostToolUse focus, security-oriented)
  • #4368 -- Enhance PreToolUse hooks to modify tool inputs (closed, updatedInput implemented)
  • #31592 -- Distinguish hook denial from hook error
  • #24327 -- Exit code 2 causes Claude to stop instead of acting on feedback

This feature would transform PreToolUse hooks from a binary allow/deny gate into a complete middleware layer, enabling an ecosystem of token-optimization and content-transformation tools.

View original on GitHub ↗

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