Claude Code MCP permissions in 2026: when to use allowedTools instead of bypass mode

Claude Code MCP permissions can look like a small configuration detail until the agent has a database tool, a GitHub tool, a filesystem tool, and a Slack tool in the same session. At that point, the difference between allowedTools and bypassPermissions is not cosmetic. It is the difference between approving the one external tool you meant to expose and approving far more of the agent surface than the task needs.

The practical answer is simple. Use allowedTools when you want Claude to call a specific MCP server or a narrow set of MCP tools. Use permissionMode: "dontAsk" with an allowlist when the agent must stay locked down and deny anything outside that list. Use bypassPermissions only inside an isolated environment where you are comfortable with every tool that reaches the permission-mode step being approved automatically.

This is a field note for developers wiring Claude Code or the Claude Agent SDK to MCP servers in 2026. It is not a generic “MCP is dangerous” warning. MCP is useful. The problem is that useful tools are still tools, and tools need a boundary. A screwdriver is also useful, but I do not want it automatically editing production credentials because it felt inspired.

The short version

If your goal is “let this agent use the GitHub MCP server to list issues,” write an allowedTools rule for that server or that exact tool. If your goal is “let this agent read from a database but never write,” allow only the read query tool and keep the connection account read-only. If your goal is “stop asking me questions and just run everything,” pause and ask whether you are solving approval fatigue or deleting your safety layer.

The Claude Code MCP docs describe MCP tools as requiring explicit permission before Claude can call them. They also show the naming pattern mcp__<server-name>__<tool-name>, which makes allowlisting possible at the server or tool level. That is the key operating primitive. You can allow mcp__github__list_issues without approving every tool from every connected server.

The important caveat is that allowedTools is an allow rule, not a sandbox. It pre-approves the tools you list. It does not make unlisted tools disappear from the agent’s awareness, and it does not constrain bypassPermissions. If a tool is unlisted, the permission flow continues to the current permission mode or approval callback.

So the safe mental model is this: allowedTools is a narrow “yes” rule. disallowedTools is a hard “no” rule. permissionMode decides what happens when neither rule has already settled the request. When that mode is bypassPermissions, the remaining requests are broadly approved. That is convenient, but it is not narrow.

Why MCP changes the permission conversation

Before MCP, many coding-agent permission decisions were about files and shell commands. Can the agent edit this repo? Can it run tests? Can it install packages? Can it push? Those are still important, but MCP adds another axis: external systems. A tool call might read a ticket, query a database, trigger a workflow, post a message, update a CRM record, or fetch private context from a vendor API.

That makes the old “I trust this repository” question incomplete. You also need to ask whether you trust every MCP server, every tool on that server, every credential passed to that server, and every downstream action the tool can perform. A local repo can be harmless while its MCP tools are not harmless at all.

This is why acceptEdits is not the answer for MCP access. The official MCP page notes that acceptEdits does not auto-approve MCP tools; it is focused on file edits and filesystem Bash commands. If your only problem is that Claude keeps asking before changing files, acceptEdits may be relevant. If your problem is MCP access, use MCP-specific allow rules.

The risky shortcut is jumping from “MCP prompts are annoying” to bypassPermissions. The MCP docs warn that bypass mode does auto-approve MCP tools, but it also disables other safety prompts. That is broader than the job most teams actually have. Most teams do not need a permission bypass. They need a small allowlist that matches the server and tool names they intended to expose.

The decision table

Situation Better setting Why
Let Claude search documentation through one MCP server allowedTools: ["mcp__docs__*"] The server is scoped to a low-risk read task
Let Claude list GitHub issues only allowedTools: ["mcp__github__list_issues"] One read tool is enough
Let Claude query a read-only database allowedTools: ["mcp__db__query"] plus read-only credentials Tool allowlist and credential scope reinforce each other
Build a headless agent with a fixed surface allowedTools plus permissionMode: "dontAsk" Listed tools run, everything else is denied
Block a risky tool in every mode disallowedTools or a settings deny rule Deny rules are evaluated before permission mode
Run broad automation in a disposable container Carefully considered bypassPermissions Only if the environment is already isolated

The most useful row is the headless-agent row. If you are building a recurring job or SDK-based agent, prompts may be impossible or undesirable. That does not mean the correct move is broad bypass mode. Pairing an allowlist with dontAsk gives you a fixed tool surface: approved tools run, everything else fails closed.

That failure mode is important. A headless agent that silently waits for a human approval may hang. A headless agent that bypasses all prompts may do too much. A headless agent that denies unknown tools is easier to reason about. It is less magical, which is often another word for maintainable.

The permission-flow trap

The Claude Agent SDK permissions docs describe an evaluation order that matters in practice. Hooks run first, deny rules are checked, then the permission mode is applied, then allow rules and approval callbacks may be used depending on what happened earlier. The exact flow is a product detail, but the operating lesson is stable: do not assume that an allowlist is the same as a total boundary.

The docs explicitly call out the trap: setting allowed_tools=["Read"] together with permission_mode="bypassPermissions" still approves every tool that reaches bypass mode, including tools beyond Read. In TypeScript naming, the same warning applies to allowedTools and permissionMode: "bypassPermissions". That is the configuration smell this article is trying to prevent.

If you need a narrow MCP permission, do not mix a narrow allowlist with a broad bypass and expect the allowlist to magically win. Use dontAsk for a deny-by-default headless posture, or use default with a runtime approval callback when a human or controller can make decisions. If you truly need bypass, add hard deny rules for anything that must never run.

Subagents add another wrinkle. The permissions docs say broad permission modes such as bypassPermissions, acceptEdits, or auto can be inherited by subagents and may not be overridden per subagent. That means a parent session with broad permissions can accidentally grant a delegated worker more autonomy than you intended. If you use subagents, make the parent permission posture boring before you make the agent team clever.

A safe MCP rollout pattern

Start with one server and one read-only tool. For example, use a documentation MCP server or a GitHub read tool before attaching a database, cloud console, payment system, or production issue tracker. The first test is not “can the agent finish the task.” The first test is “can I predict which tool calls are possible.”

Next, inspect the tool names. MCP tools follow the mcp__server__tool naming pattern, so write the allowlist at the narrowest level that still keeps the workflow usable. If the server has ten tools and you only need two, allow two. If a wildcard is acceptable, make sure the whole server is low risk and read-oriented.

Then scope credentials outside Claude. A read-only database account is better than hoping the agent never asks for a write tool. A GitHub token with minimal scopes is better than a broad personal token. Permission rules and credential scope should agree with each other. If either one is too broad, the other has to carry too much weight.

After that, add logging. The MCP docs show how to inspect connection status and log tool calls from SDK messages. In a real team, you want to know which server connected, which tool was called, what credential class was used, and whether the result changed state. If nobody can reconstruct the tool call history, your permission design is still incomplete.

Finally, test the failure path. Ask the agent to do something outside the allowed surface and verify that it fails the way you expect. If unknown tools prompt forever, your headless job may hang. If unknown tools run, your boundary is too wide. If unknown tools deny cleanly, you have a workflow you can operate.

allowedTools vs settings rules

For SDK code, allowedTools is convenient because it travels with the agent configuration. A small script can declare the MCP servers and the exact tool names in one place. That is good for focused jobs, experiments, and deployable internal agents where code review can see the permission surface.

For team policy, settings rules are often better. Shared project settings can document which commands, files, web domains, and MCP tools are allowed or denied for a repository. Managed settings can go further and prevent risky modes from being used in an organization. The Claude Code permissions docs mention permissions.disableBypassPermissionsMode as a way to block bypass mode through managed settings.

The split I like is simple. Use code-level allowedTools for the specific job. Use shared or managed settings for the policy that should hold even if a developer changes the job. The job can say “use the GitHub issue reader.” The policy can say “never use bypass mode here” and “never read .env.”

This prevents a common drift pattern. A developer starts with a narrow MCP tool. The agent keeps prompting. The developer flips a broad mode to get unstuck. The broad mode becomes the new default. Six weeks later, nobody remembers why the repo has a giant permission surface. Congratulations, the settings file has become folklore. Folklore is fun around a campfire, less fun around production tokens.

What I would use in practice

For a docs search agent, I would use a wildcard for that one documentation server because the blast radius is small. Something like mcp__claude-code-docs__* is reasonable if the server only exposes documentation search and reading tools. I would still log calls because “low risk” does not mean “invisible.”

For a GitHub triage agent, I would start with read-only issue and pull request tools. I would not allow merge, branch protection changes, workflow dispatch, secret updates, or release publishing in the first pass. If the agent needs to write comments, I would separate that from read permissions and require a narrower review.

For a database agent, I would use read-only credentials, allow only the query tool, and prefer a database user that cannot mutate state even if a tool bug or prompt injection appears. Tool allowlists are useful, but database permissions are still the real wall. One wall is good. Two walls are better. Walls are underrated until the model starts confidently improvising SQL.

For local file work, I would not reach for MCP first. If the task is simple file editing inside a repo, normal Claude Code file permissions and sandboxing may be enough. MCP should add a capability you cannot get cleanly from the default tool surface. Every extra server adds tool descriptions, auth, failures, and security review work.

For production-adjacent automation, I would keep bypassPermissions off. If the workflow truly needs fewer interruptions, I would use allow rules, deny rules, dontAsk, isolated credentials, and a disposable environment before considering bypass. The more boring the permission design looks, the easier it is to trust.

Related Reading

FAQ

Should I use allowedTools or bypassPermissions for MCP tools?

Use allowedTools when you want a specific MCP server or tool to run without repeated prompts. Use bypassPermissions only when the whole environment is isolated and you are comfortable with broad automatic approval. For most MCP workflows, allowedTools is the safer first move.

Does allowedTools hide unlisted tools from Claude?

No. The Claude Agent SDK permissions docs describe allow and deny rules as controlling approval, not tool availability. Unlisted tools may still exist and fall through to the active permission mode or approval callback.

What is the safest setup for a headless MCP agent?

Use a small allowedTools list together with permissionMode: "dontAsk" when you want a fixed surface. Approved tools run. Anything else is denied instead of prompting or being broadly approved.

Is acceptEdits enough for MCP tools?

No. acceptEdits is for auto-accepting file edits and certain filesystem operations. The Claude Code MCP docs say MCP tools still need explicit permission, so use MCP-specific allow rules.

What should I do if I must use bypass mode?

Run in a disposable container or VM, scope credentials tightly, add disallowedTools for anything that must never run, keep logs, and avoid production systems. Also consider disabling bypass mode through managed settings for shared team environments.

Sources