If your AI agent gets this wrong, can you undo it?
Agent permission systems ask which tools an agent may use, or how confident the model is. Neither answers the question a person actually cares about. This project defines five Reversibility Classes, R0 to R4, that describe how recoverable a single tool call is. It also publishes an open, evidence-linked registry of real MCP servers' tools classified on that scale, and a small reference classifier you can drop into a gateway.
| Class | Name | Test | Default policy |
|---|---|---|---|
| R0 | No effect | Nothing changes outside the agent's working context | Act |
| R1 | Restorable | Fully restorable with ordinary effort; nobody outside saw it | Act, then notify |
| R2 | Compensable | Can't be restored, but a known fix exists at bounded cost | Approve, or act within a budget |
| R3 | Hard to recover | Needs a human or out-of-band recovery; changes access, credentials, availability or physical state | Approve each time |
| R4 | Irreversible | A disclosure, unrecoverable destruction, or a consequence that can't be recalled | Never autonomous |
Charging a customer $40,000 by mistake is expensive but compensable: refund the charge and the customer is made whole, at a known cost. R2. A one-line message to the wrong Slack channel is cheap but permanent: R4. Severity scores get these backwards; reversibility doesn't.
- MCP's tool hints are too coarse.
readOnlyHintanddestructiveHintcan't say "compensable" or "this is a disclosure", and the MCP spec tells clients to treat them as untrusted anyway. This spec maps them conservatively (section 7) and lets an independent registry say more. - Class is a fact about the action type, not a model's opinion. A gateway can enforce it with no model in the decision path.
- Arguments matter. GitHub's
create_repositoryis R1 when the repository is private, and R4 when it's public. Records can say so with narrow, allow-listed rules. - Unknown means R4. Unclassified tools fail closed.
- Audit-ready. Logging the class and the rule behind every decision answers "which irreversible actions did this agent take, and who approved them?"
Read the full specification: SPEC.md (v0.1, draft for comment).
Browse the whole registry: reversibility.twomuchlabs.com.
20 servers, 536 tools classified. Counts are base classes; some tools drop to a lower class for specific arguments.
Every record links to the exact README or source file the tool names were verified against. Every class carries a one-sentence rationale. Entries start as draft and become reviewed or verified as people check them; see CONTRIBUTING.md.
A few classifications worth arguing about:
filesystem/write_fileis R4: it overwrites without keeping the old content.edit_fileis R1, because it returns a diff you can invert.slack/slack_add_reactionis R4: other people see it, and the server can't remove it.postgres/queryis R4 unless the query is a singleSELECT. The server wraps every query in a read-only transaction, but a query that starts withCOMMIT;escapes it (reported by Datadog Security Labs in 2025).- Every Playwright tool that accepts a
filenameis R4 when one is given: an explicit filename is resolved against your workspace root, so it can overwrite your own files.
The registry has had one independent audit; the fixes are applied, and the open questions it raised are waiting for opinions. Disagree with a class? Open a classification dispute. That is the point.
pip install git+https://github.com/TwoMuchLabs/reversibility-classes
rclass lookup sqlite write_query --args '{"query": "DROP TABLE users"}'
# R4 Irreversible (registry)
# Accepts any non-SELECT statement: UPDATE, DELETE and DROP destroy prior values, and even INSERT can be an upsert (INSERT OR REPLACE, ON CONFLICT DO UPDATE) that overwrites rows.
# default policy: never_autonomousfrom rclass import Session, Decision
session = Session() # bundled open registry
c, decision = session.check("git", "git_commit", {"repo_path": ".", "message": "wip"})
print(c.rclass, decision) # R1 Decision.ALLOW_AND_NOTIFY
session.record(c, decision)examples/gateway_hook.py shows how to gate MCP tools/call requests in a proxy in about 40 lines.
Your environment can be safer than the defaults. If you snapshot a database nightly, publish a deployment override that lowers DROP TABLE from R4 to R3, with the reason logged on every decision. Overrides can never lower a disclosure.
v0.1 is a draft for comment. Open questions for v0.2 include a detection window (how long a mistake usually goes unnoticed), pattern rules for sequences of actions that are harmless alone but not together, and whether a reversibility hint belongs in MCP itself. See SPEC section 12.
Specification, schema and registry: CC BY 4.0. Code: Apache 2.0. To cite, see CITATION.cff.
A Two Much Labs project. Maintained by Deepak Waghmare. Written in a personal capacity.