Skip to content

All posts

AI

How to set tool-level authorization for AI agents

Ritesh Patel

AI: How to set tool-level authorization for AI agents

Tool-level authorization decides, for each tool an agent can call, whether the call runs, waits for a person or is refused. The MCP tools specification splits that work: the server must enforce access controls on its tools, and the client should ask a person before sensitive operations. This post maps the split and shows how Claude Code evaluates tool rules.

Where the checks happen

A tool call can pass three checks. The server decides whether the caller may use a tool at all. The client decides whether the call runs without asking. A person decides when the client asks. Each check is only as strong as its implementation, so a policy that relies on one layer alone leaves gaps the other two would have closed.

The specification does not require one user interaction model. It says that “for trust & safety and security, there SHOULD always be a human in the loop with the ability to deny tool invocations.” That is a recommendation to clients, not a mechanism you can rely on without checking your client’s settings.

What the MCP spec asks of servers and clients

The security section of the tools specification splits the duties by party. Servers and clients have different obligations, and the spec uses its MUST and SHOULD keywords to mark how firm each one is.

  1. Servers MUST validate all tool inputs, implement proper access controls, rate limit tool invocations and sanitize tool outputs.
  2. Clients SHOULD prompt for user confirmation on sensitive operations and show tool inputs to the user before calling the server.
  3. Clients SHOULD validate tool results before passing them to the language model, implement timeouts for tool calls and log tool usage for audit purposes.

The spec does not say how a server should implement access controls, so the detail is yours to design. It also says clients must treat tool annotations as untrusted unless they come from trusted servers. Use a server’s descriptions and annotations as information. Do not write allow rules from them alone.

How Claude Code evaluates a tool rule

Claude Code has three rule types: allow rules let a tool run without approval, ask rules prompt every time, and deny rules block the tool. Rules are checked in a fixed order, which matters more than how specific each rule is.

The permissions page states the order directly: “Rules are evaluated in order: deny, then ask, then allow. The first match in that order determines the outcome, and rule specificity doesn’t change the order.” An allow rule cannot override a deny rule, so a narrow allow for one tool does not reopen a server you denied.

A bare tool name in a deny rule removes the tool from Claude’s context, so the model never sees it. A scoped rule leaves the tool in place and blocks only the matching calls. Rules you add during a session apply from the next tool call.

MCP tool rule syntax

MCP tools use the name mcp__<server>__<tool>. You can match one tool, every tool from a server, or every MCP tool across all servers. Only some of these forms work in each rule type, so check which one you need before you write it.

Rule Matches Notes
mcp__puppeteer__puppeteer_navigate One tool, from the puppeteer server Use the full name when only one tool is risky
mcp__puppeteer__* Every tool from the puppeteer server Works as allow, ask or deny
mcp__github__get_* Tools from github whose names start with get_ Allow glob with a fixed server prefix
mcp__* Every MCP tool on every server Deny or ask only. An allow glob like this is skipped

For <server>, write the name as it appears in the tool names. Characters such as . or spaces become _, so a server configured as my.server is written mcp__my_server__*.

Example rules

A deny on every MCP tool is the simplest starting point. With this rule, every MCP tool is removed from Claude’s context. The pages fetched on 2026-11-11 name no single release, so record the Claude Code version you test on.

{
  "permissions": {
    "deny": [
      "mcp__*"
    ]
  }
}

The same allow and deny shape applies to built-in tools. This example from the Bash section allows two command prefixes and blocks a push.

{
  "permissions": {
    "allow": [
      "Bash(npm run *)",
      "Bash(git commit *)"
    ],
    "deny": [
      "Bash(git push *)"
    ]
  }
}

If you write an MCP server, you can mark a single tool so that Claude Code asks on every call. The flag is set in the tool’s tools/list entry.

{
  "name": "grant_access",
  "description": "Requests access to a protected resource",
  "inputSchema": {
    "type": "object",
    "properties": {}
  },
  "_meta": {
    "anthropic/requiresUserInteraction": true
  }
}

Gaps a rule file does not close

Several limits appear only in the fine print. Each one changes what a policy can promise.

  • Parameter matching on MCP tools is not in settings files. The docs say a settings file skips any mcp__ rule with parentheses. The documented route is a deny rule passed with --disallowedTools.
  • A Bash rule matches the command text Claude writes. The docs say a deny rule “isn’t a security boundary around the program,” because the same program can run in another form. Pair it with the sandbox network allowlist when the restriction must hold.
  • Read rules do not cover a command that reads files without naming them, or a script that opens files itself. The docs point to OS-level sandboxing for that case.
  • An organization can set a connector tool to ask. Allow rules for that tool then do not skip the prompt, and in dontAsk mode the call is denied.

Mapping the controls

Each control is enforced by a different party, so a gap in one does not show up in the others. The table lists what each source says the control covers.

Control Enforced by Limit stated in the source
Allow, ask and deny rules Claude Code, the client Matches command text for Bash. A deny on one form does not cover another form of the same program
Input validation and access controls MCP server Required by the spec. The spec does not define the access model
Confirmation on sensitive operations Client UI A SHOULD. The spec does not mandate an interaction model
Per-tool forced prompt MCP server metadata, read by Claude Code Prompts on every call, with no remember option
Usage logging Client A SHOULD. The spec does not define a log format
Insufficient permissions Server, HTTP 403 The authorization spec returns 403 for invalid scopes or insufficient permissions

Next steps for your tool policy

Start by writing the list of tools each agent can reach, by full name. Then work through the rest in this order.

  1. Mark every tool that changes state. Those need an ask or deny rule.
  2. Deny whole servers you have not approved with mcp__<server>__*.
  3. Allow only narrow names, such as mcp__<server>__get_*, and test that each allow matches only the tools you meant.
  4. Check each server for its own access controls and scope checks. Client rules do not replace them.
  5. Keep the settings file in version control. The permissions page says you can check these settings in so the whole team shares them.
  6. Record the Claude Code version you tested on, and rerun the examples when the permissions page changes.

For the server-level side of the same job, read how to restrict which MCP servers Claude Code can use. For the record of what an agent called, read how to audit MCP tool calls. The MCP security hub covers the wider topic. The AIControls product page is a product overview only. This post makes no claim about whether that product enforces tool-level rules.

Sources

Frequently asked questions

What is tool-level authorization for AI agents?

It is a decision made for each tool an agent can call. The call can run, wait for a person to approve it, or be refused. The decision can use the tool's name and, where the tool supports it, its input values. It is separate from deciding which server or account the agent uses at all.

Who enforces tool-level rules, the model or the client?

In Claude Code, the permission rules are enforced by Claude Code, not by the model. Instructions in a prompt or CLAUDE.md shape what the model tries to do, but they do not change what Claude Code allows. The MCP specification also places access control on the server, which must implement it for its own tools.

Can an allow rule override a deny rule for an MCP tool?

No. In Claude Code, rules are checked in the order deny, then ask, then allow, and the first match decides the outcome. An allow rule cannot carve an exception out of a deny rule, and a more specific allow rule does not change the order. A deny on mcp__* therefore stays in force even when a narrower allow rule also matches.

How do I block one MCP tool without blocking the whole server?

Write a deny rule with the full name of the tool, in the form mcp__<server>__<tool>. The Claude Code permissions page gives this form for a single tool. A wildcard such as mcp__<server>__* covers every tool from that server, so use the full name when only one tool is risky.

Does a PreToolUse hook replace permission rules?

No. A hook can block a call, but Claude Code still evaluates deny and ask rules when a hook returns allow or ask. A matching deny rule blocks the call and a matching ask rule still prompts. The docs also say a hook that exits with code 2 blocks the call even when an allow rule would permit it.

Ritesh Patel, Founder & VP Product, Nirmata

Ritesh Patel is a founder of Nirmata and leads its products. He writes about governing AI coding agents, MCP security and Kubernetes policy as code.

LinkedIn

Govern AI agents with AIControlsControl what AI agents do, and prove it.
ShareXLinkedIn

Stay up-to-date with the latest in AI, governance, and infrastructure

Subscribe to our free monthly newsletter to get industry news, DevOps tips, and articles right to your inbox on Kyverno, containerization, Kubernetes deployment & management and more.