Connect the MCP server

By the end of this page, you will know what the SealTrust MCP server allows an assistant to do, how to connect it to your client, which identifier to pass it, and what each of its seven tools returns.

On this page

The MCP server is a small program that you run on your own machine. It gives a conversational assistant the right to query the SealTrust public API, read only. MCP, for Model Context Protocol, is the standardized protocol through which an assistant calls external tools. By the end of this page, you will know how to install the server, declare it in your client, choose the identifier to pass it and call its seven tools.

The path has four steps: check that the scope matches your need, connect the server to your client, learn which identifier you have at hand, call the tools.

#1. The scope, before anything else

The server issues only GET requests, to public endpoints of https://api.sealtrust.io. It sends no API key and accepts none. None of its tools creates, modifies or deletes anything.

Direct consequence: the server sees exactly what an anonymous visitor sees when opening a passport in their browser. The ESPR regulation distinguishes several audiences: the public, consumers, value chain professionals and market surveillance authorities. SealTrust applies that distinction with six access levels: public, end_user, repairer, recycler, upstream and authority. These values are SealTrust's own. The regulation, for its part, names audiences. The MCP server reads the public level and that level only. Fields reserved for professionals or authorities cannot be reached through this path.

What the server announces to your client when the connection opens: its name, sealtrust, its version, and a sentence of instructions reminding the assistant that all data is public and that no tool modifies anything.

#2. Connect the server

You need Node.js version 18 or later. The package is called @sealtrust-io/mcp-server and the latest published version is 0.4.0.

You can launch it directly, which serves to check that it starts.

Terminal
npx @sealtrust-io/mcp-server

The server communicates through its standard input and output. A startup line goes to the error output and indicates the version as well as the API address in use. Standard output is reserved for the dialogue with the client, so nothing else is written there.

#Declare the server in a client

An MCP client is configured with a command and its arguments. The block below is the form expected by desktop clients, to be placed in their configuration file.

JSON
{
  "mcpServers": {
    "sealtrust": {
      "command": "npx",
      "args": ["-y", "@sealtrust-io/mcp-server"]
    }
  }
}

On the command line with Claude Code, the same declaration is written as follows.

Terminal
claude mcp add sealtrust -- npx -y @sealtrust-io/mcp-server

Restart the client after modifying its configuration. The seven tools then appear in its list of available tools.

#The two available settings

Two environment variables, and only two, change the behavior of the server.

VariableDefault valueEffect
SEALTRUST_API_URLhttps://api.sealtrust.ioThe address of the API being queried. Trailing slashes are stripped.
SEALTRUST_TIMEOUT_MS15000The maximum duration of a call, in milliseconds. A value that is not a strictly positive number is ignored, and the default value applies.

The timeout covers the full round trip, headers and response body included. Going over it returns an explicit network error, never a silent hang.

These two variables go in your client's configuration, under the env key. The block below is complete and can be copied as is. It carries a 30 second timeout.

JSON
{
  "mcpServers": {
    "sealtrust": {
      "command": "npx",
      "args": ["-y", "@sealtrust-io/mcp-server"],
      "env": {
        "SEALTRUST_TIMEOUT_MS": "30000"
      }
    }
  }
}

#3. Know which identifier you have

Six tools out of seven take a product identifier. Four forms exist, and they are not accepted everywhere.

FormWhat it looks likeWhere you find it
Serial number12 characters, digits and uppercase letters, for example EXEMP1E00000This is the only identifier readable on a product. The printed QR carries it. Someone who has scanned or copied a label has that one.
Identifier hash (uid_hash)0x followed by 64 hexadecimal charactersReturned by our own responses. This is the product's internal identifier. It comes from the NFC chip when the product carries one. SealTrust draws it at random when the product is identified by QR alone.
Token identifierA string of digitsThe identifier of the on-chain token, returned under the name token_id.
Certificate numberStarts with ST-CERT-On the certificate of authenticity. Accepted by verify_product and get_certificate only.

The serial number alphabet excludes the letters I, L, O and U, so that a person copying a label does not mistake a letter for a digit. The MCP server passes the number along exactly as it is given, without rewriting a single character. It is the API that puts the number back in uppercase and that brings ambiguous characters back to their canonical form: I and L are worth 1, O is worth 0.

Two tools also take a GTIN, the article code with 8, 12, 13 or 14 digits. A GTIN designates a model. It designates no particular item.

#4. The seven tools

#verify_product

Answers the question "is this product genuine". The tool chains two calls: resolving the identifier, then reading the certificate.

ParameterTypeRequiredDescription
identifierstringyesSerial number, identifier hash, token identifier or certificate number.

The response carries a status field that takes five values.

ValueWhat it means
authenticAn active certificate of authenticity is registered.
revokedThe certificate has been revoked. The product is to be treated as suspect.
expiredThe certificate has passed its end date.
found_no_certificateThe product exists in the registry and no active certificate is attached to it.
unknownNo trace of this identifier.

Coming with them: a plain text message field, a product block carrying the product name, the brand name, the category, the token identifier, the identifier hash, the contract address, the mint transaction and the creation date, a certificate block or the null value, a has_published_passport boolean, and an anchored_on_base boolean.

anchored_on_base is true when an anchor exists, whichever one it is: the one for the product batch, or the one for the passport content. The two are independent. A passport can be anchored while its batch has not been, and the other way around. To find out which of the two, and with which transaction, call get_passport_proof.

Expect this boolean to be false most of the time. Anchoring is an operation that SealTrust triggers by hand. An anchoring request coming from a brand account is refused. Minting a product does not anchor it, and publishing a passport does not anchor it either. Most products are never anchored. So an anchored_on_base set to false says nothing against the product, and your assistant must never present it as a suspicion.

#get_passport

Returns the published digital passport, at the public access level.

ParameterTypeRequiredDescription
identifierstringyesSerial number, identifier hash or token identifier. A certificate number is not accepted here.
formatstringnojson by default, or jsonld for a representation in Schema.org and GS1 vocabulary. Any other value is refused before the network call.

The response in the default format carries the passport version, the published content under the data field, the SHA-256 hash of that content under data_hash, the name of the product and of the brand, the GTIN and the product's GS1 Digital Link.

The two fields that point to the IPFS copy always hold the null value on this path. They are reserved for authenticated access levels. The link to the immutable copy, when it is published, appears in the response of get_passport_proof.

#get_passport_proof

Returns the public proofs that make it possible to verify a passport without trusting us.

ParameterTypeRequiredDescription
identifierstringnoThe item you want the proof for.
gtinstringnoThe model you want the proof for, at reference level.

You must supply exactly one of the two. Neither of them, or both at once, gives an immediate refusal with a message that explains the difference. The two parameters do not designate the same thing: the proof of an item dates the object, the proof of a reference dates the document.

The response contains the content hash, the link to the IPFS copy when it is published, a seal block that says whether the version is sealed and whether its content still matches what was sealed, the issuance state of the verifiable credential, and the number of successful physical verifications. A missing proof is absent from the response, with no empty field.

A reference level passport, the one obtained with gtin, legitimately carries neither an anchor block nor a verification count. It is tied to no physical item. Its proof is the anchoring of its own content, which dates the document. The response then carries level: "model" and the normalized GTIN.

#get_product_history

Returns the history of a product: ownership movements and authentication scans, gathered into a single timeline, from the most recent to the oldest. The first element of timeline is the most recent event.

ParameterTypeRequiredDescription
identifierstringyesSerial number, identifier hash or token identifier.

Each entry carries a type field that is verify for a scan or transfer for an ownership movement, a timestamp, the token identifier and the identifier hash. The rest depends on the type. A verify entry carries a validity indicator and no party label. A transfer entry carries two display ready labels, for the party giving up and the party receiving, and no validity indicator.

For an anonymous caller, and the MCP server is one, identities are masked: email addresses are anonymized and raw wallet addresses are not returned.

#get_certificate

Returns the public certificate of authenticity of a product.

ParameterTypeRequiredDescription
identifierstringyesSerial number, identifier hash, token identifier or certificate number.

The response carries the certificate number, its state, its issuance date, its end date when there is one, the name of the issuer, the name of the product and that of the brand, as well as the custom fields recorded at issuance, returned as is. Everything written there is public.

#resolve_gs1

Starts from a GS1 Digital Link and returns the passport it designates.

ParameterTypeRequiredDescription
gtinstringyesThe article code of the model.
serialstringnoThe serial number printed on the item. Omit it to target the model.

The presence of the serial number changes what the link designates. With it, the link /01/{gtin}/21/{serial} designates a precise item and the response carries level: "item". Without it, the link /01/{gtin} designates the article class and returns the reference level passport, the one shared by every item of the model, with level: "model". It is that second level that is used by goods sold by the meter, by the box or by the pallet. An empty string in serial is read as an absence.

The response also carries the reconstructed link, the address the resolution redirects to, the identifier retained, and the public passport when there is one.

Without a published reference passport, /01/{gtin} answers 404 and the tool returns found: false with the message "Unknown GS1 Digital Link". The result resolved: true accompanied by a null passport only happens when you supply a serial and that item has no published passport.

The serial number expected here is indeed the one printed on the product. The on-chain token identifier is not suitable in this place.

#verify_credential

Verifies the signature of the passport's verifiable credential, in SD-JWT-VC format, against the key published by the issuing brand in the form did:web.

ParameterTypeRequiredDescription
identifierstringyesSerial number, identifier hash or token identifier.

The response carries verified, the issuer's decentralized identifier, the type of the credential, the version of the signing key, the access level applied, an error field holding verification_failed in case of failure, and the content of the credential when the signature is valid.

#5. Reading responses and errors

The server separates three situations, and that separation matters for an assistant reasoning on the result.

SituationWhat the server returnsWhat to conclude from it
Product, passport or credential untraceableA normal result carrying found: false and the API message, or status: "unknown" for verify_productThe question got an answer. Nothing broke.
API error response other than a 404A tool error whose message reads SealTrust API error (, the HTTP code, ): , then the detail returned by the API. There is no space before the colon.The API answered and refused. The code says why.
API unreachable, timeout exceeded, DNS, connection refusedA tool error starting with Network errorNo information about the product. Conclude nothing about its authenticity.

A malformed argument is refused before any network call: an empty identifier, a format that is neither json nor jsonld, or the absence of a choice between identifier and gtin on the proof.

#6. The call limits

Public endpoints are capped per calling IP address, over a 60 second window. The path families that have a limit of their own each keep their own counter. The others fall under a general counter, shared by all routes without a dedicated limit.

ToolPath calledLimit
verify_productresolution, then certificate60 per minute on each of the two
get_passport, get_passport_proof, verify_credentialpassport60 per minute
get_certificatecertificate60 per minute
get_product_historytimeline30 per minute
resolve_gs1GS1 resolution, then passportgeneral counter on the GS1 resolution, shared with the other paths without a limit of their own, then 60 per minute on the passport

Two tools consume two calls instead of one. verify_product queries the resolution then the certificate. resolve_gs1 reads the resolver's redirect without following it, then requests the passport directly. Count them as two.

Going over the limit returns a 429 response, which the server surfaces as a tool error. One case is an exception. On verify_product, only the first call surfaces a 429. Going over the limit on the certificate read is logged to the error output and the tool answers anyway, with the summarized certificate that the resolution already carries.

#What can stop you

SymptomCauseWhat to do
The client shows no toolThe server did not start, or the client was not restarted after its configuration was modifiedRun npx @sealtrust-io/mcp-server by hand. The startup line must appear. Then restart the client.
Network error on every toolThe API is not reachable from the machine, or SEALTRUST_API_URL points elsewhereCheck the variable, then the machine's network egress.
A call systematically exceeds the timeoutThe 15 second timeout is too short for your linkRaise SEALTRUST_TIMEOUT_MS under the env key of your client's configuration, as in point 2, then restart the client.
found: false on an identifier you know to be rightThe form used is not accepted by this toolCheck the form in the identifier table. A certificate number only goes through on verify_product and get_certificate.
The tools contradict each other on an item withdrawn from the marketEach tool handles end of life in its own wayverify_product returns status: "unknown". get_passport_proof and get_certificate return found: false. get_passport returns the passport, together with a lifecycle block indicating that the item is no longer current.
The passport comes back without an IPFS linkThe public access level does not carry itUse get_passport_proof, which returns the link to the immutable copy when it is published.
A 429 errorA call limit has been reachedSpace out the calls. The counter restarts after 60 seconds.

Your answer opens a pre-filled email in your mail app, addressed to contact@sealtrust.io. You read it over before sending it.

Suggest a correctionReport a problem