Surface MCP tool annotations (readOnlyHint/destructiveHint) in PreToolUse hook input
Feature request
Surface MCP tool annotations (readOnlyHint, destructiveHint, openWorldHint) in PreToolUse hook input.
Problem
PreToolUse hooks receive tool_name and tool_input for MCP tools, but not the tool's declared MCP annotations. A hook that wants to gate writes cannot tell a read-only MCP tool from a destructive one without maintaining a name-based deny list, which breaks as servers are added or renamed.
The MCP spec already defines these hints on the tool object (annotations.readOnlyHint, annotations.destructiveHint), and Claude Code has them at dispatch time — they just aren't forwarded to the hook.
Concrete use case
We maintain a workflow-enforcement plugin whose PreToolUse hooks block file writes during planning/review phases. The hooks match built-in tool names (Write|Edit|MultiEdit|NotebookEdit, Bash). Any write-capable MCP tool bypasses the gates entirely, because:
- a blanket
mcp__.*deny would break read-only research tools the planning phases depend on; - guessing write-ness from tool names is unreliable;
- a hand-maintained deny list rots as MCP servers change.
With annotations in the hook input, the hook could allow readOnlyHint: true tools everywhere and gate the rest — capability-based instead of name-based.
Proposed shape
Add an optional field to PreToolUse (and PostToolUse) hook input for MCP tools, e.g.:
{
"tool_name": "mcp__server__tool",
"tool_input": { ... },
"tool_annotations": {
"readOnlyHint": true,
"destructiveHint": false
}
}
Annotations are optional in the MCP spec, so the field would be absent or partial when a server doesn't declare them — hooks can then fall back to their own policy for unannotated tools.
Environment
- Claude Code CLI on macOS (darwin)
- Verified against the hooks documentation, 2026-08-04: hook input for tool events carries
tool_name/tool_inputonly, no annotation metadata.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗