[BUG] macOS / Pencil MCP: tools spuriously rejected as 'user denied' without permission prompt (v2.1.120)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
In a Claude Code session on macOS, every call to a mcp__pencil__* tool returns the system signal:
The user doesn't want to proceed with this tool use. The tool use was rejected
without any permission prompt being displayed, and without any user interaction. The Pencil MCP server itself reports as healthy (✓ Connected), and the ~/.claude/settings.json allow-list contains an entry for it. The same setup worked in earlier sessions on this exact machine; the regression was first noticed today on Claude Code 2.1.120.
This is closely related to (but separate from) #53169 — that report is for mcp__codebrain__* on Windows. I am filing this one to give Anthropic a clean macOS / different-MCP-server data point so the bug is not mistakenly scoped to Windows or to a single MCP package.
Concrete trace
Tool call: mcp__pencil__get_editor_state({ "include_schema": true })
Result: The user doesn't want to proceed with this tool use. The tool use was rejected
(eg. if it was a file edit, the new_string was NOT written to the file).
STOP what you are doing and wait for the user to tell you how to proceed.
- No permission prompt was shown.
- The user was watching the screen and pressed nothing.
- The macOS Notification hook (
osascript ... display notification "Claude Code が許可を求めています") also did not fire, which suggests the request never reached the permission UI in the first place — it appears to be auto-rejected upstream.
Other MCP servers in the same session (dart, github, context7, container-use, mobile-mcp, o3) all work normally, and non-MCP tools (Bash, Read, Edit, Write, Grep) also work. The block is specific to mcp__pencil__*.
What Should Happen?
In order of preference:
- The tool just runs. Once
mcp__pencil__*(or an explicit allow entry) is inpermissions.allow, subsequent calls should execute and return their real result. Earlier sessions on the same machine confirm this used to work. - If a permission really is required, the standard permission prompt should be displayed and the macOS
Notificationhook should fire — explicit approve / deny by the user, not silent auto-deny. - If something goes wrong at the transport / server level, the model should be told the truth (e.g.
"MCP transport error: ...","Tool call timed out after Ns"), not the literal string used for genuine user denials. Reusing the user-denial string for transport errors poisons the model's decision-making — per its operational rules it stops retrying and asks the user, even though the user did nothing.
Error Messages/Logs
Tool: mcp__pencil__get_editor_state
Args: { "include_schema": true }
Result: The user doesn't want to proceed with this tool use. The tool use was rejected
(eg. if it was a file edit, the new_string was NOT written to the file).
STOP what you are doing and wait for the user to tell you how to proceed.
claude mcp list:
pencil: /Applications/Pencil.app/Contents/Resources/app.asar.unpacked/out/mcp-server-darwin-arm64 --app desktop - ✓ Connected
claude mcp get pencil:
pencil:
Scope: User config (available in all your projects)
Status: ✓ Connected
Type: stdio
Command: /Applications/Pencil.app/Contents/Resources/app.asar.unpacked/out/mcp-server-darwin-arm64
Args: --app desktop
~/.claude/settings.json (excerpt):
{
"permissions": {
"allow": [
"mcp__pencil",
"...other entries..."
],
"deny": [
"...non-MCP entries only..."
]
}
}
Steps to Reproduce
- macOS 15.5, Apple Silicon.
- Install Pencil.app and ensure its bundled stdio MCP server is registered:
```
claude mcp get pencil
✓ Connected
should show , command /Applications/Pencil.app/Contents/Resources/app.asar.unpacked/out/mcp-server-darwin-arm64 --app desktop`.
- Confirm Pencil.app desktop is running.
- In
~/.claude/settings.json, ensurepermissions.allowcontains an entry for the server ("mcp__pencil"). Nodenyentry for it. - Start a new Claude Code session:
claude. - From the chat, request a Pencil action that requires a Pencil MCP tool, e.g. "open the editor and tell me what node is selected" — this triggers a
mcp__pencil__get_editor_statecall. - Observe: the tool result is the literal
user deniedrejection string, with no permission prompt shown and noNotificationhook fired. - Repeat with
mcp__pencil__open_document/mcp__pencil__batch_get— same auto-rejection. - In the same session, call any non-pencil tool (
Bash,Read,mcp__github__*,mcp__context7__*) — all work normally.
Claude Model
_(default; not relevant — model is invoking the tool correctly, the rejection happens before reaching it)_
Is this a regression?
Yes — mcp__pencil__* tools worked in earlier sessions on the same machine with the same ~/.claude/settings.json and the same Pencil version. The regression was first observed on 2.1.120.
Last Working Version
Unknown precise build, but mcp__pencil__open_document / mcp__pencil__batch_get / mcp__pencil__get_editor_state were used successfully in this project within roughly the past two weeks. Likely working on ~ 2.1.11x.
Claude Code Version
2.1.120 (Claude Code)
Platform
Claude Code CLI
Operating System
macOS 15.5 (Build 24F74), Apple Silicon
Terminal/Shell
zsh (default), running inside iTerm2 / macOS Terminal
Additional Information
- Related: #53169 — same symptom (
mcp__*tool returns literaluser deniedrejection without prompt) on Windows formcp__codebrain__*. Filing this issue separately because (a) different OS, (b) different MCP server, (c) different MCP transport (Pencil ships its own stdio binary), so Anthropic can confirm the bug is not Windows- or codebrain-specific. - A
PreToolUsehook is configured forBashonly (~/.claude/scripts/deny-check.sh); it should not affect MCP tools, and other MCP tools work fine in the same session. Disabling the hook does not change the symptom (tested separately). - Other MCP servers using stdio transport (
o3,dart,container-use,context7,mobile-mcp) all work normally in the same session, so this is not a generic stdio-MCP regression — it appears to be specific to which tool name / which MCP server gets auto-rejected. - Once a session enters this state, it does not recover for
mcp__pencil__*tools; restarting the session shows the same behavior immediately on the first call. Pencil app restart does not fix it.claude mcp restart pencil(re-adding the server) does not fix it.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗