Prerequisites
- You can create policy rules through
/v1/policies. - You know the tool’s name exactly. Matching is
fnmatchglob against the name, and two MCP instances exposing the same tool name are indistinguishable. - Read tool authorization for the layers this guide manipulates.
API=http://localhost:8000 and a bearer token in $TOKEN.Steps
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.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.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.Verify
Check the snapshot the task actually carries. This is the authoritative answer, because it is what every enforcement point reads: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:
Troubleshooting
The rule exists and nothing changed
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
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 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
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
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
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 .Related
Tool authorization
Every layer a call clears and why they are separate
Require human approval
The third verdict a tool decision can return
Set a budget
The other gates on the pre-tool phase