MCP tools
One tool surface, two doors. Local: npx -y epcis.dev mcp — stdio, newline-delimited JSON-RPC 2.0, a session-lived store. Hosted: POST https://epcis.dev/mcp — the same JSON-RPC envelope on the apex, where translate answers with no key and the record-touching tools are keyed.
{ "mcpServers": { "epcis": { "command": "npx", "args": ["-y", "epcis.dev", "mcp"] } } }
The tools
| tool | does | on stdio (local) | on the apex door |
|---|---|---|---|
| capture | the one capture pipeline: strip → validate → stamp → append | answers; store is session-lived | keyed — without a capture key, the typed epcisException:SecurityException 401 as a tool result |
| query | one-shot SimpleEventQuery; Scope-filtered — out-of-scope events are absent, not redacted | answers | keyed, same 401 contract |
| get_event | one stored event by eventID as an EPCISQueryDocument page; out-of-scope is absent (404), never redacted | answers | keyed, same 401 contract |
| trace_epc | every in-scope event referencing an EPC in any role (MATCH_anyEPC) | answers | keyed, same 401 contract |
| translate | EPCIS 1.1/1.2/2.0 XML → 2.0 JSON-LD + fidelity report | answers | answers with no key |
| resolve | identity resolution delegates to the id.org.ai resolver | — | answers a typed tool-level error (isError: true), never a fabricated resolution |
| subscribe | standing subscriptions | — | answers a typed tool-level error (isError: true) — use query for pull queries |
Calls to any name outside the advertised list are a -32602 unknown-tool error. Paginated tools (query, trace_epc) lift the continuation into structuredContent.nextPageToken; pass it back to page — absence means last page.
isError semantics — switch on it
A door refusal (rejected capture, out-of-scope read, bad query, missing key) is a tool result with isError: true mirroring the gateway's RFC 7807 problem document. It is not a JSON-RPC error. The keyed-tool refusal on the apex, executed:
{
"jsonrpc": "2.0", "id": 3,
"result": {
"content": [{ "type": "text", "text": "{ …problem document… }" }],
"structuredContent": {
"type": "epcisException:SecurityException",
"title": "Unauthorised request",
"status": 401
},
"isError": true
}
}
Match on structuredContent.type — the same epcisException:* taxonomy every HTTP door uses.
Proof — initialize and tools/list, verbatim
Over stdio against the published epcis.dev@0.1.0:
$ printf '%s\n%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
"protocolVersion":"2025-06-18","capabilities":{},
"clientInfo":{"name":"docs-proof","version":"0"}}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| npx -y epcis.dev mcp
The server answers (result fields excerpted from the two envelopes):
{ "serverInfo": { "name": "epcis.dev-spine",
"title": "epcis.dev event spine", "version": "epcis.dev/0.1.0" },
"protocolVersion": "2025-06-18" }
tools/list (stdio): capture, query, get_event, trace_epc, translate
tools/list (apex POST /mcp): capture, query, get_event, trace_epc,
translate, resolve, subscribe
Both lists were cut from real runs on 2026-07-31 — the stdio door from the npm tarball, the apex list from POST https://epcis.dev/mcp. The two extra apex names are the honest-refusal tools in the table above: advertised because calling them answers a typed error, never a guess.
The store behind capture/query/get_event/trace_epc on stdio does not survive the process — batch your capture-then-query work inside one MCP session.
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.