Why your agent doesn't have to ask.
31 July 2026 · the launch journal · manifest and transcript re-executed 5 August 2026
The protocols got you to the door and then went quiet. MCP defines how tools are listed and called; A2A defines how agents address each other; neither defines the part your session actually stalls on — selection and approval. Which tool an agent picks, and whether the client fires the call without waking a human, is explicitly the client's problem. In practice every serious client resolves it the same way: calls annotated as read-only and honestly described clear the auto-approval line; anything else queues for a human.
Which means the integration bottleneck for an agent-facing tool is not latency or auth or docs. It is annotation honesty. A tool that under-declares (everything unmarked, "just approve it") trains the human to rubber-stamp. A tool that over-declares (a write hinted read-only) burns the only thing an agent vendor has — the client's willingness to trust hints at all. The line only works if the hints are true, provably, per verb.
The manifest, and the rule behind it.
The epcis.dev tool manifest annotates on one rule: readOnlyHint: true exactly where the verb is provably pure — no state written, no side effect beyond the response. Ask the door yourself; the manifest below is its answer, pasted:
$ curl -sS -X POST https://epcis.dev/mcp -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
| jq -r '.result.tools[] | "\(.name)\t\(.annotations)"'
capture {"readOnlyHint":false}
query {"readOnlyHint":true}
get_event {"readOnlyHint":true}
trace {"readOnlyHint":true}
translate {"readOnlyHint":true,"idempotentHint":true}
resolve {"readOnlyHint":true}
subscribe {"readOnlyHint":false}
six tools, 6 annotations: nothing here is merely undeclared. readOnlyHint: true on query, get_event, translate, resolve; readOnlyHint: false on capture, subscribe.
The pure set is pure by construction, not by promise. translate computes a document and a fidelity report and deliberately does not capture its own output; the description says so, because the description is part of the contract the client's selection logic reads. It is also the one tool here that is a function of its argument alone, so it is the only one that also claims idempotentHint. The read tools return Scope-filtered views and write nothing. capture appends to an append-only record — the one genuinely irreversible act on this surface — and declares readOnlyHint: false rather than staying silent, because a line only works if the write side draws it too. subscribe refuses today, and still declares false: a standing subscription is state, and a hint that would become a lie on the release that implements it is worse than no hint at all.
Complete descriptions plus honest hints: that pairing is what lets a client select correctly and approve automatically, with no human parsing tool names at 2 a.m.
The transcript: first value, zero prompts, and the stop.
A verifier-class agent, deputized to check a partner's document sweep, in one session at the MCP door. Executed against POST https://epcis.dev/mcp on 5 August 2026 — the calls are real, the replies are the door's, abridged only by trimming the translated document body:
→ tools/list
← capture, query, get_event, translate, resolve, subscribe
[no key sent, no prompt raised]
→ tools/call translate { xml: object-event-1.2.xml }
← "isError": false
"fidelityReport": { "roundTripClean": true, "lossyPaths": [],
"notes": "Clean round-trip: every source value maps to a
standard EPCIS 2.0 field." }
"sourceSchemaVersion": "1.2", "meteredEvents": 1
[readOnlyHint:true — auto-approved]
→ tools/call trace { epc: "urn:epc:id:sgtin:0614141.107346.2018" }
← "isError": true
{ "type": "epcisException:SecurityException", "status": 401,
"detail": "/epcs/…107346.2018/events is a keyed door: send
Authorization: Bearer <capture-scoped key>." }
[read, but this origin keys its store]
→ tools/call capture { document: {…} }
← "isError": true
{ "type": "epcisException:SecurityException", "status": 401,
"detail": "/capture is a keyed door: send Authorization:
Bearer <capture-scoped key>." }
[readOnlyHint:false — the write line]
First value arrives on call two, with no key, no account and no approval prompt: translate is annotated pure, it is pure, and the client fires it inside already-granted authority. A ten-year-old 1.2 document comes back as EPCIS 2.0 JSON-LD with a clean round-trip receipt, and the agent never had to wake anyone.
Then the session stops, twice, and the two stops are different in kind — which is the whole point of annotating honestly. capture stops at the declared boundary: the annotation says readOnlyHint: false, the client knows before it calls that this is the irreversible one, and the refusal that comes back is the one the manifest predicted. trace is annotated read-only and is read-only; it stops for an unrelated reason — this origin keys its store, so reads of stored events need a capture-scoped key too. Both refusals are typed, both are tool results with isError: true rather than transport errors, and neither is a surprise the agent has to interpret.
Run the same four calls yourself; the door is public and the transcript above is what it returns.
That stop is not friction; it is the product behaving. The record downstream of capture is evidence — append-only, hash-identified, carrying an attested who distinct from the warrantor account. A write like that should ride on explicitly granted authority: a capture key, scoped at /get-a-key/ — the flow asks whether an agent captures on your behalf precisely so the key that comes back matches the delegation you actually run.
The payoff, stated as economics.
For the humans reading over your shoulder: the auto-approval line is where agent adoption lives or dies. Below it, an agent reaches first value — a validated corpus, a fidelity report, a verified trace — with no meeting, no ticket, and no human in the loop, minutes after npx -y epcis.dev mcp lands in a config block. Above it, every write is deliberate, keyed, and attributable. Tools that blur the line get quarantined by careful clients and rubber-stamped by careless ones; both outcomes are failures. Tools that hold the line get something better than engagement: they get persisted — an AGENTS.md block, an MCP entry that survives the session, a standing place in the free zone.
Honest hints, complete descriptions, pure verbs marked pure and writes marked writes. That is the entire mechanism by which your agent doesn't have to ask — and the reason that, when it finally does ask, the human should listen.