Why the rules live outside the model
A system prompt that says “never drop a table” is a request, not a rule. The model can misread it, a prompt injection can talk it out of it, and nothing records whether it was followed. A tool-call policy moves the decision to code that runs between the agent and the tool. The agent still chooses what to ask for; the policy decides what actually runs.
OWASP’s Top 10 for LLM applications describes this risk as “Excessive Agency”: the vulnerability that enables damaging actions in response to unexpected, ambiguous or manipulated model output. It names three usual causes: excessive functionality, excessive permissions and excessive autonomy.4 Narrow credentials shrink what a tool can do at all. A policy narrows it further, call by call.
What a rule can read
The useful rules read facts about the call, not the agent’s description of it:
- The tool and its server, such as
postgres.executeorstripe.refunds.create. - The arguments. For SQL that means the kind of statement, the tables it names, whether an
UPDATEorDELETEhas aWHEREclause, and whether several statements are packed into one call. - Who is asking: which agent, on whose behalf, in which session.
- Context: the time, and how many calls or how much money the agent has used in the last minute, hour or day.
Tool descriptions and annotations aren’t facts in this sense. The MCP specification says clients must treat tool annotations, such as a hint that a tool only reads, as untrusted unless they come from trusted servers.1
The three decisions
Most policy engines settle on three outcomes. Allow runs the call. Deny refuses it and returns a reason the agent can act on, such as “send one statement per call”. Hold sends it to a person first; see human-in-the-loop approval. What happens when no rule matches matters as much as the rules: a default of deny means a new tool does nothing until someone allows it. Deedglass starts new orgs on a default-deny policy (source 5).
Writing rules that hold up
- Fail closed. If the engine can’t parse the arguments, or the SQL is ambiguous, refuse the call instead of guessing.
- Test rules like code. Keep cases that must be allowed and cases that must be refused next to the policy, and run them in CI. Each Deedglass starter policy carries its own test cases, which run in CI.3
- Try before you enforce. Run a new version alongside the current one, record what it would have decided, then switch.
- Record every decision, with the rule and the policy version that made it.
What a policy can’t do
A policy decides on the request. It can’t see what the system did afterwards: a trigger that changed 300 more rows, or a tool that reported success and failed. And a call that is within policy is allowed even if the agent was tricked into making it. Rules, budgets and approvals narrow that risk; they don’t remove it.