FIELD NOTES / 4 MIN READ

Claude Agent SDK Can Now Force a Single MCP Tool to Always Ask, Even Under allowedTools

The Claude Agent SDK's permission evaluation order now lets a custom MCP tool carry its own permission_policy of always_ask or always_deny, which beats allowedTools and most permission modes. This explains what that field actually overrides, what still gets around it, and how to apply it to one destructive tool instead of gating the whole server.

AI-assisted editorial: researched and drafted with AI using the primary sources linked below. Examples are illustrative; they are not measured product results. Editorial policy.

Key takeaways

  • A tool's permission_policy of always_ask overrides allowedTools entirely: no allow rule, wildcard, or bare tool name approves that call, and it always reaches your canUseTool callback.
  • always_deny is stronger than bypassPermissions for that one tool: the docs describe a deny rule as blocking the call before canUseTool is ever invoked, even in bypassPermissions mode.
  • dontAsk mode denies an always_ask call outright instead of prompting, because that mode never prompts at all — don't assume always_ask guarantees a human sees the call under every permission mode.

The problem: one allow rule covers every call to a tool

Say your bot connects an MCP server with two tools: read_invoice and delete_record. You want Claude to use read_invoice freely but never run delete_record without a human looking at the input first. The obvious move is an allowedTools wildcard covering the whole server, but Anthropic's own MCP documentation shows that pattern approving every tool on the server at once, including the one you actually wanted to gate.

You could drop the wildcard and list only read_invoice, but then delete_record still runs the moment a broader permission mode covers it. The same documentation is explicit that permissionMode: 'bypassPermissions' auto-approves MCP tools and disables most other safety prompts, which is far broader than gating one tool. The gate was never attached to the tool. It lived in a list or a mode that anyone touching the config could widen by accident.

The Claude Agent SDK's permission evaluation order answers this by letting the tool carry its own policy instead of depending on what's in your allow list or your permission mode that day.

Sources: Connect to external tools with MCP - Claude Code Docs.

What permission_policy actually changes

The field sits on a custom tool's definition, with two values described in Anthropic's permissions documentation: always_ask and always_deny. The docs state that in the TypeScript SDK, if you set a custom tool's permission_policy to always_ask, no allow rule approves its calls, not even an allowedTools entry — the call always falls through to your canUseTool callback, which is where you decide case by case.

always_deny is the stronger version. The same documentation describes a deny rule as denying the call and skipping canUseTool entirely, even in bypassPermissions mode and even when a hook returned allow, and extends that same evaluation step to a custom tool whose permission_policy is set to always_deny: the call is denied at that step, full stop.

The key difference from a plain deny rule in settings.json is where the policy lives. A settings.json rule is project or user configuration that someone else can edit or override. A permission_policy set on the tool itself travels with the tool's own definition, so anyone who wires up that MCP server gets the gate by default, whether or not they know the tool is dangerous.

Sources: Configure permissions - Claude Code Docs.

Worked example: gating one tool on a billing server

Illustrative setup: your bot's MCP server exposes read_invoice and delete_record. You mark only delete_record with permission_policy: 'always_ask' on the server's tool list, and leave read_invoice with no policy at all. Your options still set an allowedTools wildcard covering both tools for convenience.

When Claude calls read_invoice, the wildcard approves it immediately — no callback, no prompt, because nothing overrides the allow rule for that tool. When Claude calls delete_record, the wildcard is ignored for that one call. canUseTool fires every time, and your callback gets the chance to inspect the input — which invoice, which record — before deciding allow or deny.

This is the practical win: you keep one broad allowedTools entry for convenience, and the dangerous tool still gets per-call scrutiny without a separate allow list to maintain. If you later add a second destructive tool to the same server, it runs under the wildcard's default approval until you explicitly mark it too — the policy has to be set per tool, not inherited from a sibling.

Where always_ask still gets bypassed

This field changes the evaluation order, but it doesn't guarantee a human sees every call. Anthropic's permissions documentation states that in dontAsk mode the call is denied instead of prompted, because that mode never prompts at all. So an always_ask tool under dontAsk mode never reaches your callback — it's silently denied, not silently approved, but it's also not the human-in-the-loop check you might have assumed you'd get.

The documentation is specific that this behavior is described for the TypeScript SDK. Don't assume an equivalent field exists in every SDK language without checking that SDK's own reference first.

Finally, the policy is a property of the tool definition you control, not a property of the MCP protocol itself. If a remote MCP server you don't operate exposes a destructive tool, you can't attach permission_policy to someone else's server-side tool definition. This field helps when you're the one wiring up the server configuration; for an external server you don't control, the gate still has to live in your allowedTools list, a settings.json deny rule, or your canUseTool callback logic.

Sources: Configure permissions - Claude Code Docs.

The decision: when to reach for this field

Use permission_policy: 'always_ask' on any MCP tool whose failure mode is hard to undo: deletes, sends, payments, irreversible writes. Don't treat it as a guarantee that a human always reviews the call — Anthropic's docs show dontAsk mode denying rather than prompting, so pair the field with a deliberate choice of permission mode, not just the policy alone.

If the tool lives on a remote MCP server you don't control, this field won't help you. Fall back to a narrow allowedTools list plus a settings.json deny rule for anything you never want approved, and avoid bypassPermissions mode for any session that touches that server, since the MCP documentation shows that mode auto-approving MCP tools broadly.

Before shipping, read the permissions page for your exact SDK and language. The behavior described here comes from the TypeScript-specific wording in Anthropic's current docs; check your own SDK's reference before assuming the same field and defaults apply.

Sources: Configure permissions - Claude Code Docs, Connect to external tools with MCP - Claude Code Docs.

Primary sources

Sources checked 2026-10-28. Standards and product documentation can change; follow the linked version when implementing.

  1. Configure permissions - Claude Code Docs — Anthropic
  2. Connect to external tools with MCP - Claude Code Docs — Anthropic

BotBento is in development. Suggest a correction.