Work · 04Agent interface
An assistant that reaches a warehouse without being handed the keys to it.
This is the build with no client in it. It targets a named third-party warehouse-management API, and the architecture, tool surface, refusal codes and test count below can be judged on their own terms without taking anything on trust.
- Client
- None. A build with no client in it.
- Layer
- 4 of 4. Agent interface.
- Target
- Extensiv 3PL Warehouse Manager API.
- As of
- 2026-09-18, verified against a local mock, not a live tenant.
The problem is not connectivity. It is that agent retries, ambiguous names, stale previews and dropped connections all produce duplicate writes against a system of record — and a warehouse's system of record losing the truth is the failure that matters. Name the substitute first: a general-purpose MCP gateway already exposes thousands of apps and tens of thousands of actions through one connection, so any build whose value is "connect an assistant to a mainstream SaaS app" is dead on arrival. The paid work is in the tail — private systems, apps with no gateway coverage or no triggers, auth scoping, per-tenant permissioning, and deterministic behaviour under agent retry.
01The constraint
What was actually hard.
Making a write safe enough to let an assistant near it. The answer is that preparing and committing are separate tools, preparing writes nothing, and every check is evaluated again at commit time against live state — so a scope narrowed after the preview, or an order somebody edited in between, stops the write rather than overwriting it.
02The mechanism
How it works.
- Five packages, one adapter boundary. The core holds the domain model, the sixteen tools, scope policy, the mutation engine and the append-only stores, and knows no URL and no wire field. A single adapter package holds everything vendor-specific — OAuth2 client credentials, HAL envelopes, RQL filters, ETag concurrency, error translation. A second warehouse system reuses the core entirely.
- Sixteen tools: eleven read, four prepare, one writes. With writes off, the five write tools are not registered at all — they are absent from the tool list, so the assistant cannot call what it cannot see. Read-only is structural, not a setting that gets checked.
- Six gates at commit — replay, tenant and environment, expiry, scope, natural key, version — each re-evaluated against live state. A replayed commit is a no-op, never a second order.
- Four independent ways a retry stays harmless: the change id returns the stored outcome; the intent fingerprint hands back the same change rather than a second plan, and refuses a reused key on a different request; the natural key looks the reference number up upstream before any create and stops on a mismatch; the version token fails the commit if the record moved on.
- Separate webhook ingest process verifying each delivery's RSA-SHA256 signature against the published key, deduplicating and appending — separate because the vendor needs a public HTTPS endpoint answering within three seconds, while an MCP server is usually a local child process with no inbound network at all.
- Blast radius is capped: 200 lines and 10,000 units per mutation, prepared changes expire after fifteen minutes, and every call, refusal and commit lands in an append-only audit log recording who asked, what was previewed, what was sent and what came back.
- Fifteen structured error codes, so a failure is data the assistant can reason about rather than a protocol crash — including OUTCOME_UNKNOWN for a connection lost mid-write, which marks the change and forces a reference lookup before anything else. Never a blind retry.
03The figures
What it measured.
Each figure carries the date it was taken. A number without a date is not on this page.
| Figure | Value | As of |
|---|---|---|
| Packages, with one adapter boundary | 5 | 2026-09-18 |
| Tools | 16 — 11 read · 4 prepare · 1 commit | 2026-09-18 |
| Gates re-checked at commit | 6 | 2026-09-18 |
| Independent idempotency mechanisms | 4 | 2026-09-18 |
| Structured error codes | 15 | 2026-09-18 |
| Tests passing against the local mock | 324 · clean typecheck and build | 2026-09-18 |
Four things it deliberately will not do.
Confirm a shipment · confirm a receipt · release a hold · adjust inventory. Each is either irreversible, or needs someone who has physically seen the pallet, or is a deliberate stop signal only its author should lift. Asked to do one, the assistant reports the current state and says why it cannot. That is the intended answer, not a limitation to work around — writing stock counts from a chat window is how a system of record loses the truth.
Everything above was verified end to end against a local mock built wire-for-wire from public documentation, with fault injection and signed webhook emission, where every faked behaviour cites its source and every guess is flagged for re-verification. That is the strongest claim available without a tenant, and it is not the same claim as production verification. The repository carries a verification-status document listing exactly what remains open. A prospect is told this before they ask.
04Doing it again
What it would take to do again.
A fit
A system with no gateway coverage, or one where writes need per-tenant scoping and must stay safe under agent retry.
Not a fit
Connecting an assistant to a mainstream SaaS app a general-purpose gateway already covers. That is a configuration task, not a build.
Up against a constraint like this one?
Thirty minutes, no prep, no pitch. Bring the thing that is slow; if it is not a fit, I say so and point you somewhere useful.