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 |
| translate | EPCIS 1.1/1.2/2.0 XML → 2.0 JSON-LD + fidelity report | answers | answers with no key |
| resolve | identity resolution is homed at id.org.ai | — | answers a typed RFC 7807 refusal (epcisException:ImplementationException 501, isError: true), never a fabricated resolution |
| subscribe | standing subscriptions | — | answers the same typed 501 refusal — this deployment serves pull queries only; use query |
Calls to any name outside the advertised list are a -32602 unknown-tool error. Paginated tools lift the continuation into structuredContent.nextPageToken; pass it back to page — absence means last page.
One tool is publish-gated, and the two doors disagree about its name
Publish-gated: the EPC walk, renamed trace_epc → trace.
It answers on both doors and is advertised under two different names, because this repository renamed it and the published tarball predates the rename. Measured 2026-08-05:
| door | name it advertises |
|---|---|
| npx -y epcis.dev@0.1.0 mcp (stdio) | renamed — still trace_epc |
| POST https://epcis.dev/mcp (apex, built from this repository) | trace |
It does the same thing at both: every in-scope event referencing an EPC in any role (MATCH_anyEPC), paginated, keyed on the apex with the same 401 contract as query. docs/specs/verbs/epcis.dev.md §A2 renames it and rules out an alias; the tarball that carries the new name has not been published.
So its catalog row is tier T2 and it is absent from the table above — the claims floor is the published tarball, and a name half the surface does not serve cannot be a T1 claim. Bind the old name trace_epc → over stdio, and trace on the apex, until one publish closes the gap.
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. The refusal tools speak it too — resolve, cut live from the apex door on 2026-07-31:
{
"structuredContent": {
"type": "epcisException:ImplementationException",
"title": "resolve is not served on this door",
"status": 501,
"detail": "identity resolution is homed at id.org.ai — one
resolver, one home; this door answers a typed refusal,
never a fabricated resolution."
},
"isError": true
}
(detail wrapped for width; every other byte verbatim.)
Proof — initialize and tools/list
Over stdio against epcis.dev@0.2.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 initialize result, verbatim, cut 2026-08-05 from a clean install of the 0.2.0 tarball:
{ "serverInfo": { "name": "epcis.dev-spine",
"title": "epcis.dev event spine", "version": "epcis.dev/0.2.0" },
"protocolVersion": "2025-06-18" }
Every tool documented on this page is advertised by tools/list, on the door named beside it:
tools/list (stdio): capture, query, get_event, trace_epc, translate # renamed
tools/list (apex POST /mcp): capture, query, get_event, trace,
translate, resolve, subscribe
Both lists were re-cut from real runs on 2026-08-05 — the stdio door from npx -y epcis.dev@0.1.0 mcp, 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 trace_epc → trace difference is the publish gate described above, not a transcription slip — this page previously printed trace on both lines and that was wrong about the tarball.
The store behind the record-touching stdio tools — capture, query, get_event and the renamed trace_epc → does not survive the process. Batch your capture-then-query work inside one MCP session.