Matcher Patterns: Precision Targeting
- Write matchers using exact strings, pipe-delimited lists, and regex patterns for different targeting needs
- Apply event-specific matchers for MCP tools, subagent types, session sources, and file paths
- Combine matcher and if fields to achieve precise hook targeting with minimum configuration
The Matcher's Role in Hook Targeting
A hooks configuration with no matchers would fire every handler for every invocation of every event. That is sometimes what you want — a logging hook that should fire for all Bash calls, a Stop hook that should run after every turn regardless of what tools were used. But most hooks need to be more selective.
The matcher in a hook configuration is the primary targeting mechanism. It filters which specific invocations of an event activate the associated handlers. For PreToolUse and PostToolUse, the matcher filters by tool name. For SessionStart, it filters by how the session was started. For FileChanged, it specifies which filenames to watch. Each event has its own matching context, but the pattern syntax is shared across all of them.
Three Matching Modes
The string value of the matcher field determines which matching mode is used. The rules are based on the characters the string contains.
Match all: An empty string "", an asterisk "*", or the absence of a matcher field matches everything. Use this for hooks that should fire for all invocations of the event.
Exact or list: A string containing only alphanumeric characters, underscores, hyphens, spaces, commas, and pipes uses exact matching. A single value like "Bash" matches only the Bash tool. Multiple values separated by pipes like "Edit|Write" match either Edit or Write. Case is significant: "bash" would not match the Bash tool.
Regex: Any string containing characters outside the above set is treated as a regular expression. The pattern is unanchored — it can match anywhere in the target string unless you add ^ and $ anchors. "^Bash$" is equivalent to "Bash" as an exact match. "mcp__.*" matches any MCP tool. "^Notebook" matches notebook tools but not others that contain the word "Notebook" in the middle.
Event-Specific Matchers
Different events match against different fields, and the values that make sense vary by event.
For PreToolUse, PostToolUse, PermissionRequest, and PermissionDenied, the matcher filters by tool name. The built-in tool names are Bash, Edit, Write, Read, Glob, Grep, WebFetch, WebSearch, TodoWrite, and the agent-spawning tools. For MCP-provided tools, the name follows a compound convention covered in the next section.
For SessionStart, the matcher filters by session source. Four values are meaningful: "startup" for new sessions, "resume" for sessions resumed from a previous state, "compact" for sessions that resumed after context compaction, and "clear" for sessions started after a /clear command. Using different matchers for startup and resume lets you run environment setup on fresh sessions but skip it on resumptions.
For SubagentStart and SubagentStop, the matcher filters by agent type name. "general-purpose" matches the default general-purpose agent. "Explore" matches the Explore search agent. Custom named agents match by their declared name.
For Notification, the matcher filters by notification type: "permission_prompt", "auth_success", or other notification type strings.
For FileChanged, the matcher specifies the filenames or patterns to watch. This is not a filter on an event that already fires — it tells Claude Code which files to watch for changes. You also need to register the containing directory with watchPaths in a SessionStart hook.
MCP Tool Matchers
MCP tools have a compound naming convention that enables precise targeting at multiple granularities. The format is mcp__serverName__toolName, where serverName is the configured MCP server name and toolName is the specific tool on that server.
This naming structure makes regex matchers especially useful:
"mcp__memory__create_entities"— exact match for one specific memory tool"mcp__memory__.*"— all tools on the memory server"mcp__.*"— all MCP tools from all servers"mcp__plugin_my-plugin_db__.*"— all tools on a plugin-scoped server named db in the my-plugin plugin
The plugin-scoped naming convention uses underscores as separators throughout, which can make regex patterns need careful crafting. If a server name contains underscores, the compound name contains double underscores at the boundaries and single underscores within the server name.
The if Field as a Second Layer
The matcher in a hook configuration targets the event invocation at the tool level. The if field on a handler targets the invocation at the argument level. Both must pass for the handler to activate.
The if field uses permission-rule syntax: a tool name followed by a pattern in parentheses. Bash(git *) matches Bash tool calls where the command starts with git (after stripping any leading VAR=value assignments and checking subcommands and $() expansions). Edit(*.ts) matches Edit calls where the file path ends in .ts. Bash(rm *) matches Bash calls that start with rm.
This two-layer targeting is more efficient than regex-matching tool names and then re-checking the arguments inside the script. The if filter runs before the handler script starts, so a hook targeting Bash(git push *) never even launches for git status calls.
{
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/block-force-push.sh",
"if": "Bash(git push *--force*)"
},
{
"type": "command",
"command": ".claude/hooks/auto-approve-read.sh",
"if": "Bash(git log|git status|git diff)"
}
]
}
]
}
The if field is best-effort — if the permission pattern fails to parse (due to syntax errors or unsupported operators), the hook runs anyway. Treat it as an optimization and performance filter, and ensure your hook script handles cases where its conditions are not met.
- The matching mode is determined by the string content — alphanumeric plus underscore, hyphen, space, comma, and pipe uses exact or list matching; anything else is treated as an unanchored regex
- MCP tools follow a compound naming convention — mcp__serverName__toolName enables precise targeting with mcp__memory__.*for all memory tools or mcp__.*for all MCP tools
- SubagentStart and SubagentStop matchers accept agent type names — target the general-purpose agent differently from an Explore agent or a custom named agent type
- FileChanged matchers specify the filenames to watch but require watchPaths registration in a SessionStart hook — without watchPaths, the event will not fire for those locations
- The if field adds a second targeting layer after the matcher — Bash(git *) passes only when the bash command starts with git, enabling sub-command precision without a regex on the tool name