Claude Code v2.1.118 (released April 23, 2026) added type: "mcp_tool" as a new hook handler type. You can now wire any lifecycle event -- PreToolUse, PostToolUse, Stop, SessionStart -- directly to a tool on any connected MCP server. No shell script, no subprocess. The tool output feeds back into the hook pipeline exactly like command-hook stdout.
I have been waiting for this. The hooks system has been one of the most underused parts of Claude Code because writing a shell script for every automation chain is friction. With the mcp_tool handler, if you already have an MCP server configured, that friction is gone.
What exactly does the mcp_tool hook handler do?
The mcp_tool hook handler is one of five handler types in Claude Code -- alongside command, http, prompt, and agent. When a lifecycle event fires, it calls a named tool on an already-connected MCP server, passes an optional input object with template variables, and surfaces the tool text output back into the hook pipeline as if it were stdout from a shell command.
The key phrase is "already-connected." The MCP server must be configured in your settings.json before the hook fires. This routes through the same persistent protocol channel Claude Code uses for all tool calls during a session -- it is not a fresh connection per hook invocation. There is no subprocess spawn, no new authentication handshake.
From the official GitHub release notes for v2.1.118, released April 23, 2026, by @ashwin-ant at Anthropic: "Hooks can now invoke MCP tools directly via type: mcp_tool." That was one of 34 CLI changes shipped in that single release. The same release added vim visual modes, custom themes, a unified /usage command, the DISABLE_UPDATES env var, and WSL settings inheritance -- but the MCP hooks integration is the one with the highest automation leverage for anyone running MCP servers in their stack.
The exit code behavior is identical to the command handler. From the official hooks docs at code.claude.com/docs/en/hooks: "A PreToolUse hook that exits 2 stops the tool. A Stop hook that exits 2 forces Claude to keep working." The mcp_tool handler participates in both of those control mechanisms fully.
How do you configure a mcp_tool hook in settings.json?
A mcp_tool hook block requires two fields -- server (the name of your configured MCP server) and tool (the tool name on that server). The optional input object supports ${variable} template substitution from the triggering event payload, letting you forward file paths, tool arguments, or other context directly into the MCP tool call without intermediate scripting.
Here is the minimal config from the official hooks reference, showing a PreToolUse hook that calls a security scanning tool before any file Write:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "mcp_tool",
"server": "my_server",
"tool": "security_scan",
"input": { "file_path": "${tool_input.file_path}" }
}
]
}
]
}
}
The matcher field targets which tool triggers the hook. For hooks on MCP tool calls themselves, the naming convention is mcp__<server>__<tool> with double underscores. So if you want a hook to fire when Claude calls create_entities on a memory server, the matcher is mcp__memory__create_entities. Regex patterns work here: mcp__memory__.* matches all tools on the memory server, and mcp__.*__write.* matches write-family tools across all servers.
Where you place the config determines scope. User-level hooks in ~/.claude/settings.json apply to every project on your machine. Project-level hooks in .claude/settings.json apply only to that project and can be committed to version control for team sharing. Plugin manifests can ship hooks in a hooks/hooks.json file, and skill frontmatter can define hooks that are active only while that skill is running. Five configuration scopes total: user, project, project-local, managed policy, and plugin.
Want the templates from this tutorial?
I share every workflow, prompt, and template inside the free AI Creator Hub on Skool. 500+ builders sharing what actually works.
Join Free on Skool
What are the 3 most useful automation chains this unlocks?
The three highest-leverage automation chains are: (1) blocking file writes that fail a security scan, (2) posting a notification when Claude finishes a session, and (3) logging every tool call result to a memory MCP server for persistent audit trails. All three were possible before with shell scripts that called MCP servers -- the mcp_tool handler removes the subprocess layer and the authentication plumbing entirely.
Chain 1: Auto-security scan on every Write or Edit
Wire a PreToolUse hook on the Write matcher to call a security scanning tool on your MCP server. The ${tool_input.file_path} template variable forwards the target file path to the scanner automatically. If the scanner triggers exit code 2, the Write is blocked before it touches disk. This is the equivalent of a pre-commit hook, but it runs inline during the Claude session -- you catch the issue before the file is written, not after it is staged.
"PreToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "mcp_tool",
"server": "security_tools",
"tool": "scan_file",
"input": { "path": "${tool_input.file_path}" }
}
]
}
]
You can extend this to both Write and Edit matchers using a regex pattern. The hook runs synchronously before the file operation. If the MCP tool returns a warning, Claude sees it as hook stdout and can adjust before retrying.
Chain 2: Notification ping when Claude stops
Wire a Stop lifecycle event to a Slack, Telegram, or Discord MCP server. Every time Claude finishes a response turn, a message fires to your chosen channel. This is the most practical setup for long-running background sessions where you want to know when Claude finishes a task without watching the terminal. Because Stop hooks can exit with code 2 to force Claude to keep working, you can also build conditional stop logic -- notify only when a specific output condition is met, otherwise keep the session running.
"Stop": [
{
"hooks": [
{
"type": "mcp_tool",
"server": "slack",
"tool": "post_message",
"input": {
"channel": "dev-alerts",
"text": "Claude Code session finished"
}
}
]
}
]
The same pattern works for any notification MCP server. You pick the server and tool name; the hook wiring is identical regardless of which notification channel you use.
Chain 3: Cross-session logging via PostToolUse to a memory server
Wire a PostToolUse hook with a broad matcher or no matcher (catches all tool calls) to your memory MCP server. After every tool call -- file reads, edits, bash commands, MCP tool invocations -- the result gets written to the memory server. This gives you a persistent audit log across sessions that you can query to reconstruct what happened. The memory server text response feeds back into Claude context exactly like any hook stdout, so Claude can also receive and act on the log confirmation in the same turn.
This chain has the longest tail of value. Session-level context evaporates when a Claude Code session ends. Routing PostToolUse through a memory MCP server means every action from every session builds into a queryable history. Combined with a PreToolUse hook on the same memory server, you can surface relevant past actions at the start of each new session automatically.
How does the mcp_tool handler fit into the full hooks lifecycle?
Claude Code has 25+ named lifecycle events across session, turn, and tool-call cadences. The mcp_tool handler can attach to any of them -- there is no event restriction specific to this handler type. This is worth noting because the agent-type hooks had a bug where they failed with "Messages are required for agent hooks" when configured for events other than Stop or SubagentStop. That bug was fixed in v2.1.118 alongside the mcp_tool addition.
The three lifecycle cadences relevant to most automation chains:
- Session cadence:
SessionStart fires on startup, resume, clear, and compact. SessionEnd fires on clear, resume, logout, and other exit paths. Good for initialization and teardown logic.
- Turn cadence:
UserPromptSubmit fires before Claude processes input and can block it. Stop fires when Claude finishes a turn and can force continuation via exit code 2. PostToolBatch fires when all parallel tool calls resolve. Good for completion notifications and conditional continuation.
- Tool-call cadence:
PreToolUse fires before execution and exit code 2 blocks the tool. PostToolUse fires after completion and cannot block. PostToolUseFailure fires after failures. PermissionRequest fires before a permission dialog appears. Good for security gates, logging, and follow-on actions.
The full event list also includes SubagentStart, SubagentStop, PreCompact, PostCompact, Notification, InstructionsLoaded, ConfigChange, CwdChanged, FileChanged, WorktreeCreate, WorktreeRemove, Elicitation, ElicitationResult, and more. The mcp_tool handler gives you native MCP protocol access at any point in that lifecycle.
What else shipped in v2.1.118?
The v2.1.118 release contained 34 CLI changes in total (per @ClaudeCodeLog on X). Beyond the MCP hooks feature, the most practically useful additions for daily use are vim visual modes, the unified /usage command, custom themes, and the DISABLE_UPDATES env var.
Vim visual mode is now properly implemented: v starts character-wise visual selection, V starts line-wise, and both work with operator combinations and visual feedback. If you were using Claude Code in vim mode and running into selection behavior issues, this release resolves them.
The /cost and /stats commands are merged into /usage, which opens with tabs for both views. Both old commands still work as shortcuts that open the relevant tab, so nothing breaks in existing workflows. Custom themes are now creatable and switchable from /theme, or you can hand-edit JSON files in ~/.claude/themes/. Plugins can also ship themes via a themes/ directory in the plugin manifest.
The DISABLE_UPDATES env var is stricter than the old DISABLE_AUTOUPDATER flag. Setting DISABLE_UPDATES=1 blocks all update paths including manual claude update from the terminal. The old flag only blocked the automatic background updater -- manual updates still went through. This is useful in CI environments or managed deployments where you pin a specific Claude Code version.
WSL on Windows can now inherit Windows-side managed settings via the wslInheritsWindowsSettings policy key. The bug fixes in v2.1.118 also addressed two MCP OAuth issues that forced re-authentication every hour on servers with non-standard token responses, and a credential save crash on Linux and Windows that could corrupt ~/.claude/.credentials.json.
FAQ
Does mcp_tool work with any MCP server, or only specific ones?
The mcp_tool hook handler works with any MCP server already configured in your Claude Code settings. There is no restriction on server type, protocol version, or transport layer. The server must be active and connected before the hook fires. Claude Code uses the same persistent protocol channel it uses for all tool calls during a session, so no additional authentication is needed beyond the initial server configuration.
What is the difference between mcp_tool and command hooks?
A command hook spawns a shell subprocess and receives event JSON on stdin. A mcp_tool hook routes through the MCP protocol directly, using the server connection already established during the session. No subprocess spawn, no shell overhead. The mcp_tool handler also accepts structured input objects with template variable substitution. Use command when you need shell access. Use mcp_tool when you are wiring to a tool already configured as an MCP server.
What lifecycle events can trigger a mcp_tool hook?
Any of the 25+ named lifecycle events in Claude Code can trigger a mcp_tool hook -- PreToolUse, PostToolUse, Stop, SessionStart, SessionEnd, UserPromptSubmit, PostToolBatch, SubagentStart, SubagentStop, PreCompact, PostCompact, and more. There is no event restriction specific to the mcp_tool handler type. PreToolUse with exit code 2 blocks the tool. Stop with exit code 2 forces Claude to continue. PostToolUse cannot block since the tool has already run.
Can I use template variables in the mcp_tool input field?
Yes. The input field in a mcp_tool hook supports ${variable} template substitution from the triggering event payload. The official example from the hooks reference at code.claude.com/docs/en/hooks forwards a file path: "input": { "file_path": "${tool_input.file_path}" }. This lets a PreToolUse hook on Write or Edit pass the target file path directly to a scanning tool on your MCP server without any shell intermediary.
Want the templates from this tutorial?
I share every workflow, prompt, and template inside the free AI Creator Hub on Skool. 500+ builders sharing what actually works.
Join Free on Skool