Tool kinds
Each tool is a read, a write (it changes something) or one that sends out (to an address, a URL, a person or a channel). The kind decides what the tool check asks of a call: a read with valid values runs; a write waits for a person's yes unless a rule allows it; a destination in a call to a tool that does not send out is refused.
Where a tool's kind comes from
The first of these that applies decides:
- Your file,
tool_kinds.yaml: a tool it names is the kind it says, always. - The tool's own marks: a catalogue that already says
read_only,destructiveoregress. - The inference, from the tool's name, description, arguments and HTTP method, with MCP annotations
(
readOnlyHint,destructiveHint) as one more signal. It makes a tool a read only where every signal agrees: a reading verb, no writing or sending verb anywhere, no destination argument, no annotation saying otherwise and no method that changes anything. A destination argument (to,url,email, aformat: uriorformat: email) or a sending verb makes a tool send out. English and Portuguese words are read. - Otherwise a write that sends nowhere. A tool nobody can tell costs a confirmation, never a silent run.
A wrong read would let a write run without anyone's yes; a wrong write only asks once more. So every doubt falls on the write side.
The file
# tool_kinds.yaml
infer: true # false: every tool not named here is a write
tools:
orders: # a name that does not say what it does
kind: write
source: you
reason: "it creates an order"
notify_customer:
kind: sends_out
source: you
reason: "it texts the customer"
source says who decided: you (a person), assistant (an AI assistant, through set_tool_kinds), catalogue,
annotation, inferred or unsure. Pass it as Guardrail(..., tool_kinds="tool_kinds.yaml"); infer_tool_kinds=
False switches the inference off as infer: false does.
Drafting it with stalwart mcp
writes_file drafts the file for your tools: each with its kind, the reason and where it came from, unsure tools
first, so you read those before the rest. The classify-tools prompt lets your own assistant propose kinds for the
unsure ones through set_tool_kinds; its changes are recorded as source: assistant and listed apart, for you to
check. Our server never calls a model.
What it cannot do
A tool whose name and description hide what it does may be classed wrongly. Mark it in the file. The file's digest and
whether the inference was on are part of every decision_id, and explain_decision says where a tool's kind came
from.