Approval gates
Every tool you expose to an agent is a capability that will eventually fire without a human watching. This skill sorts them into what may run freely and what must stop for a person, and finds the ones currently on the wrong side of that line.
Read-only. Produces a policy proposal; never applies it.
Applies to
| Project types | MCP servers, agent tool surfaces, function-calling schemas, and APIs about to be exposed to an autonomous agent |
| Stage | Before an agent surface goes to production. Re-run whenever a write capability is added, since that is when a safe surface quietly stops being one |
| Needs | Tool definitions, and ideally the handler source — reading the implementation is where the real findings come from |
| Skip if | There is no agent-callable surface yet. This skill audits what is exposed, not whether something should be |
Particularly relevant if your tools can send, publish, delete, transfer, or spend. If every tool is read-only, expect a short report saying so.
How to use
/agentrel:approval-gates audit this repository's tool surface
/agentrel:approval-gates ./src/mcp audit a specific server directory
/agentrel:approval-gates ./tools.json audit a tool definition file
Produces approval-gates-report.md including a proposed permission policy as JSON, plus a
printed summary led by any critical findings. Read-only — the policy is proposed, never
applied.
The classification
Four tiers. Assign every tool to exactly one.
| Tier | Rule | Examples |
|---|---|---|
| auto | Read-only. Wrong output wastes tokens, nothing else. | search, list, get, describe |
| auto-bounded | Writes, but reversible and low blast radius. Safe within a stated limit. | create draft, add comment, upload to scratch |
| approve | Irreversible, externally visible, or spends money. | send, publish, pay, delete, transfer, deploy |
| forbid | No legitimate agent use. Should not be exposed at all. | rotate credentials, change permissions, disable audit logging |
The line that matters is approve. Ask one question of every tool:
If this fires wrongly at 3am with no human present, what is the cost and can it be undone?
Irreversible or externally visible means approve, regardless of how the tool is annotated
today. Cost is not only money — a message sent to a customer cannot be unsent.
1. Enumerate the surface
Find every callable tool.
| Source | Where |
|---|---|
| MCP server | ListTools handler, tools/ directory, .mcp.json, mcp.json |
| Tool definitions | tools.json, *.tool.ts, a tools array in an agent config |
| OpenAPI | Every operation, with its HTTP method as a first hint |
| Function calling | The schema array passed to the model |
Count them and record the count. A surface with 60+ tools has a selection problem as well as a safety problem, and it is worth saying so.
2. Classify each tool
For each tool, record:
| Field | Notes |
|---|---|
| Tool | Name |
| Declared | What annotations and description claim |
| Actual | What the implementation does — read the handler |
| Tier | auto / auto-bounded / approve / forbid |
| Gate | Present, absent, or partial |
| Finding | Only when declared and actual disagree, or the gate is missing |
Read the implementation, not just the schema. The whole value of this skill is catching
the gap between what a tool says and what it does. A tool named update_draft that also
sends on publish is the exact failure this exists to find.
Where the implementation is unreachable, mark the row declared-only and say the
classification is unverified. Do not guess at behaviour you could not read.
3. Check annotation honesty
MCP tools may carry destructive, idempotent, readOnly, and openWorld annotations.
Agents use these to decide what to run unsupervised, which makes a wrong annotation more
dangerous than a missing one.
| Finding | Severity |
|---|---|
destructive: false on a tool that deletes, sends, or charges |
critical |
readOnly: true on a tool that writes |
critical |
idempotent: true on a tool that duplicates on retry |
high |
| Annotations absent on a write tool | medium |
| Description omits a side effect the handler performs | high |
| Description omits cost or rate consequences | medium |
A false reassurance is worse than silence. Rank a wrong annotation above a missing one.
4. Check the gate mechanism
A tier is a decision; a gate is an implementation. Both must exist.
- Is there any approval mechanism, or only classification in prose?
- Is it enforced server-side, or does it trust the client to ask?
- Are spending or rate limits enforceable per-agent, not just per-account?
- Is every gated call logged with the approving principal and the arguments?
- What happens on approval timeout — does it fail closed?
Client-side-only enforcement is a finding. An agent that can call the tool directly can skip a gate that lives in the prompt.
5. Write the report and policy
Write approval-gates-report.md:
- Summary — tool count, tier distribution, critical findings
- Classification table — every tool with declared, actual, tier, gate
- Findings — ordered by severity, each with the fix
- Proposed policy — the machine-readable block below
- Method — what was read, what was declared-only
Include a policy the team can adopt:
{
"auto": ["search_invoices", "get_customer", "list_products"],
"auto_bounded": [
{ "tool": "create_invoice", "limit": "draft only, max 10/hour" }
],
"approve": ["send_invoice", "refund", "delete_customer"],
"forbid": ["rotate_api_key"]
}
Then print:
APPROVAL GATES
Tools found 23
auto 14
auto-bounded 4
approve 4 2 currently ungated
forbid 1 currently exposed
CRITICAL refund destructive: false, issues real refunds
CRITICAL rotate_api_key no agent use case, exposed to all callers
HIGH send_invoice no gate; description omits that it emails
Report → ./approval-gates-report.md
6. Close
Lead with the critical findings and nothing else. If there are none, say that directly — a surface with correct annotations and enforced gates is a real result and should read as one, not be padded with speculative concerns.
Notes
- Read-only. Propose the policy; never write it into the project.
- Absence of MCP is not a finding here. This skill audits what is exposed, not whether something should be.
- A tool in
forbidthat is currently exposed is always the first line of the report, ahead of everything else.