◉CLAUDE LABJP
●2.1.302 — Fork subagents are now on by default in claude -p, the Agent SDK and the desktop app. Turn them off with CLAUDE_CODE_FORK_SUBAGENT=0 (10/16)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 43 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.302 — Fork subagents are now on by default in claude -p, the Agent SDK and the desktop app. Turn them off with CLAUDE_CODE_FORK_SUBAGENT=0 (10/16)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 43 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/Claude Code
⟐ Claude Code/2026-10-19Beginner

If You Wrote MCP Allow Rules as Just `mcp__servername` — Three Lines to Recheck After v2.1.299

Claude Code v2.1.299 changed MCP allow rules to match only the server they name. Here is how to audit older entries in settings.json, with a short Python script that does it.

Claude Code266MCP56permissions11settings.json9v2.1.299troubleshooting94

On a Monday morning I sat with settings.json open and stopped on one line in the allow list: mcp__figma, and nothing after it.

I couldn't remember writing it. I did remember what it was meant to do — allow the tools of the figma MCP server as a group.

Rereading the official changelog, I found an entry in v2.1.299 (October 14) that touches exactly this style. What I'd like to say first is that you don't need to rewrite everything in a hurry. Start by counting which lines are affected.

What changed

The changelog entry is short. MCP allow rules now reach only the server they name. mcp__a no longer allows a server called a__b or a_, and you're pointed to write mcp__<server>__* using the server's whole normalized name.

So rules that leaned on prefix-style matching are read more narrowly than before. The change narrows permissions; it doesn't widen them, and that decides the order of the recheck.

When permissions narrow, tools you thought were allowed go back to asking for approval. In an unattended run, that is where things stop.

One related note: v2.1.300 fixed deny and ask rules for mcp__<server> not applying when the normalized server name contains a double underscore. That is about the deny side, so I'll keep it separate here.

Only three shapes to check

I sorted the MCP lines in settings.json into three shapes.

FormMeaningFrom v2.1.299
`mcp__github__*`All tools of the github serverKeeps working
`mcp__figma__get_screenshot`One specific toolKeeps working
`mcp__figma`Server name onlyRecheck; candidate for `mcp__figma__*`

The third shape is the one I had.

An audit script

Reading by eye misses lines, so I wrote a short script. It reads settings files and sorts only the MCP allow entries into those shapes.

#!/usr/bin/env python3
"""Audit MCP allow rules in settings.json against whole-server-name matching."""
import json, re, sys
from pathlib import Path
 
def rules(path):
    try:
        data = json.loads(Path(path).read_text(encoding="utf-8"))
    except FileNotFoundError:
        return []                      # skip missing files quietly
    return data.get("permissions", {}).get("allow", [])
 
def check(rule):
    m = re.fullmatch(r"mcp__(.+)", rule)
    if not m:
        return None                    # not an MCP rule
    rest = m.group(1)
    if rest.endswith("__*"):
        return ("OK", f"all tools of server '{rest[:-3]}'")
    if "__" not in rest:
        return ("CHECK", f"server name only; consider mcp__{rest}__*")
    return ("OK", "tool name given")
 
for p in sys.argv[1:]:
    for r in rules(p):
        res = check(r)
        if res:
            print(f"{res[0]:6} {r:40} {res[1]}   ({p})")

Pass the settings files you want checked:

python3 audit_mcp_allow.py ~/.claude/settings.json .claude/settings.json .claude/settings.local.json

I ran it on a test file containing mcp__notion, mcp__github__* and mcp__figma__get_screenshot. Only the mcp__notion line came back as CHECK. Missing files don't stop it, since not every project has all three.

Confirm the server name before rewriting

I nearly made a mistake here: replacing mcp__figma with mcp__figma__* mechanically.

The changelog asks for the server's whole normalized name, and the name I typed in config may not be the name that appears in tool names. Before rewriting, I look at a real tool name in an approval prompt or in /mcp, and copy the part between mcp__ and the next __.

  • Copy the server part of the tool name from the prompt as shown
  • Don't fill it in by guesswork; check line by line
  • After the edit, call that tool once and confirm no approval prompt appears

Don't widen more than you must

mcp__<server>__* is convenient, but it allows every tool of that server. If a server only needs read access, turning it into a blanket allow defeats the point of this review.

Two earlier articles approach permissions for unattended runs from other angles:

Pick the scope by server, and widen by tool. I try to recall that line each time I add a row.

If you do one thing, run the script once and count how many CHECK lines appear. If it's zero, you're done for today.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

⟐ Claude Code2026-08-27
Trusting the allow rules in your repo, or moving them to the environment
On disposable machines, the permissions.allow rules committed to your repo are dropped while the workspace waits to be trusted. Here is what gets dropped and what survives on 2.1.246, where each kind of rule belongs, and a preflight that catches the gap before a run starts.
⟐ Claude Code2026-08-25
A One-Letter Typo in settings.json Is Ignored Without a Single Warning
I diffed claude doctor output between a settings.json with misspelled keys and a correct one. There was no difference at all. Here is what actually gets validated, what slips through, and a small check that catches typos before they cost you a day.
⟐ Claude Code2026-08-21
My read-deny rules never reached inside the folder I had allowed
An audit of how far permissions.deny Read rules actually reach in a working tree where secrets and source code live side by side, with a script you can run today.