Using AI

Why claude -p Denies MCP Tools: Allowing Just 5 Operations with --allowedTools

Why claude -p Denies MCP Tools: Allowing Just 5 Operations with --allowedTools

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.

POINT
  • In unattended claude -p runs, any action that needs confirmation is denied
  • --permission-mode acceptEdits only lets through file edits and a handful of file-operation commands
  • MCP tools go through once you list them explicitly in --allowedTools or permissions.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.

ModeWhat runs without askingUse case named in the docs
defaultReads onlyWork where you check every action yourself
acceptEditsReads, file edits, and some file-operation commandsWork where you review the code as you go
planMostly reads (edits need approval)Investigating before making changes
autoA classifier reviews each action and decidesLong tasks
dontAskReads and pre-approved tools onlyLocked-down CI and scripts
bypassPermissionsAlmost everythingIsolated 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.

  1. The --permission-mode flag (or --dangerously-skip-permissions)
  2. permissions.defaultMode in 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.

SyntaxWhat it covers
mcp__puppeteerEvery tool on the puppeteer server
mcp__puppeteer__*Likewise, every tool on the server (wildcard)
mcp__puppeteer__puppeteer_navigateOnly 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.

OperationAllowedUse for the shopping list
SearchYesFind the list and items
FetchYesRead the contents of a page
QueryYesFilter the database by conditions
CreateYesAdd a new item
UpdateYesChange a status such as “Buy next time”
DeleteNoNot used
Anything other than the 5 aboveNoNot 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.

Try the fee simulator and get 100 points, no sign-up required Review your phone bill with Rakuten Mobile

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.

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.

  1. Write out in plain words what you want the bot to do
  2. Pick the MCP tools those actions need
  3. Check the real tool names in the form mcp__<server name>__<tool name>
  4. List them in --allowedTools or permissions.allow
  5. If there are tools you never want used, put them in deny
  6. Actually call it with claude -p and see whether any denial appears
  7. 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

Warning
  • 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

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

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.

Try the fee simulator and get 100 points, no sign-up required Check Rakuten Mobile plans and pricing

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 .

ABOUT THE AUTHOR
まさきん

Rakuten Group employee · Digital Marketer (holds a Financial Planner qualification)

In his early 40s, part of a dual-income household with four kids. Works as a digital marketer at Rakuten Group, while also using his Financial Planner (FP) qualification to focus on household finances and building assets.

View Profile

Related Articles

RELATED
Gemini (Google AI Pro) Family Sharing Covers Up to 5 Family Members: Setup Steps and Scope Using AI

Gemini (Google AI Pro) Family Sharing Covers Up to 5 Family Members: Setup Steps and Scope

Google AI Pro can be shared with up to 5 family members besides the manager. The Gemini app and storage are shared, while YouTube Premium Lite is for the manager only. Flow limits are shared across the family. I went through the official help to sort out the setup steps and how child accounts are handled.

2026.10.13 · 11 min read
3 Reasons Your AI Subscription Billing Date Does Not Match Your Budget: Claude Upgrades Change It Using AI

3 Reasons Your AI Subscription Billing Date Does Not Match Your Budget: Claude Upgrades Change It

If you upgrade Claude to Max mid-month, your billing cycle resets and that day becomes your new billing date. The yen conversion of dollar charges is set when the data reaches your card company, and statements and budgeting apps lag by a few days. Here are 5 steps to decide the month using the receipt email as your source of truth.

2026.10.13 · 12 min read
Turning Unprocessed freee Accounting Transactions into a Monthly Reconciliation Run: 7:30 on the 5th, 3 Categories, and a Cap of 15 Items Handed to a Human Using AI

Turning Unprocessed freee Accounting Transactions into a Monthly Reconciliation Run: 7:30 on the 5th, 3 Categories, and a Cap of 15 Items Handed to a Human

About 18 unprocessed transactions sat untouched in freee Accounting across 6 weekly checks. Here is my design for a reconciliation run on my home Mac at 7:30 a.m. on the 5th of each month, and where I drew the line: one monthly task for the items that can be confirmed, and one task each, capped at 15, for the ones that need a human decision.

2026.10.12 · 13 min read
Switch to Rakuten Mobile, get up to 14,000 points Try the fee simulator and get 100 points, no sign-up required →
Apply Now →