Skip to content

Build1 publisher3 min readPublished

A risk tier on every tool moves the refusal out of the model's text and into the runtime gate

An R0-R5 tiering in the open-source KeelBase server lets the declared level decide whether an agent's call executes, waits for a person, or is refused, and a tool that declares nothing has its writes held at confirmation.

The Engineer · Build desk

Illustration accompanying A risk tier on every tool moves the refusal out of the model's text and into the runtime gate

What happened

  • A dev.to post proposes tagging every agent tool with a risk level from R0 to R5 and letting that level, not a blanket setting, decide whether the call executes, waits for a human, or is refused.
  • The bands are fixed in the runtime: R0 through R2 run automatically, R3 and R4 return a confirmation marker and execute only on approval, and R5 is blocked at the policy gate before the side effect.
  • A tool that declares no risk level has one derived from semantics, with writes held at R3 confirmation and reads allowed at R1, so an unlabelled write is not callable unattended.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • decision Adopting the scheme means labelling an existing tool inventory by hand, because the semantic fallback can only sort handlers it can recognise as reads or writes.
  • constraint Once the block lives in the gate's strategy table, an agent that has been argued into a delete still gets a policy refusal, and the argument never reaches the write path.
  • cost Revocability has to be implemented per write path, and for an email already sent to the wrong customer the compensation is not a rollback, so that class of error still costs someone.
  • exposure Because the tier ships inside the MCP tool declaration, any client that can list tools can also enumerate which operations are blocked and which ones only need a confirmation click.

The gate sits after the permission check and before the write. A call arrives, the runtime resolves row-level scope (own or org), then reads the tier: R0 through R2 execute, R3 and R4 come back with a confirmation marker and wait for approval, R5 is refused with a response saying the operation was blocked by security policy [5][7]. Six declared tiers produce four behaviours at that gate, and three of the six share one of them [15]. The extra resolution is there for setting policy across a whole inventory.

The default is the part I would copy first. A tool that declares no riskLevel is not waved through; the runtime derives a tier from semantics, sending writes to R3 confirmation and reads to R1 automatic [3]. That inverts the failure the post describes, where a write sits in the inventory with no execution policy and runs because it is callable [14]. It holds only as long as the runtime can tell a read from a write, and the post does not describe how that classification is made. A handler whose name reads like a query and whose body writes a row is the case the derivation cannot catch.

The conformance number is a claim about the project's own vectors. The script reports 34 of 34 checks passing in zero seconds, including R1 to auto, R3 to confirmation, R4 to human_approval, R5 to block, and both derivation defaults [10]. Tampering, tier escalation and confirmation bypass are rejected in the same run [11]. For the pass count to mean anything in another deployment, the tiers on that system's tools have to match its actual exposure. What the suite tests is the strategy table.

The governance layer can override a single tool's enablement and confirmation requirement at runtime, with immediate effect [4]. Those are the two levers that decide whether a business-sensitive write waits for a person. An operator who clears the confirmation requirement on an R3 tool has made it behave like R1 without editing the declaration, and the declaration is what outside callers read, because riskLevel is surfaced in the MCP tool declaration under _meta.keelbase before anything is invoked [6].

Recorded side effects are revocable, including cross-system compensation, though compensating for a write is not the same as undoing it [9]. The post's own list of bad writes runs from a wrong order amount to an overwritten customer record to an email sent to the wrong person, much of it irreversible or expensive to reverse [12].

The test the post proposes for an existing system is to stop a high-risk operation and see where the refusal came from, the model's text or the execution layer [18]. A refusal from the layer is checkable afterwards, because an authorization denial lands on the audit hash chain with isError=true and the reason list attached [8]. If the answer is that the model refused, the post said, the boundary is still sitting in the prompt and off the execution path [13].

What to watch

  • Whether the runtime override can lift an R5 block as well as a confirmation requirement, which decides if irreversible operations are forbidden or merely defaulted off.
  • Whether other MCP servers adopt a risk field in the tool declaration, letting clients read a tier before they call.
  • Whether the derivation gains a way to classify handlers that read by name and write internally, the gap the semantic default cannot cover.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories