All articles

Approvals

How to require human approval for MCP tool calls

Tier Two10 min read

An agent is about to create an issue, run a SQL statement, or push a commit. You want a person to say yes first. The hard part isn't wanting that; it's choosing where the yes happens. Ask too early and the reviewer approves a plan, not a call, and never sees the arguments the model eventually chose. Ask too late and the review is commentary on something already running.

MCP doesn't settle this for you. The protocol says a human should be able to deny a tool invocation, then leaves the mechanism to the client. So the decision is yours, and it has three reasonable answers: rules inside the host, a shared point in the call path, or both. This is a guide to picking, with the exact syntax and the exact waiting behavior you'll get from each.

The short answer

Put the gate at the last point where the call can still be stopped: after the caller and the tool are known, after policy has said this call needs a person, and before anything reaches the downstream system. Hold that exact invocation, show the reviewer its arguments, and on approval forward the held call itself rather than asking the model to try again.

Four places a gate can sit, and what each one sees

Every approval scheme is a bet on who reviews, what they see, and what they can't route around. Laying the options side by side makes the trade explicit.

WhereWho reviewsWhat they seeWhat it covers
In the prompt ('ask me before writing')Nobody; the model decides whether to askWhatever the model chooses to summarizeNothing enforceable
In the host (Claude Code rules, Cursor prompts)The person driving that hostThe tool name and inputs, in that host's UIThat machine and that host
At a shared point in the call pathAn admin, who may be a different personIdentity, agent, host, tool, argument previewEvery agent that routes through it
In the destination (Linear's own permissions)Nobody, per callNothing; it answers can or can'tThat system, for that account

The prompt row is there because it's the most common setup and the least useful one. The MCP tools specification is blunt that tools are model-controlled; an instruction to the model to ask first is a preference, and a prompt injection in a fetched web page is a competing preference. Everything below assumes you want the second or third row, or both.

Host-native approval in Claude Code

Claude Code has a real permission system for MCP tools, and you should know it before deciding you need anything else. Rules live in the permissions block of a settings file and match tools by name: mcp__linear matches every tool from the server named linear, mcp__linear__save_issue matches one tool, and mcp__github__get_* matches a name pattern under a literal server prefix. There are three arrays. Deny is checked first, then ask, then allow, and the first match wins regardless of how specific a later rule is. A minimal Linear policy looks like this:

{
  "permissions": {
    "allow": ["mcp__linear__list_issues", "mcp__linear__get_issue"],
    "ask": ["mcp__linear__save_issue", "mcp__linear__save_comment"],
    "deny": ["mcp__linear__delete_comment"]
  }
}

Two details matter for teams. Project-scoped servers in a .mcp.json file prompt for approval before first use in an interactive session, and a setting called enableAllProjectMcpServers turns that prompt off, so check that key before assuming a colleague reviewed the server. And an organization can ship a managed settings file, at /Library/Application Support/ClaudeCode/managed-settings.json on macOS, that user and project settings can't override; it can also restrict which MCP servers may be configured at all. The permissions documentation has the current rule grammar, and the modes page covers the permission modes, from the default prompting mode through acceptEdits, plan, auto, dontAsk and bypassPermissions. One more knob is on the server side: a server can mark a tool with a requiresUserInteraction flag in its metadata, which forces a prompt on every call even under bypassPermissions.

What this buys you is a gate on one machine, reviewed by the person already driving the agent. That's the right shape for a solo developer and the wrong shape when the person who should say yes is someone else: a data owner, a lead, an on-call engineer. Managed settings fix who can edit the rule. They don't change who sees the prompt.

Host-native approval in Cursor

Cursor asks before using MCP tools by default. Its run modes decide what happens next: in Auto-review, allowlisted calls run immediately and the rest go to a classifier that judges whether to prompt; in Allowlist mode only listed actions run without approval; Run Everything does what it says. The allowlist is a permissions.json file, per user or per repository, with an mcpAllowlist array of patterns such as linear:list_*, server:* or *:tool. There is no MCP denylist key in that file; if you want a tool gone, take it away at the server or in the team dashboard.

Teams get more. An admin can publish MCP servers to the whole team, keep an allowlist of servers and per-server tools, and set network controls, and the Cursor MCP documentation states the precedence: team admin settings win over permissions.json, which wins over the IDE settings UI. Same limits as Claude Code, though. The prompt appears to the person using Cursor, on their screen, and a second host on the same laptop has its own rules.

A shared gate: what Tier Two holds, and who decides

A shared gate changes two things at once: the rule applies to every agent in the workspace, whatever host it runs in, and the reviewer can be someone other than the requester. In Tier Two you get there by setting a tool's mode to Ask. When an agent then calls that tool, Tier Two records the person, the agent, the connection, the tool and a preview of the arguments, and holds the call. The preview is deliberately partial: top-level keys only, strings truncated at 80 characters, arrays and objects collapsed to a shape hint, the whole thing capped at 2 KB. Raw arguments are never stored, and a connection can turn previews off entirely, at the cost of the approver seeing only the tool name.

Workspace admins approve; members can see their own pending requests and whether an admin has answered. The approver sees who is asking, from which agent and host, which tool, and the preview, then picks one of: approve once, approve for one hour, eight hours or seven days, always for that person, or deny. A connection can cap the longest remembered approval, enforced server side, so a team can decide that nothing on the production database is ever remembered for more than eight hours. Approve once opens a five-minute window that exactly one call can consume, which stops a retried request from riding a stale yes.

Nothing here replaces the host prompt. Claude Code will still show its own prompt if your rules say ask, and then Tier Two will hold the call for an admin. Most teams end up with Allow rules in the host for tools that the shared policy governs, so the developer isn't answering two questions about one call, and keep host-side deny rules as a belt for tools the workspace has Blocked anyway.

What the agent and the reviewer experience while a call waits

The waiting behavior is where most approval designs quietly fail, because a host that times out at thirty seconds turns every Ask into an error. Tier Two's hosted call waits in place for up to nine minutes. The agent receives a pending result carrying the request id and an approval link, a 'still waiting' notification every sixty seconds, and progress ticks if the host asked for them. The request itself lives fifteen minutes. Hosts in the Codex family can't hold a call open that long, so they get a checkpoint instead: the call returns an ask_pending result naming a built-in wait tool, tiertwo__await_approval, which the agent calls in slices of at most four minutes, and an approved answer tells the agent to retry the original tool exactly once.

The reviewer opens the link, which lands on the Approvals page with that request selected, and decides. Each outcome has one defined effect:

DecisionThe held callWhat the agent is toldActivity shows
Approve onceForwarded as-is; its result returns to the agentThe tool's normal resultAllowed, approved once
Approve for 1 hour, 8 hours or 7 daysForwarded; later calls by that person to that tool skip the Ask until expiryThe tool's normal resultAllowed, remembered, with the expiry
Always for that personForwarded; remembered until an admin revokes itThe tool's normal resultAllowed, remembered
DenyRefused, never forwarded'The human DENIED this action. Do not retry it and do not attempt to accomplish it through other tools or paths.'Asked, then a person said no
Nobody answersRefused when the request expires; nothing forwardedA refusal: no one answered in timeAsked, then no one answered in time

On approve there is no second invocation. The call that was reviewed is the call that runs, which is the whole point of holding it rather than telling the model to try again. And while a request is pending, identical requests from the same person for the same tool and scope fold into it, so a model stuck in a retry loop produces one entry in the queue, not forty.

Remembered approvals are the sharp edge

A remembered approval belongs to the person, not to the agent that asked. Any agent that person signs in with can use it until it expires or an admin revokes it, and a revoke means their next call asks again. That's a deliberate design: the thing being trusted is Dana's judgment about a tool, not a particular process on her laptop. It also means a seven-day approval on save_issue is a seven-day approval for every host Dana uses, and a reviewer granting 'always' should picture that before clicking.

Two rules keep this honest. A remembered approval is not a fresh review of each later call; if the arguments matter every time, use approve once and accept the interruptions. And if everyone in the workspace should run a tool without prompts, don't accumulate individual 'always' approvals; flip the tool to Allow, where the decision is visible workspace-wide and shows up in the governance ledger with the admin's name and a before-and-after diff. The Activity documentation describes both ledgers.

When approval is not enough

An Ask gate governs calls that pass through it. Four things routinely don't.

  • A second route to the same system. If a developer's host still has a direct entry for the Linear server next to the governed one, the agent can pick either. Tier Two's setup command offers to remove the direct entries so calls route through the workspace, and can undo that later; until you've done it, check the host config, not the dashboard.
  • Anything that isn't an MCP call. Shell commands, a browser tool, and a direct HTTP call to an API are outside the governed path entirely, and no MCP-level gate changes that.
  • An over-broad credential. When a connection uses a shared workspace key, Tier Two's modes are the only limit on what that key does, because approval never narrows a credential. Per-person sign-in keeps the destination's own permissions in play.
  • Sensitive reads. A read tool on Allow never reaches the reviewer, and 'read-only' says nothing about what the read returns. The read-only access article covers when a read deserves an Ask.

Then there's the reviewer. The preview is truncated, and a person approving forty requests a day stops reading them. Fewer Asks, on the tools where the arguments change the answer, with everything else on Allow or Block, produces better decisions than an approval queue that's long enough to skim.

Put the gate where the arguments are

Decide the gate's location before you decide which tools deserve one. Host rules are fine when the person at the keyboard is the right reviewer and there's one host to configure. The moment the reviewer is someone else, or the same rule has to hold across Claude Code, Cursor and whatever the next person installs, the gate belongs in the path, holding the actual call. Everything else about approval design follows from that placement.

Tier Two gives every tool on a connection one mode, and Ask holds the real call, with its arguments, for a workspace admin. Connect one system, set one write tool to Ask, and watch the request arrive; the Free plan covers three people and three connections.

Sign up for Tier Two