Skip to main content

Authorize a tool call

Do this when an agent must not call a particular tool, or must be restricted to a named set of tools. Do it as well when a tool call failed and you need to know which of the several enforcement layers stopped it. Do not do this to remove a tool from an agent entirely — that is composition, and un-equipping the agent is cheaper than denying it. And do not expect a rule to affect a task that is already running: the policy snapshot is frozen when the task is created. Start from the posture: tool policy is default-allow. An agent with no matching rules may call every tool it is composed with. Restriction is something you add.

Prerequisites

  • You can create policy rules through /v1/policies.
  • You know the tool’s name exactly. Matching is fnmatch glob against the name, and two MCP instances exposing the same tool name are indistinguishable.
  • Read tool authorization for the layers this guide manipulates.
Examples assume API=http://localhost:8000 and a bearer token in $TOKEN.

Steps

1. Choose the scope

A rule binds to one subject. The resolver merges the layers workspace → agent → user → task, and a lower layer can only tighten. The user layer resolves from whoever created the task, which is how the same agent produces different verdicts for different callers.

2. Choose deny or allowlist

Deny a named tool when the default set is right and one capability is not. This is the option to reach for most of the time.
Use an allowlist when the agent should be confined to a known set and you want new tools to be excluded by default as they are added.
Every allow rule at a layer contributes one entry to that layer’s allowlist. Once an allowlist is non-empty, anything outside it is denied. An absent or empty allowlist means “no allowlist in use”, not “deny everything”. Both effects require a named tool. tool:* is parsed as a valid target and then skipped by the compiler, so a wildcard allow or deny rule is stored and does nothing. Response is HTTP 201 with the created rule, including its id.

3. Preview the merged result

Before running anything, resolve the layers without creating a task:
The preview resolves the workspace, agent and calling user’s layers. Pass task_policy in the same body to see what a per-task document would do on top.

4. Tighten one task only

A per-task restriction rides on task creation rather than the rule table:

Verify

Check the snapshot the task actually carries. This is the authoritative answer, because it is what every enforcement point reads:
The response includes source_policy_ids, so you can confirm your rule is one of the inputs, and resolver_version (policy-resolver-v1). Confirm the denial at runtime. A denied call appears in the task event stream as a tool.result event whose data carries denied_by_policy: true:
A denied tool is also not offered to the model at all — disclosure drops it before the LLM call — so in a healthy configuration you should usually see the agent never attempting it rather than attempting and being refused.

Troubleshooting

The rule exists and nothing changed. Check the target kind. The compiler handles allow and deny only on a named tool: target. Rules targeting mcp:<id>, model:<id>, skill:<id>, collection:<id> or tool:* are accepted by the API, stored, returned by GET /v1/policies, and then skipped when the layer is compiled. They have no runtime effect. Confirm with the preview endpoint: if tools is absent or unchanged, the rule did not compile. 422 when creating or previewing. The resolver rejects a lower layer that would loosen a higher one. An agent-level allowlist has to be a subset of the workspace allowlist patterns; a task policy cannot re-enable a denied tool. The error names the field that tried to widen. The task still calls the tool. The snapshot is taken at task creation. A rule written after the task started does not reach it, and there is no revocation path into a running workflow. Cancel and restart the task. It works over the API but not through the MCP proxy, or the reverse. The proxy has no task snapshot, so it resolves only the workspace and calling user’s layers at request time. An agent-scoped rule therefore does not apply to a proxy call. Move the rule to the workspace or user layer if it must cover both paths. A tool the model should not see is still in its context. Disclosure keeps tools that require approval, and always keeps the control-flow tools (completion, task_complete, request_user_input, recall_history, read_tool_output, activate_tool_source, load_tools) regardless of policy, so a restrictive rule cannot strand a run. The call failed but not with a policy reason. Several other layers can stop a tool call independently of your rules: the budget gates deny when the run or service budget is exhausted, and a pattern gate denies destructive shell and SQL strings such as DROP TABLE, rm -rf / or TRUNCATE TABLE. Those surface as an activity failure rather than a denied_by_policy event. Read the worker log for the interceptor name in the raised GovernanceDenied.