Risk classes
Every tool call is mapped to a capability, such as email.send, and one risk class:
| Class | Meaning | Examples |
|---|---|---|
READ |
observes, changes nothing | read_file, web_search, mcp_gmail_get_message, ls |
LOCAL_WRITE |
changes local state | write_file, patch, memory add, skill_manage |
EXTERNAL_WRITE |
changes remote state | browser_click, mcp_*_create_*, git push, curl -X POST |
COMMUNICATION |
sends to people or channels | send_message, mcp_gmail_send_message |
DELETE |
destroys data | mcp_drive_delete_file, rm, git reset --hard |
PERMISSION_CHANGE |
alters who can access what | mcp_drive_share_file, chmod, sudo |
FINANCIAL |
commits money | mcp_shop_place_order, mcp_bank_transfer_funds |
EXECUTION |
runs arbitrary code | terminal (non-trivial), execute_code, cronjob create |
UNKNOWN |
no signal | never treated as safe |
How classification works
Section titled “How classification works”The first layer that matches wins:
- Explicit mapping.
policies/defaults.yamlmaps Hermes’ built-in tools. A mapping can depend on an argument:send_message(action="list")is READ, andprocess(action="kill")is EXECUTION;browser_consoleis READ, but with anexpressionit runs JavaScript in the page, so it is EXECUTION;patchin multi-file mode names its files inside the patch text. AgentsFence reads the*** Add/Update/Delete File:headers and both sides of*** Move File: a -> bwith Hermes’ own grammar (and Hermes’ patch parser when available), checks every path, and treatsDelete Fileas DELETE;- a tool mapped as a read that takes an
action,operationormethodargument is a read only for actions declared as reads.spotify_library(action="list")is READ;action="save"is EXTERNAL_WRITE andaction="remove"is DELETE. An undeclared action is classified by its verb, or as UNKNOWN.
- Glob patterns. For example,
kanban_*→ LOCAL_WRITE. - Verb inference for everything else, including every MCP tool (
mcp_<server>_<tool>):- the name is split into words, and the first verb decides:
get_orderis READ,place_orderis FINANCIAL; - strong verbs anywhere in the name escalate the class:
get_and_delete_recordis DELETE; - a service keyword names the capability domain:
gmail→email.*,drive→file.*,stripe→payment.*.
- the name is split into words, and the first verb decides:
The shell
Section titled “The shell”terminal commands are sub-classified:
| Command | Class |
|---|---|
ls, cat, grep, git status, pipes of these |
READ, unless an option makes them write or launch something (sort -o, tree -o, rg --pre, yq -i, find -fprintf, git diff --output, git -c …) |
git branch newname, git tag v1, git remote add, uniq in out |
LOCAL_WRITE |
rm, shred, git clean, git reset --hard, git branch -D, git tag -d, git stash drop |
DELETE |
curl -d/-F/-X POST, scp, ssh, rsync, git push |
EXTERNAL_WRITE (shell.network_write) |
sudo, chmod, chown |
PERMISSION_CHANGE |
anything else, or anything with ;, &&, $(…) or redirects that isn’t clearly worse |
EXECUTION |
any command prefixed with variables (LD_PRELOAD=…, LESSOPEN=…), or a program outside system directories (./ls) |
EXECUTION |
Compound commands are split, and the worst segment decides.
Checking a classification
Section titled “Checking a classification”agentsfence classify mcp_gmail_send_message '{"to": "John <john@example.com>", "bcc": "x@y.com"}'This prints the capability, the risk class, where the classification came from (mapped, pattern, inferred or unknown), and the extracted targets.
If a tool is misclassified, map it explicitly.