Docs

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

tooldoeson stdio (local)on the apex door
capturethe one capture pipeline: strip → validate → stamp → appendanswers; store is session-livedkeyed — without a capture key, the typed epcisException:SecurityException 401 as a tool result
queryone-shot SimpleEventQuery; Scope-filtered — out-of-scope events are absent, not redactedanswerskeyed, same 401 contract
get_eventone stored event by eventID as an EPCISQueryDocument page; out-of-scope is absent (404), never redactedanswerskeyed, same 401 contract
trace_epcevery in-scope event referencing an EPC in any role (MATCH_anyEPC)answerskeyed, same 401 contract
translateEPCIS 1.1/1.2/2.0 XML → 2.0 JSON-LD + fidelity reportanswersanswers with no key
resolveidentity resolution delegates to the id.org.ai resolveranswers a typed tool-level error (isError: true), never a fabricated resolution
subscribestanding subscriptionsanswers 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.