# Approval Gates

> Classify every tool an AI agent can call as safe to run automatically or requiring human approval, and find the destructive operations that currently have no gate. Checks whether MCP tool annotations match real behaviour, flags tools whose description understates what they do, and produces a permission policy you can adopt. Works on an MCP server, a tool definition file, an OpenAPI spec, or agent tool-handler source. Use when asked whether an agent surface is safe, to review agent permissions or tool authorization, to add human-in-the-loop boundaries, or before letting an agent act on production systems.

- Skill: `agentrel/approval-gates` (Agent Skill)
- Install (CLI): `npx skillmds@latest add agentrel/approval-gates`
- Raw SKILL.md: https://api.skillmd.com/api/skills/agentrel/approval-gates/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: agentrel (https://skillmd.com/u/agentrel)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/agentrel/approval-gates

---


# 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`:

1. **Summary** — tool count, tier distribution, critical findings
2. **Classification table** — every tool with declared, actual, tier, gate
3. **Findings** — ordered by severity, each with the fix
4. **Proposed policy** — the machine-readable block below
5. **Method** — what was read, what was declared-only

Include a policy the team can adopt:

```json
{
  "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 `forbid` that is currently exposed is always the first line of the report,
  ahead of everything else.

