Tool rules
tool_deny and tool_allow match against every tool name in the request.
That is one list, and it deliberately does not distinguish where a tool came from,
because the wire does not either.
A Claude Code turn’s tools array holds its own tools (Bash, Read, Edit,
Task), the flattened tools of every local MCP server (mcp__github__create_pull_request),
and any provider-side built-in it has enabled (web_search). To the model they are
all just callable functions, so to policy they are all just names.
What a coding agent typically declares
Names are the agent’s to choose and do change between versions — read your own inventory rather than trusting a list in a document. As a starting point:
| Agent | Shape of its tool names |
|---|---|
| Claude Code | Bash, Read, Write, Edit, Glob, Grep, Task, WebFetch, WebSearch, Skill; MCP as mcp__<server>__<tool> |
| Codex CLI | apply_patch, local_shell, web_search; remote MCP as a separate {"type":"mcp"} entry |
| Cursor | MCP as mcp_<server>_<tool> |
Denying a tool
vulnetix ai-firewall policy guardrail no-shell \
--rule-type tool_deny --action flag --pattern 'Bash' \
--priority 100 --enable
At flag this records every session that offers shell execution. At block it
refuses those sessions outright. At strip it removes Bash from the array so
the model is never offered it, and the session continues without it.
A tool rule cannot stop a command that already ran.
Bash executes on the developer’s machine. The firewall sees the request that
carries the result back, not the execution. What a rule can do is stop the tool
being offered on this and every later turn, so the model cannot call it again —
and refuse a request whose history shows it was called, so the output never
reaches the model.
That is a real control. It is not the control people assume from the name, so it is worth being precise about which one you are buying.
Allowlisting tools
Any enabled tool_allow rule switches the whole tool family into allowlist mode:
every tool not matched by an allow rule is refused.
vulnetix ai-firewall policy guardrail read-only-agents \
--rule-type tool_allow --action block --pattern 'Read' --priority 100 --enable
vulnetix ai-firewall policy guardrail read-only-agents-grep \
--rule-type tool_allow --action block --pattern 'Grep' --priority 100 --enable
This is strict, and on agent traffic it is stricter than it looks: an agent whose
tool list gains one entry in a point release starts failing. Run it at flag
first and read what it would have refused.
Blocking a whole MCP server through its tools
A local stdio MCP server has no separate identity on the wire — its tools are already flattened into the array. The prefix is how you reach it:
vulnetix ai-firewall policy guardrail no-jira \
--rule-type tool_deny --action strip --pattern 'mcp__jira__*' \
--priority 100 --enable
That is a naming convention, not a protocol guarantee. See MCP servers for what it can and cannot promise.
Narrowing by verb
Tool names are usually verb-shaped, which makes a useful middle ground between “all of this server” and “one specific tool”:
# any destructive MCP tool, from any server
--rule-type tool_deny --action block --pattern '*__delete_*'
Read that as a heuristic. remove_issue and purge_cache do not match it, and
delete_draft does. It is a good tripwire and a poor boundary — which is exactly
why the shipped blueprint for it is set to flag.
Related
- The inventory — what your agents are actually declaring.
- Glob reference — the pattern syntax in full.
- Rolling out a rule — flag, then strip, then block.