Governance
Two operator-controlled mechanisms sit between yourtools/call and the tool’s
handler. Neither is configurable through the API, both are invisible in
tools/list, and both are common explanations for “it works for my colleague in
the app but not for my connector”:
- Organization AI guardrails — policies that can refuse a call outright or force it through the approval gate.
- The headless autonomy dial — a per-key / per-facility / fleet-wide switch that can force every write through the approval gate.
A. Organization AI guardrails
Guardrails are the sameblock / require_approval / warn policies the
in-app assistant enforces, evaluated on the headless surface too. An
organization defines them; a connector inherits them.
Policies are inherited down the org tree — a guardrail set on a parent
organization applies to every facility beneath it. The resolved list is sorted
by priority descending (ties broken by policy id descending) and the first
matching policy wins. Ordering between kinds is therefore a priority
question, not a “block beats warn” rule.
What each kind looks like to a caller
block
The call is refused with JSON-RPC -32003 (FORBIDDEN). The message is the
policy’s own reason text, and error.data.guardrail carries the policy
name:
message falls back to
Blocked by guardrail "<policy name>". Either way, the presence of
data.guardrail is the reliable signal — branch on that, not on the prose.
require_approval
There is no error. Your call returns the ordinary
status: "pending_approval" result with an approvalId, even for a tool whose
risk level would normally auto-execute, and regardless of the
autonomy dial:
low-risk write that used to
execute inline starts pausing, a require_approval guardrail (or the autonomy
dial) is the reason. Poll get_approval_status with the approvalId — it needs
general:read.
warn
Nothing observable. The call proceeds exactly as it would have; the match is
logged server-side with the tool name and policy name. Do not build detection
logic around warn — there is no field, header, or result flag for it.
Which predicates can match a headless call
A policy carries exactly one predicate. Two of the three can match over MCP:
Two more mechanics worth knowing:
- A
risk_levelpredicate needs the call to have a risk level. Registry tools and connected-app tools always carry one (reads arelow), so amin: "low"policy matches essentially everything on that path. - An unrecognized predicate type is logged and skipped, not treated as a match — a forward-compatible policy row from a newer version cannot accidentally block you.
Which calls are evaluated
Guarantees you can rely on
- Tighten-only. A guardrail can block a call or force approval on one that would have auto-executed. There is no policy kind that lowers a risk default or removes an approval requirement.
- Resolution fails open. If the guardrail lookup itself errors, the resolver returns an empty policy list and the call proceeds through the rest of the stack. Guardrails are a policy layer on top of scopes, RBAC, suite entitlements, and the tenant-boundary guard — those gate independently and do not fail open.
- Evaluated before the idempotency reservation. A guardrail-blocked call
never consumes your
Idempotency-Key: the reservation is taken after the guardrail verdict on both the verb path and the registry path. You can fix the policy and retry with the same key. See Idempotency.
B. The headless autonomy dial
This is not the agent-definition autonomy described in
Agent Runs. That setting belongs to a
configured agent and governs its run loop. The dial on this page belongs to a
headless credential and governs MCP
tools/call dispatch. They share the
level names, which is the confusing part; they are separate settings resolved
from separate sources.Levels
auto_execute · notify_after · require_approval
Only require_approval changes dispatch: it forces the approval gate. The other
two add no restriction of their own — they leave the decision to the tool’s risk
level and the org’s rules.
Resolution order
Most specific first; the first source that yields a valid level wins:
The two maps are JSON objects of the form
{ "<id>": "<level>" }. Malformed
JSON, or a value that is not one of the three levels, is dropped and resolution
falls through to the next source.
auto_execute at step 5 is deliberate: tightening is opt-in, so operators who
never configured the dial keep the behavior MCP had before it existed. “Unset”
is permissive here, not ungoverned — org AI rules and the risk floor still apply.
Tighten-only, and what it does not do
When the dial resolves torequire_approval:
- Every write tool goes through the approval gate, even one whose risk level
would auto-execute. A
low-risk write likecreate_crm_notereturnspending_approval. - Reads are untouched. The registry path applies the dial only to non-read-only tools; read tools never enter the approval path at all.
- It never relaxes anything. It cannot lower the org AI-rule decision, and
it cannot lift the hard risk floor that keeps
highandcriticalactions atrequire_approvalon every surface. It only ever adds the gate.
require_approval guardrail by OR — either one forcing the
gate is enough.
Troubleshooting: all my writes return pending_approval
If every write comes back as pending_approval — including low-risk ones
that have no business pausing — the cause is almost always one of:
- the headless autonomy dial resolving to
require_approvalfor your key, your facility, or the whole fleet, or - a
require_approvalguardrail with a broad predicate (arisk_levelpolicy withmin: "low", or atool_namelist covering your writes).
status: "pending_approval" as a first-class
outcome and poll get_approval_status; a credential holding agents:approve
can also resolve its own pending actions with listPendingApprovals /
approveAction / rejectAction.
If only some writes pause, this dial is probably not the cause — look at the
tool’s risk level instead.
See also
- MCP — the risk → autonomy default table and the full approval loop.
- Model tier gate — the other fail-closed operator
control, and the one that returns
-32003withrequiredTier. - Tenant isolation — the facility-boundary guard that runs just before guardrails on the registry path.
- Idempotency — why a blocked call leaves your key reusable.

