An agent’s call often doesn’t carry the facts a rule needs. “Submit invoice ACC-PINV-0042” names a record; “close deal 1187” doesn’t say whether there’s a signed MSA; an item’s group is filled in by the app on save. The guard reads those facts from the app, through the app’s own read tools, before the check. All of it is mapping data: proposed by Claros, editable by the expert, no app-specific code.

How it runs

  1. The guard sends the call to /v1/check.
  2. If the tool’s mapping needs a record, Claros answers {"allowed": false, "need": [{"key", "tool", "arguments"}]} without logging.
  3. The guard calls those read tools on the wrapped server itself. The agent never sees these calls.
  4. It asks again with fetched: {key: record}. The final check is logged with the records it read (resolved: true).
A failed or missing read blocks the write (or forwards it with --fail-open). A need naming a tool the guard doesn’t consider a read is refused.

Fetch: the record the call targets

$path is an argument of the call. The record is merged under the call’s own values at into. Values in a fetched record win over the agent’s: the app keeps what it has.

Derived: values the app computes

Fields, numbers, + - * / and parentheses. No eval.

Lookups: master data

$ is the key’s value; one read per distinct value, cached in the guard for 60 s. pick + var feed a fact to precise rules; context fields go only to the judge (as item_code.item_name).

Query lookups: history

How many refunds has this customer had this quarter? Is this a supplier’s first invoice? A query lookup reads rows and reduces them to one value:
op is count, first, last, min or max (of field). where is applied by the guard too, for servers that ignore their own filters. Only the value goes back to Claros, never the rows. The result goes to the judge as context under as, or to a fact under var. Validated on the server: a read tool of the same app (or of another server, below), $ substitution only, at most 4 levels and 50 leaves.

Lookups on another server

A rule can need a fact the app being called doesn’t hold: a refund tool’s call carries the amount, but the order total is in the shop. Add "app" to a lookup to use a read tool of another guarded server in the same workspace:
The guard of the server being called asks the guard of shop (both run on the same machine, started by your agent clients) over a local socket readable only by you; that guard runs the read tool on its server. It answers only guards that use the same Claros key, and only with tools it considers reads. If no guard of shop is running, the read fails and the write is blocked. The server checks that order_read is a registered read tool of shop in your workspace, and the mapper is shown other servers’ read tools when it proposes a mapping.

Limits

  • The record is read just before the write, not locked: it can change in between.
  • Claros doesn’t verify that a fetched record is the one referenced.
  • A server whose list tool ignores filters makes a query lookup read every row (fine at hundreds, a ceiling at scale).