OrgRelay

Write tools and approvals

A write tool is any tool that changes data. OrgRelay never lets the AI run one directly: it creates an approval request, a person decides, and only then does your connector get the call, once, with a key that makes retries harmless.

The business side of approvals, who approves and what an owner sees on their phone, is described on orgrelay.com's approvals page. This page is what your connector has to do.

The flow

  1. A staff member asks for something that needs a write, for example "cancel order SO-1002".
  2. The AI picks your tool orders.update_status with arguments. Because its access is write, OrgRelay does not call you. It creates an approval request: which system, which tool, which arguments, who asked.
  3. The owner or a designated approver approves or rejects it in OrgRelay's web app or mobile app.
  4. On approval, OrgRelay re-checks the requester's permissions as they are now. If their access was revoked in the meantime, the approval fails and you are never called.
  5. OrgRelay calls your tool with the approval id and an idempotency key.
  6. Your result, success or error, is written onto the approval request and shown to the requester.
What OrgRelay sends MCP REST
Idempotency key _meta["orgrelay/idempotency_key"] Idempotency-Key header
Approval id _meta["orgrelay/approval_id"] X-OrgRelay-Approval-Id header
Who asked _meta["orgrelay/actor"] X-OrgRelay-Actor header

The key is always orgrelay-approval-<approval id>. Transports are described on MCP and REST.

What your connector must implement

1. Mark every data-changing tool as write

In the manifest, access: "write". Over MCP, readOnlyHint: false. This includes things that look harmless: adding a note to a customer, sending an email, creating a draft. If in doubt, mark it write.

2. Execute once per idempotency key

Repeated calls with the same key within 24 hours must execute once and return the first result.

Node.js, simplified from the template
const done = new Map(); // `${tenant}|${key}` -> { at, result }

function withIdempotency(tenant, key, args, fn) {
  if (!key) return fn();
  const k = `${tenant}|${key}`;
  const prev = done.get(k);
  if (prev) {
    if (JSON.stringify(prev.args) !== JSON.stringify(args)) throw new ToolError('idempotency_conflict', 'Key reused with different arguments');
    return prev.result;
  }
  const result = fn();
  done.set(k, { at: Date.now(), args, result });
  return result;
}

In production, store keys in your database alongside the change, in the same transaction, and expire them after 24 hours. The in-memory map in the templates is only for demonstration.

3. Re-check on your side

Never skip a check because OrgRelay already approved. Between approval and execution:

  • The record may have changed. An order approved for cancellation may have shipped. Return conflict with a message a person understands.
  • Stock may have moved. Validate quantities again.
  • Your own permission model may be stricter than the token's scope. Return forbidden.

4. Return a useful result

Write tools return at least ok, id and message. The message is shown to the requester and stored on the approval, so write it for a person: "SO-1002 cancelled, 3 items restocked", not "OK".

5. Log who approved

Log orgrelay/actor and the approval id with the change. Your system's audit trail then shows that Lin Wei asked and the owner approved, not just "API user".

Sandbox mode

When an owner adds a connection with Developer mode (sandbox), AI and staff cannot use it. The owner can call any tool by hand, including write tools, without an approval, and watch the call log. Point sandbox connections only at a test system. The quickstart walks through it.

Testing idempotency

The self-test tool with --write calls a write tool twice with one key and checks that the second call returns the first result. Run it only against test data.

Terminal
node bin/orgrelay-connector-check.mjs https://erp-test.example/mcp \
  --token <test-token> --write --sample 'inventory.adjust={"sku":"TEST-1","delta":1,"reason":"selftest"}'

Why this design

Approvals in agent systems are often criticised as rubber-stamping. OrgRelay's answer is to make each approval specific (one tool, explicit arguments, the named requester), to re-check permissions at execution time, and to make the execution itself safe to retry. The wider design discussion is on human-in-the-loop for MCP.

Common questions

Can a write ever skip approval? Not for staff or the AI. Only sandbox connections, by the owner, by hand.

Same key, different arguments? Reject with idempotency_conflict.

Trust OrgRelay's permission check? No. Re-check permissions and state.

Next

Updated (Singapore time) · OrgRelay Team