Trace an EPC in one call.
A mock recall across a co-manufacturing boundary is famously a multi-day exercise: two vendors, three spreadsheets, one phone tree. This post is the same exercise as a transcript that fits on one screen. The claim is mechanical, not aspirational: when events carry computed identity, aggregation history, and organisation-grain parties, tracing an EPC is a single query returning a chain a machine can verify. Everything below runs on the committed golden-corpus fixtures; every hash is reproduced verbatim.
The setup: four hops, two parties
The corpus sequence sequence-worked-trace-four-hop.json follows one serialized unit — urn:epc:id:sgtin:0614141.107346.2017 — through four events: commissioned with its lot at the plant, packed onto SSCC urn:epc:id:sscc:0614141.0000005501, received at a DC, received at a store. Two capturing sites, no shared vendor between them — the join is the standard, not a platform.
Wire the door into a session (one config block, from the MCP page):
{ "mcpServers": { "epcis": {
"command": "npx", "args": ["-y", "epcis.dev", "mcp"] } } }
The server exposes five tools — capture, query, get_event, trace_epc, translate — re-dispatching through the same core the CLI and HTTP door run. Capture the document, then trace:
→ capture { "document": <sequence-worked-trace-four-hop.json> }
← { "accepted": true, "events": 4 }
→ trace_epc { "epc": "urn:epc:id:sgtin:0614141.107346.2017" }
← structuredContent:
{
"epc": "urn:epc:id:sgtin:0614141.107346.2017",
"events": [
{ "bizStep": "commissioning", "readPoint": "urn:epc:id:sgln:0614141.00777.0",
"hash": "ni:///sha-256;ee0c4ea58564b0060736d2c8c6678098b83bdb85de6381ef41295e47814cf971?ver=CBV2.0" },
{ "bizStep": "packing", "parentID": "urn:epc:id:sscc:0614141.0000005501",
"hash": "ni:///sha-256;f4f3c1438a7c43d35cfddbbf0e6cbdaef0ae4d302b3ba4efd882a81e879d8789?ver=CBV2.0" },
{ "bizStep": "receiving", "readPoint": "urn:epc:id:sgln:0614141.00888.0", "via": "urn:epc:id:sscc:0614141.0000005501",
"hash": "ni:///sha-256;70676462ccf3b16ad2f8a18acb40d08c0650115f5fb0029f678c8e7ce2af36ff?ver=CBV2.0" },
{ "bizStep": "receiving", "readPoint": "urn:epc:id:sgln:0614141.00999.0",
"hash": "ni:///sha-256;c4838b4517f980df9851d1113540f84e4f557199da0332fa6dc2a89e442c67ca?ver=CBV2.0" }
]
}
Recompute the hashes yourself — same fixture, same algorithm, no trust required:
$ npx epcis.dev hash sequence-worked-trace-four-hop.json
hash 0 ni:///sha-256;ee0c4ea58564b0060736d2c8c6678098b83bdb85de6381ef41295e47814cf971?ver=CBV2.0
hash 1 ni:///sha-256;f4f3c1438a7c43d35cfddbbf0e6cbdaef0ae4d302b3ba4efd882a81e879d8789?ver=CBV2.0
hash 2 ni:///sha-256;70676462ccf3b16ad2f8a18acb40d08c0650115f5fb0029f678c8e7ce2af36ff?ver=CBV2.0
hash 3 ni:///sha-256;c4838b4517f980df9851d1113540f84e4f557199da0332fa6dc2a89e442c67ca?ver=CBV2.0
What makes the answer evidence, not a report
Three properties, each doing distinct work:
Identity is computed, so the chain is checkable. Each hop carries its CBV §8.9 content hash — identity as a pure function of the event. Any party holding the same events computes the same chain; a tampered hop computes a different hash and falls out. A trace answer without content-derived identity is a story; with it, the answer is a verifiable artifact.
The containment graph is walked, not stored. Hop 3 is the subtle one: the DC never scanned the unit. It scanned the pallet — and trace_epc resolves the unit's presence through the packing event's parent/child assertion ("via" names the SSCC). The pallet's journey becomes the case's journey by graph logic over AggregationEvents, not by a parent_id column somebody keeps current. Partial depalletization and re-aggregation stay honest for the same reason: history is appended, never overwritten.
The parties are on the events. Each hop carries its capturing account and, at organisation grain, its source/destination parties — so the co-man boundary is in the chain, not a join against someone's vendor master. And the performer grain stays uncollapsed: spine:who on these fixtures is the attested observer at each hop (a line lead, a pack-out crew, a dock crew — resolvable identities, id.org.ai's Agent. Human. Thing.), while spine:capturedBy is the warrantor account stamped at the door. Who saw it and who stands behind the record are different questions; a dispute needs both answered separately — the performer model in full.
Timed, for the skeptic upstairs
The corpus trace — capture four events, walk the graph, return the chain — completes in milliseconds on a laptop: the whole transcript above is a coffee-break demo, which is the point your deputizing engineer forwards. The contrast is not against a slow computer; it is against the multi-day phone tree that answers the same question when records live in five ungoverned shapes. The recall framing of this same machinery is in the FSMA series; the executive rendering of a trace — custody evidence, not JSON — is visibility.cloud.
For an agent integrating this: the tools are typed, reads are pure, refusals are typed results (isError: true mirroring the door's problem document, never prose to regex), and paginated tools lift continuations into structuredContent.nextPageToken. The store is append-only at every door — corrections arrive as new events carrying errorDeclaration, so a chain once returned can be re-derived later and the audit view still contains its own mistakes, visibly. Exit codes on the CLI face are stable and load-bearing: 0 ok, 1 negative domain verdict, 2 usage — a failed trace is a verdict to branch on, not a crash to catch. The full contract ships in the tarball as AGENTS.md — read it where you already are: node_modules/epcis.dev/AGENTS.md.
Run the walkthrough now: npx epcis.dev mcp — the corpus, the tools, and the hashes above, on your bench, no account. Trace over a durable record — events your whole estate captured last quarter, not this session: the hosted query surface rides a capture key — get one here.
We answer in writing. We take at most five conversations a month, only when you ask for one, and only after you already have the written read.