Rolling out a rule
The failure mode of a capability rule is worse than the content equivalent. A content rule that misfires blocks one message. A tool rule that misfires takes a capability away from an agent in the middle of a task, and the developer sees the model apologise for not being able to read a file.
Use the ladder. It starts one rung earlier than the content ladder, because with capabilities you begin without even knowing the names.
Rung 0 — read the inventory
Do nothing for a fortnight. Let the inventory fill in.
vulnetix ai-firewall inventory
vulnetix ai-firewall inventory --kind mcp_server
You are looking for the answer to two questions: what do our agents carry, and is any of it a surprise. Almost every organisation finds at least one thing it did not know about, and writing rules before you have that list means writing them against what you assumed.
Rung 1 — flag
The rule runs. Matches are recorded. Nothing is refused, nothing is removed.
vulnetix ai-firewall policy guardrail no-jira \
--rule-type tool_deny --action flag --pattern 'mcp__jira__*' \
--priority 100 --enable
Signal with zero risk. Watch which sessions it would have affected and, more importantly, whose. A rule that would have hit one contractor is a conversation; a rule that would have hit half of engineering is a policy decision somebody senior needs to make.
Rung 2 — strip
The capability is removed from the request. The session continues without it.
vulnetix ai-firewall policy guardrail no-jira \
--rule-type tool_deny --action strip --pattern 'mcp__jira__*' \
--priority 100 --enable
This rung exists because it is almost always what people actually want. The model
is never offered the tool, so it never calls it, and the developer keeps working —
they simply have an agent that cannot file Jira tickets. Compare block, which
makes the whole session unusable until the tool is removed from their config.
Strip has one guard, and it is worth understanding.
If the tool being stripped also appears in the conversation history as having
already been called, removing the declaration would leave a tool_use block with
no matching tool definition — and the provider rejects that with a 400 in the
middle of a live session.
So the engine escalates to a block in that case rather than performing the
strip. The caller gets a readable 403 naming the rule instead of an opaque
provider error.
In practice this means: a strip rule applies cleanly to a fresh session, and refuses a session that has already used the tool. That is the correct behaviour and it will look surprising the first time you see it, which is why it is documented here rather than in a changelog.
Rung 3 — block
The request is refused with a 403.
vulnetix ai-firewall policy guardrail no-jira \
--rule-type tool_deny --action block --pattern 'mcp__jira__*' \
--priority 100 --enable
Reach for this when merely not offering the capability is not enough — when you need the developer to notice and change their configuration, or when the capability’s presence is itself the thing you are refusing.
Allowlists: a special warning
Everything above is about deny rules. An allow rule is a bigger step than it looks, because one of them switches the entire family into allowlist mode.
# This does NOT mean "also allow Read".
# It means "allow ONLY Read, refuse Bash, Edit, Glob, Grep, Task and everything else".
--rule-type tool_allow --action block --pattern 'Read'
Run any allowlist at flag first, for longer than you think you need. Agent tool
lists change between point releases, so a list that was complete last month may
not be this month.
Related
- The inventory — rung zero.
- Content rule rollout — the same discipline for guardrails.
- Limitations — what none of these rungs achieve.