@sealtrust-io/mcp-server
v0.4.0
Published
Model Context Protocol server for SealTrust — lets AI agents verify product authenticity and read Digital Product Passports through the public read-only API
Maintainers
Readme
@sealtrust-io/mcp-server
Official Model Context Protocol (MCP) server for SealTrust.
It lets AI agents (Claude and any other MCP client) verify the authenticity of a SealTrust-protected product and read its Digital Product Passport — through the public, read-only SealTrust API. No account, no API key: the server only ever issues GET requests against public endpoints.
Read-only, public data. The server cannot mint, transfer, or modify anything, and it only sees what is already publicly served at
https://api.sealtrust.io(rate limits apply). Tier-restricted passport data (value chain / authority) is not accessible through it.
Installation
Run it directly (Node.js ≥ 18):
npx @sealtrust-io/mcp-serverClaude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"sealtrust": {
"command": "npx",
"args": ["-y", "@sealtrust-io/mcp-server"]
}
}
}Claude Code
claude mcp add sealtrust -- npx -y @sealtrust-io/mcp-serverConfiguration
| Environment variable | Default | Purpose |
|---|---|---|
| SEALTRUST_API_URL | https://api.sealtrust.io | Base URL of the SealTrust API |
| SEALTRUST_TIMEOUT_MS | 15000 | Per-request timeout in milliseconds |
Tools
Identifiers are, in the order you are most likely to have one:
- the printed serial carried by the QR code on the product itself, e.g.
Y5T2VGGF2NP9(12 Crockford Base32 characters). This is the only identifier a human can read off an object, so it is the one you will actually have; - an on-chain
token_id(decimal string); - a
uid_hash(0x+ 64 hex chars), the hash of the NFC chip's UID; - for
verify_productandget_certificateonly, a certificate number (ST-CERT-…).
Every tool accepts the first three. An unknown but well-formed identifier answers found: false, never an error.
verify_product
Authenticity check. Combines the universal resolver and the certificate lookup and returns a status: authentic, revoked, expired, found_no_certificate, or unknown.
{ "identifier": "0xabc…def" }{
"status": "authentic",
"message": "Authentic product: an active SealTrust certificate of authenticity is on record.",
"product": {
"product_name": "Atelier Bag No. 7",
"brand_name": "Maison Demo",
"token_id": "123",
"uid_hash": "0xabc…def",
"contract_address": "0x56…",
"mint_tx_hash": "0x12…",
"created_at": "2026-05-12T09:30:00Z"
},
"certificate": {
"certificate_number": "CERT-2026-0042",
"status": "active",
"issued_at": "2026-05-12T09:31:00Z",
"expires_at": null,
"issuer_name": "Maison Demo"
},
"has_published_passport": true,
"anchored_on_base": true
}anchored_on_base is true when either anchor exists: the Merkle batch of
products written at mint time, or the anchor of the passport's own content hash
written by the hourly sweep. The two are independent, and a passport is very
often anchored while its mint batch never was. Which anchor, and its
transaction, is in get_passport_proof.
An unknown identifier is a normal answer, not an error: { "status": "unknown", "message": "Unknown product: no SealTrust record matches this identifier…" }.
get_passport
Published Digital Product Passport — public ESPR access tier only. Returns the passport data, passport_version, data_hash (SHA-256), ipfs_uri / ipfs_gateway_url, gtin and gs1_digital_link. Pass "format": "jsonld" for a JSON-LD (Schema.org/GS1) representation.
{ "identifier": "123", "format": "jsonld" }get_passport_proof
Everything needed to verify the passport independently: data_hash, IPFS copy, on-chain anchor on Base L2 (chain id 8453, basescan_url, Merkle root or mint transaction), vc issuance status and the count of successful physical tag verifications. Absent proofs are omitted.
Takes either identifier (a unit) or gtin (the product reference), never both.
Two different anchors can put a passport on chain, and they are independent: anchor covers a batch of products at mint time, while passport_anchor covers the content of this passport version and is written by an hourly sweep. The second exists very often without the first, so "is it anchored on Base" is answered by either one. anchor.anchored is the field that decides, never the key name: a unit whose batch was never anchored falls back to its own mint transaction, served under the key anchor but which is not an anchor.
get_certificate
Public certificate of authenticity: number, status, issue/expiry dates, issuer, product and brand names, public custom fields.
resolve_gs1
GS1 Digital Link resolution — /01/{gtin}/21/{serial}.
The serial (GS1 AI 21) is the printed serial, not the on-chain token id. It is the same value /p/{serial} carries, so a retail scanner and a consumer's camera name the same unit. This README used to say it was the token id, which sends every integrator to a 404.
Returns the redirect target plus the public passport when one is published. Omit serial to resolve the GTIN alone, which names a product reference and returns the model-level passport rather than a unit.
{ "gtin": "02000310710000", "serial": "Y5T2VGGF2NP9" }verify_credential
Verifies the passport's SD-JWT-VC verifiable credential against the issuing brand's did:web signing keys and returns verified, issuer, vct, key_version and the disclosed credential_subject.
Note that this is SealTrust checking its own signature. An independent check means fetching the brand's DID Document yourself from /brand/{brand_id}/did.json and verifying against the key published there.
get_product_history
The item's timeline: every ownership movement (mint, transfers between holders) and every authentication scan, oldest first.
Entries carry a type, a timestamp and an is_valid flag. They do not carry a per-entry transaction hash, so do not offer a block-explorer link for each movement: the mint transaction is in verify_product, and the on-chain anchor is in get_passport_proof. Owner identities are masked.
{ "identifier": "Y5T2VGGF2NP9" }Errors
- Unknown product / no passport / no VC → a normal result with
found: false(orstatus: "unknown") and the API's message. Agents should treat this as an answer. - HTTP errors (4xx/5xx) → an MCP tool error:
SealTrust API error (status): detail. - Network failures (timeout, DNS, refused) → an MCP tool error clearly labelled
Network error, so "the product does not exist" is never confused with "the API was unreachable".
Development
npm install
npm run build # tsc → dist/
npm test # unit tests (node:test, fetch mocked)
node dist/index.js # run the stdio server locally