A tool listed in permissions.allow is denied by the permission classifier

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 0 comments · opened Aug 3, 2026

Summary

A tool named in permissions.allow is denied by the permission classifier before the call reaches the MCP server.

Version

Claude Code 2.1.220 on Linux, installed 2026-07-27 and launched through a wrapper that sets DISABLE_UPDATES=1, so the binary should not have self-updated.

The MCP server is a local stdio server, https://github.com/fade/dsmr-mcp. It is named for completeness; nothing in the report depends on it. Any stdio server offering a tool named in permissions.allow should serve to reproduce.

Configuration

All three configuration files parse cleanly, so no entry is being dropped by a malformed file:

$ jq empty ~/.claude/settings.json && echo valid
valid
$ jq empty ~/.claude/settings.local.json && echo valid
valid
$ jq empty ~/.claude.json && echo valid
valid

The denied tool is present in the allow list actually parsed from the file:

$ jq '.permissions.allow | index("mcp__dsmr-mcp__fs-set-project-root")' ~/.claude/settings.local.json
12
$ jq '.permissions.allow | length' ~/.claude/settings.local.json
16

The other fifteen entries are unrelated to this report. No deny or ask rule matches the tool in any of the three files:

$ jq -c '.permissions.deny' ~/.claude/settings.json ~/.claude/settings.local.json
["Read(.env)","Read(~/.aws/**)","Read(~/.ssh/**)"]
["Bash(pkill:*)","Bash(killall:*)"]
$ jq -c '.permissions.ask' ~/.claude/settings.json ~/.claude/settings.local.json
null
null

~/.claude.json contains no allow, deny or ask entries, so it is not competing with the allow list:

$ jq '[paths(scalars) as $p
       | select($p[-2]? == "allow" or $p[-2]? == "deny" or $p[-2]? == "ask")] | length' ~/.claude.json
0

A classifier refusal is terminal in this configuration rather than becoming an interactive prompt:

$ jq '.skipAutoPermissionPrompt' ~/.claude/settings.json
true

Modification times of the three files:

$ stat -c '%y  %n' ~/.claude/settings.json ~/.claude/settings.local.json ~/.claude.json
2026-08-03 11:48:09 -0400  ~/.claude/settings.json
2026-08-02 13:27:51 -0400  ~/.claude/settings.local.json
2026-08-03 12:59:20 -0400  ~/.claude.json

Two of those timestamps fall on the day of the reported session, so to account for them: ~/.claude.json is rewritten by the application during normal use and holds no permissions entries, as shown above. The edit to ~/.claude/settings.json added Bash rules, and that file contains no MCP entries at all, so it does not participate in adjudicating the tool reported here:

$ jq -c '.permissions.allow' ~/.claude/settings.json
["Bash(git push:*)","Bash(git rebase:*)","Bash(git merge:*)","Bash(git cherry-pick:*)","Bash(git worktree:*)","Bash(git tag:*)","Bash(git stash:*)"]

The allow list carrying the denied tool, in ~/.claude/settings.local.json, was last written on 2026-08-02 and is unchanged since.

On the order of events, which matters for reading the rest: this configuration had been in service for months. Refusals of this tool then began appearing, and the 2026-08-02 edit was made in response to them, adding the allow entry to clear one. The entry is therefore a consequence of the behaviour reported here, not a change that preceded it. The denials continued afterwards, which is the substance of this report.

Correlation is not causation, and nothing here establishes what changed. But the refusals arrived suddenly against a long-stable configuration, and then persisted through the documented remedy for them.

Observed

All of the following occurred in a single session on 2026-08-03.

  1. A call to mcp__dsmr-mcp__fs-set-project-root, present in permissions.allow as shown above, was denied. The refusal, as delivered to the caller:
Permission for this action was denied by the Claude Code auto mode classifier.
Reason: Blocked by classifier.
If you have other tasks that don't depend on this action, continue working on those.
IMPORTANT: You *may* attempt to accomplish this action using other tools that might
naturally be used to accomplish this goal, e.g. using head instead of cat.
But you *should not* attempt to work around this denial in malicious ways, e.g. do not
use your ability to run tests to execute non-test actions.
You should only try to work around this restriction in reasonable ways that do not
attempt to bypass the intent behind this denial.
If you believe this capability is essential to complete the user's request, STOP and
explain to the user what you were trying to do and why you need this permission.
Let the user decide how to proceed.
To allow this type of action in the future, the user can add a Bash permission rule to
their settings.

That is the text as it reached the caller, with line breaks inserted for readability: the original arrives as a single unbroken line. Nothing else is altered, and the emphasis markers around *may* and *should not* are the classifier's own. The text has not been verified against whatever the classifier emitted upstream of the harness.

  1. The identical call was retried once, minutes later: same tool, same arguments, same session, no configuration change in between. It was denied again, returning that message character for character. No third attempt was made, so it is not established whether the denial clears over a longer interval.
  1. The closing sentence of that message advises adding a Bash permission rule. The denied call was an MCP tool, not a Bash command, and the rule that would authorise it is already present. The same message, including that sentence, was also returned in the same session for a read-only Bash call, so the text does not appear to vary with the kind of tool denied.
  1. Earlier in the same session the same call did reach the MCP server, which refused it with its own error, reroot-permission-required, carrying the text Re-rooting to <path> requires human approval. Call fs-set-project-root with human_approved: true after manual verification. These refusals are therefore attributable to different layers: the earliest to the server, the later two to something ahead of it.

Expected

A tool named in permissions.allow is not subject to per-call classifier judgment.

Impact

While the denial persists, work routed through this MCP server cannot proceed, and the allow list offers no further remedy: the entry that would authorise the call is already present.

Second symptom, same session

Other tools from the same server, among them lisp-edit-form, lisp-patch-form and load-system, are absent from permissions.allow. Per-call judgment is expected for them and no guarantee is violated. They had been in routine use for months without producing denials.

In the session of 2026-08-03 they were denied repeatedly. A lisp-edit-form call was refused with:

Auto mode classifier requires confirmation for this tool.

Four consecutive denials then tripped a circuit breaker, halting the subagent that was making the calls:

4 consecutive actions were blocked. Please review the transcript before continuing.

Latest blocked action: Blocked by classifier

Session transcripts are retained locally and can be provided on request.

No call count is claimed for that history; the figure was not measured.

Progression

The two symptoms are a day apart, and the scope widened between them.

The 2026-08-02 edit was a surgical allow for a single tool that is called only when a session changes its project root. Adding one entry was a proportionate response to a narrow problem.

On 2026-08-03 the denials cover the tools through which all source editing and verification pass, and which therefore sit on the ordinary working path rather than on an occasional one.

So the affected surface moved from one narrowly used tool to the ones carrying the ordinary work, over roughly a day, and the only permissions change in that window added an entry rather than removing one. No rate is claimed for either period, since neither was measured. The observation is the widening itself.

Scope

The reported defect is the allow-listed tool being denied. The second symptom and the progression are corroborating evidence that an outcome changed, not claimed defects in themselves.

View original on GitHub ↗