01
What a hook matcher is (and why this broke quietly)
A hook is a command your tool runs automatically at a defined moment — a linter, a reviewer, a notifier. A matcher is the name pattern that decides when a hook fires. Until recently, Claude Code matched on a substring: if your pattern appeared anywhere in the tool name, the hook ran. Claude Code v2.1.195 (2026-06-26) changed that to exact-match. That's a correctness and security improvement — no more accidental firing on a partial name. But it has a side-effect: any matcher whose identifier is hyphenated (e.g. code-reviewer, mcp__brave-search) that previously relied on substring matching no longer fires. No error, no migration warning — it just silently stops. Plain alphanumeric matchers (like Bash or Edit) are unaffected.
02
The 30-second audit (do this once)
Walk these three steps in order. The whole point is to surface hooks that went dead without telling you:
| Step | What to do | Why it matters |
|---|
| 1. LIST | Open your settings (.claude/settings.json — project and user) and list every hook matcher | You can't fix what you can't see; this is the only step that takes real attention |
| 2. FLAG | Mark any matcher that contains a hyphen (code-reviewer, mcp__brave-search, etc.) | Hyphenated identifiers are exactly the ones that silently stopped matching |
| 3. REWRITE + TEST | Rewrite each flagged matcher as a wildcard, then trigger the hook once and confirm it fired | Exact-match means you must opt into the wildcard explicitly — and verify, don't assume |
List, flag, rewrite-and-test. If a hook still doesn't fire after the rewrite, its matcher name itself is wrong — not just the matching mode.
03
The wildcard migration pattern
This is the release's own recommended fix. To make a hyphenated matcher fire again, append the wildcard suffix so it matches the family of tools instead of relying on a substring:
- Before (silently broken):
mcp__brave-search — used to substring-match every brave-search tool; now matches nothing because no tool is named exactly that. - After (fixed):
mcp__brave-search__. — the pattern is matcher + two underscores + . (dot-star, a regex wildcard). It matches all tools from that hyphenated MCP server. - Same shape for a plain hook name: a matcher like
code-reviewer that was relied on as a prefix should be rewritten to the explicit name(s) it needs to match, or to a wildcard family if your tool supports it. - Rule of thumb: exact-match means you state the family explicitly. The wildcard
__.* is how you say "all tools under this server."
Verbatim from the release: "Use mcp__brave-search__.* to match all tools from a hyphenated MCP server."
04
The second thing the same update changed (plugins)
v2.1.195 also tightened plugin security. External plugins that were enabled only by a project's .claude/settings.json now require explicit install consent on every loader path — they no longer load automatically just because a project config declared them.
- This is a good security change: a checked-in project config can no longer silently activate a third-party plugin on your machine.
- The side-effect: a plugin you were relying on (via a project's settings.json) may now be sitting un-loaded, waiting for consent you haven't given.
- Re-check: if a project-level plugin's behaviour vanished alongside your hooks, this is why. Approve the ones you trust, intentionally.
Two silent changes in one release — hooks and plugins. Both are safer-by-default; both can leave something you depended on quietly switched off.
05
The operator habit underneath this
The specific bug will age out. The habit won't: after any AI-tool update, assume something you relied on may have silently changed, and spend 30 seconds confirming it still works.
- After an update, skim the release notes for the words 'match', 'default', 'consent', 'breaking', or 'now requires'.
- Re-run your most important automation once — a hook, a plugin, an agent command — and confirm it actually fired.
- Treat 'no error' as 'unverified', not 'fine'. Silent failures are the expensive ones.
- Keep your config in version control so you can see what a release reset or what you changed in response.
These tools ship fast and tighten defaults often. The thing that should never be on autopilot is whether the automations you depend on are still firing.
Get the next drop
One AI operator move a week, plus the occasional bonus template. No spam, unsubscribe anytime.
By submitting you agree to our Privacy Policy & Terms. Unsubscribe anytime.
You're in — check your inbox to confirm.
Frequently asked questions
How do I know if my hooks actually broke?
Check whether any of your hook matchers contain a hyphen — e.g. code-reviewer or mcp__brave-search. Before v2.1.195 those matched on a substring; now they exact-match, so they fire on nothing unless you migrate them. Plain alphanumeric matchers (Bash, Edit) are unaffected. The only sure test is to trigger the hook once and confirm it ran.
What's the exact fix for an mcp__brave-search hook?
Rewrite the matcher as mcp__brave-search__. — that's the matcher name, two underscores, then . (dot-star wildcard). The release notes state this verbatim: 'Use mcp__brave-search__. to match all tools from a hyphenated MCP server.' The general shape is <matcher>__. to match a whole tool family.
Why did Anthropic make this change?
It's a correctness and security fix. Substring matching meant a matcher could accidentally fire on a partial name it wasn't meant to. Exact-match removes that surprise. The downside is purely migration: configs that quietly relied on the old substring behaviour need updating to wildcards.
Did this update change anything besides hooks?
Yes. The same v2.1.195 release also made external plugins that were enabled only by a project's .claude/settings.json require explicit install consent on every loader path. So a plugin you relied on via a project config may now be sitting un-loaded until you approve it. If a plugin's behaviour vanished alongside your hooks, that's why.
Is there a habit that prevents this kind of surprise?
Yes — treat every AI-tool update as potentially having silently changed something you depend on. Skim release notes for 'match', 'default', 'consent', 'breaking', or 'now requires'; re-run your most important automation once and confirm it fired; and treat 'no error' as 'unverified' rather than 'fine'. Thirty seconds of checking beats a silent failure you discover days later.