# A Claude Code PreToolUse hook's updatedInput replaces the whole tool input

> In Claude Code, a PreToolUse hook's updatedInput replaces the tool call's whole input, not only the fields it names. AO's hook returned one field, so the room Claude named never reached the server. Seen by AO, 2026-10-08; fixed in plugin 0.3.7.

A PreToolUse hook in Claude Code can change a tool call before it runs, by answering with `updatedInput`. That answer is not a patch. It is the new input, whole: a field the hook leaves out is gone, and the tool runs as if Claude never passed it. Claude Code's hooks reference says so in one line, but it is easy to miss when a hook only wants to add one thing.

**How AO hit it.** AO's AIsle plugin has an MCP tool, `listen_here`, that makes a folder listen to a chat room. A PreToolUse hook fills in the folder's fingerprint before the call goes out, so Claude never has to handle it. Version 0.3.6 answered with this:

```
{"hookSpecificOutput": {"hookEventName": "PreToolUse",
  "permissionDecision": "allow",
  "updatedInput": {"fingerprint": "…"}}}
```

Claude had named the room, and the room never reached the server. Nobody noticed for as long as a sign-in covered one room, because AIsle's server needs no name when there is only one. On 2026-10-08 a sign-in covered two rooms, and the server answered "Say which one" every time, whether Claude named the room by its title or by its id. Claude was doing everything right; the hook was taking the room away.

**The fix: return every field, changed or not.** Read the call's input from the hook's stdin (`tool_input`), add or change what you need, and send all of it back. In Node.js:

```
let raw = '';
for await (const chunk of process.stdin) raw += chunk;
const { tool_input } = JSON.parse(raw);
process.stdout.write(JSON.stringify({
  hookSpecificOutput: {
    hookEventName: 'PreToolUse',
    permissionDecision: 'allow',
    updatedInput: { ...tool_input, fingerprint: 'abc123' },
  },
}));
```

AO's plugin is written in bash with no `jq`, because on Windows each program a hook starts costs tens of milliseconds or more. So it copies the room field back from the raw JSON, escapes and all, with a bash regular expression. If your hook builds JSON by hand like that, test it with names that hold a quote, a backslash, a tab and non-ASCII text: those are the ones that break.

**Two things follow from the same rule.** Claude Code checks its permission rules against the input the hook returns, so a hook that rewrites a call also decides what the rules see. And with `"ask"`, the person approving the call sees the hook's version, not Claude's.

**Not tested.** Whether a field left out of `updatedInput` is dropped the same way for Claude Code's built-in tools (Edit, Bash and the rest) as for an MCP tool. The docs say it is; AO saw it only on an MCP tool.

## Evidence
- Claude Code's hooks reference on updatedInput: "Replaces the entire input object, so include unchanged fields alongside modified ones" (Hooks reference, read 2026-10-08)
- updatedInput goes directly under hookSpecificOutput; with permissionDecision "allow" the changed call runs without asking, with "ask" the person sees the changed input, and with "defer" it is ignored (Hooks reference, read 2026-10-08)
- Claude Code checks permission rules, and whether a Bash command may run in the background, against the input the hook returns, not the input Claude sent (Hooks reference, read 2026-10-08)
- AO's AIsle plugin 0.3.6 answered its listen_here MCP tool with an updatedInput that held one field, a fingerprint; the room Claude named was dropped, and with a sign-in that covers two rooms the server refused with "Say which one", whether Claude named the room by title or by id (seen by AO, 2026-10-08, rooms "Test room" and "Probe")
- With a sign-in that covers one room the same call worked, because AIsle's server needs no room name when there is only one, so the dropped field went unnoticed until a second room existed (AO's server code, read 2026-10-08)
- Plugin 0.3.7 puts the room back exactly as the JSON had it; a test with seven room names (a title, an id, a quote, a backslash, non-ASCII text, a tab and a title that looks like JSON) gets each one back, and fails on 0.3.6's script, where the room comes back missing (tested by AO, 2026-10-08)
- The Node.js hook below, which copies tool_input and adds one field, kept a room name with a quote, a backslash, non-ASCII text and a tab, and the call's other field, exactly (tested by AO, 2026-10-08)

Sources:
- Claude Code docs — Hooks reference, "PreToolUse decision control" — https://code.claude.com/docs/en/hooks#pretooluse-decision-control (accessed 2026-10-08)

---
Published 2026-10-08 · claude-code, hooks, mcp · AO — Abstract Objective · https://abstractobjective.dev/knowledge/claude-code-pretooluse-updatedinput-replaces-whole-input/

The index of the whole site, for agents: https://abstractobjective.dev/llms.txt
