- 01The bot could not write to the shopping list
- 02The cause was a missing permission
- 03Nobody is there to answer in an unattended run
- 04Permission modes compared
- 05bypassPermissions had no effect
- 06How to write MCP tool names
- 07Listing tools explicitly with —allowedTools
- 08The 5 operations I allowed
- 09How I think about keeping permissions minimal
- 10Setup steps
- 11Common mistakes
- 12Things to check before you start
- 13Frequently asked questions
- 14Official sources I referred to
- 15Summary
Good morning. まさきん here.
Our family chatbot could not write to our shopping list. The claude -p process running behind it was denying MCP tools every single time. Here are my notes on the cause and the fix, checked against the official documentation.
Note: This article reflects information at the time of publication. Please check the official sites for the latest terms.
- In unattended
claude -pruns, any action that needs confirmation is denied --permission-mode acceptEditsonly lets through file edits and a handful of file-operation commands- MCP tools go through once you list them explicitly in
--allowedToolsorpermissions.allow - The format is
mcp__<server name>__<tool name>, and you can also allow a whole server - I allowed just 5 Notion operations: search, fetch, query, create, and update
The bot could not write to the shopping list
We have a bot in the messaging app our family uses. I built it myself, and behind the scenes it calls claude -p. In other words, it runs Claude Code headlessly.
I wanted this bot to read and write our shopping list. The shopping list is a Notion database. Someone in the family sends “please pick this up,” and the bot adds it to the list. That was the idea.
But no matter how many times I tried, it could not write anything. Every call to a Notion tool was denied. It worked fine in an interactive session on my own machine, yet it failed only when it went through the bot.
The cause was a missing permission
The answer turned out to be simple. The claude -p process the bot calls had no permission for the Notion connector.
The bot runs with --permission-mode acceptEdits. That setting lets file edits through automatically. So I assumed I had already handed over a fair amount of control. MCP tools, however, were not inside that scope.
In my environment, MCP tools in unattended runs were denied every time. That continued until I listed them explicitly with --allowedTools. This is where the whole story starts.
Nobody is there to answer in an unattended run
Let me lay out the mechanism. Claude Code sometimes asks for permission before it acts. In an interactive session, a prompt appears on screen and a person answers it.
A run with -p is non-interactive mode. The official documentation covers it on the page titled “Run Claude Code programmatically.” It says that in a -p run with nobody set up to answer prompts, actions that need confirmation are denied.
So it does not “stop and wait to be asked.” It gets “turned down without being asked.” From behind a bot, that difference shows up as “always denied.” Once I understood the mechanism, it made sense.
Permission modes compared
Next, permission modes. The official “Choose a permission mode” page lists what each mode can run without asking.
| Mode | What runs without asking | Use case named in the docs |
|---|---|---|
| default | Reads only | Work where you check every action yourself |
| acceptEdits | Reads, file edits, and some file-operation commands | Work where you review the code as you go |
| plan | Mostly reads (edits need approval) | Investigating before making changes |
| auto | A classifier reviews each action and decides | Long tasks |
| dontAsk | Reads and pre-approved tools only | Locked-down CI and scripts |
| bypassPermissions | Almost everything | Isolated containers and VMs only |
The default mode appears on screen under the name Manual. As a setting value it is still default.
The mode is the foundation. Allow rules and deny rules are layered on top of it. The docs say deny rules apply in every mode, including bypassPermissions.
What acceptEdits lets through
What acceptEdits approves automatically is file edits inside the working directory. File-operation commands such as mkdir and mv also go through.
Other shell commands and network access, on the other hand, are out of scope. The docs say those need --allowedTools or a permissions.allow rule. MCP tools are not on the auto-approved list.
acceptEdits is “the mode where you hand over editing,” not “the mode where you hand over operating external services.” I think that is the natural way to read it.
bypassPermissions had no effect
In fact, I had written bypassPermissions in my settings file. It lives at ~/.claude/settings.json. So I was convinced everything should go through. Yet it had no effect on the bot.
The reason is in the docs. The starting mode is whichever of these applies first.
- The
--permission-modeflag (or--dangerously-skip-permissions) permissions.defaultModein the settings file
The CLI reference says the same thing. --permission-mode overrides defaultMode in the settings file. The bot starts with --permission-mode acceptEdits. So the bypassPermissions in my settings file was never used.
I have not checked the relationship between flags and the settings file in detail for anything other than the mode. I will leave this as a fact about my own environment.
How to write MCP tool names
MCP tool names in permission rules follow a fixed format. These are the forms listed in the official “Configure permissions” page.
| Syntax | What it covers |
|---|---|
mcp__puppeteer | Every tool on the puppeteer server |
mcp__puppeteer__* | Likewise, every tool on the server (wildcard) |
mcp__puppeteer__puppeteer_navigate | Only puppeteer_navigate on the puppeteer server |
mcp__github__get_* | Tools on the github server that start with get_ |
You can specify by server or by tool. Wildcards in allow rules come with a condition, though. They can only be used after mcp__<server name>__. The docs say an allow rule that blurs even the server name, like mcp__*, is ignored with a warning.
One more thing to watch is connector names. Tools from connectors that Claude Code fetches on its own have a slightly different shape. They are said to be mcp__claude_ai_<server name>__<tool name>. It is safest to check the real name before you write it.
Listing tools explicitly with —allowedTools
The fix was simply to list the tools I use in --allowedTools. The docs describe it as the flag that specifies “tools that execute without prompting.”
It looks like this. The server and tool names are examples. The real names differ by environment, so replace them with yours.
claude -p "Add milk to the shopping list" \
--permission-mode acceptEdits \
--allowedTools \
"mcp__notion__search" \
"mcp__notion__fetch" \
"mcp__notion__query" \
"mcp__notion__create_pages" \
"mcp__notion__update_page"
In my case, this allowlist lives inside the bot’s code. Every time the bot calls claude -p, it passes the same list.
You can also put it in the settings file. List the tools in the same format under permissions.allow in settings.json.
{
"permissions": {
"allow": ["mcp__notion__search", "mcp__notion__fetch"],
"deny": ["mcp__notion__delete_page"]
}
}
The tool names here are examples too. Where you put it may be a matter of taste. I feel more at ease when I can read “what this bot is able to do” in the code, so I chose the code side.
The 5 operations I allowed
If you allow the whole server, the configuration is a single line. I did not do that. I allowed only 5 operations.
| Operation | Allowed | Use for the shopping list |
|---|---|---|
| Search | Yes | Find the list and items |
| Fetch | Yes | Read the contents of a page |
| Query | Yes | Filter the database by conditions |
| Create | Yes | Add a new item |
| Update | Yes | Change a status such as “Buy next time” |
| Delete | No | Not used |
| Anything other than the 5 above | No | Not used |
Reading and writing the shopping list needs only these five. Delete is not included. When I want an item gone, updating its status is enough.
After I added the permissions, the bot started working. It now puts items the family asks for under “Buy next time” in the shopping list and reports back.
Trimming a tool’s permissions down to what it actually uses feels close to reviewing fixed costs. With a phone bill too, waste becomes easier to see once you match the plan to what you really use.
Clicking this opens the Rakuten login page. Once you log in, you’ll see the campaign details.
How I think about keeping permissions minimal
Why narrow it down? Because the input to the bot does not come only from me.
Family members send whatever text they like. There are slips of the tongue, and sometimes a message gets interpreted differently from what was meant. If delete were allowed at that moment, there would be no way back. With only create and update, a mistake can be fixed.
Laid out as steps, the thinking goes like this.
- First split things into “read operations” and “write operations”
- Split write operations into “add or change” and “remove”
- Leave out remove operations until you actually need them
- Keep in mind that a server-wide permission grows in scope whenever tools are added
The docs also describe the order in which rules are evaluated. The order is deny, ask, allow. The first match becomes the result. So putting a tool you never want used into deny is the sure way. There is also the option of writing the bare tool name in --disallowedTools. That tool is said to become invisible to Claude.
The same sense of “not feeling safe just because the form looks right” mattered when handling secrets too. I wrote about that in my article on the 2 secrets gitleaks missed.
The dontAsk option
While reading the official docs, one more mode caught my eye. It is dontAsk.
dontAsk is a mode that denies every action that would need confirmation. Only reads and things you approved in advance go through. The docs show an example of it for CI with a strictly defined allowlist.
claude -p "run the test suite" --permission-mode dontAsk --allowedTools "Bash(npm test)" "Read"
My bot stays on acceptEdits for now. There are times when I want it to handle file edits as well. Still, if all you need is operations on external services, dontAsk may be the better fit.
Note that a -p run with no permission mode specified needs care. The starting mode varies by environment and version. The docs also tell you to state the mode you want explicitly.
Setup steps
Here is the flow if you are about to do the same thing.
- Write out in plain words what you want the bot to do
- Pick the MCP tools those actions need
- Check the real tool names in the form
mcp__<server name>__<tool name> - List them in
--allowedToolsorpermissions.allow - If there are tools you never want used, put them in deny
- Actually call it with
claude -pand see whether any denial appears - Add to the allowlist only when the set of actions grows
How to check tool names and denials
Tool names written by guesswork will miss. The docs introduce an event called system/init. It is the first thing emitted with --output-format stream-json. It contains the model, the tools, and the MCP server information.
Denials can be traced in the same output. A denial appears as a system message called permission_denied. The final result message is also said to carry a list under permission_denials. Keeping these in the bot’s logs should make hunting for the cause faster.
Common mistakes
- Assuming MCP tools will go through once you set acceptEdits
- Assuming bypassPermissions in the settings file is stronger than the flag
- Writing tool names by guesswork so they do not match the real names
- Writing a broad permission like
mcp__*and not noticing it is being ignored - Getting tired of it and allowing the entire server
- Using bypassPermissions in unattended runs so that anything goes through
The first two are exactly what I did. The impression a mode’s name gives and the range it actually lets through were slightly different.
On bypassPermissions, the docs warn strongly. It is meant for use inside isolated containers and VMs. I do not think it suits a bot that receives the family’s input as is.
Things to check before you start
- Which permission mode the bot starts in
- Whether the mode is set by the launch flag or by the settings file
- Whether you have written out in words the actions you want to allow
- Whether you have checked the real names of the MCP tools
- Whether you have left out irreversible actions such as delete
- Whether you can trace denied tools in the logs
- Whether you have settled on one place to keep the allowlist
Frequently asked questions
Q. Why are MCP tools denied with claude -p?
-p is a non-interactive run. The docs say that when nobody is set up to answer prompts, actions that need confirmation are denied. In my environment, MCP tools were always denied until I allowed them explicitly.
Q. Will MCP tools go through if I set acceptEdits?
They did not. According to the docs, what acceptEdits approves automatically is file edits and some file-operation commands. Everything else is said to need --allowedTools or permissions.allow.
Q. I wrote bypassPermissions in settings.json, but it has no effect.
Check whether you are passing --permission-mode at launch. The docs say this flag overrides defaultMode in the settings file. My bot got caught on exactly this.
Q. Can I allow a whole server at once?
Yes. Writing mcp__<server name> or mcp__<server name>__* covers every tool on that server. That includes things like delete, though. I listed only the operations I needed, one by one.
Q. What is the difference between —allowedTools and —tools?
--allowedTools specifies tools that run without prompting. --tools is a flag that narrows down which built-in tools are available. The docs say --tools does not affect MCP tools.
Q. Can a tool still be blocked even after I write an allow rule?
It seems so. The docs say allow rules have no effect on connector tools that an organization has set to ask. The same goes for MCP tools marked as requiring user interaction. In dontAsk mode, these are denied.
Q. What happens if I add —bare?
--bare is a mode that skips automatic loading of things like MCP servers. The docs say only the MCP servers passed with --mcp-config get connected. Authentication also assumes an API key or similar, so it seems wise to check before adding it.
Q. Is there a way to state explicitly that nobody will answer prompts?
The docs list a flag called --permission-prompts none. It denies actions that need confirmation and tells Claude not to retry. It is said to require v2.1.259 or later. I have not used it yet.
Official sources I referred to
- Run Claude Code programmatically (Claude Code Docs)
- Choose a permission mode (Claude Code Docs)
- Configure permissions (Claude Code Docs)
- CLI reference (Claude Code Docs)
- All settings (Claude Code Docs)
- Configure permissions - Agent SDK (Claude Code Docs)
Summary
MCP tools were denied with claude -p because there was no permission for them. acceptEdits is a mode for file edits. Operations on external services had to be listed separately.
Narrowing permissions down to the operations you actually use seems to be the way to go. I started with 5 operations. Whether it is worth keeping a homemade bot running is something I sorted out in my article on Claude Max usage. The setup for consulting AI straight from a notification is also covered in my article on the “send to Claude” button. When your bot will not work, start by taking a look at its launch flags.
Clicking this opens the Rakuten login page. Once you log in, you’ll see the campaign details.
This article contains affiliate advertising. If you sign up for a product or service through a link on this site, we may receive compensation from the partner company. The site operator is also an employee of Rakuten Group and may receive compensation through an employee referral program. The content and opinions here are based on the operator’s own experience and research regardless of any advertising relationship, but please read with the above in mind. See the Disclaimer & Affiliate Disclosure for details. Disclaimer & Affiliate Disclosure
This article includes machine-translated content. Please check the official Rakuten Mobile multilingual page for exact terms.
If you have concerns about Rakuten Mobile coverage or signal quality, you can consult through the official signal improvement request form .