tracepass-mcp-server
TracePass MCP server lets AI assistants manage EU Digital Product Passports via TracePass APIs, including catalogue, passport lifecycle, compliance, EPCIS events, scope checks, supplier requests, and DPP-law Q&A.
Manage the product catalogue: list, get, create, batch-create, update, and archive products.
Manage Digital Product Passports: list/get by id or serial, create drafts, suspend/archive, generate QR/Data Matrix codes, view snapshots, manage condition flags, capture/read battery measurements, and place battery passports on the EU registry.
Run compliance and registry-readiness checks on passports.
Update passport category-specific fields and set/remove economic-operator parties such as manufacturer, importer, distributor, recycler, etc.
Work with GS1 EPCIS 2.0 supply-chain events: export, capture, poll capture jobs, and query events.
Inspect DPP category field schemas/templates and check whether product types need a passport; read catalogue-wide scope reports and trigger scope refresh runs.
Use read-only resources and reusable prompts for workflows like passport audits, product onboarding, requirements explanations, compliance gap checks, and EPCIS review.
Via separate endpoints: suppliers can answer TracePass data requests with validation/evidence upload/submission, and users can ask EU DPP law questions and create draft passports.
Supports ISO 26324 DOI identifiers for Digital Product Passports: passports can be created with a DOI identifier (bare 10./, with required model/batch/item granularity), any https://doi.org/ or doi: prefix is stripped on input, and the identifier resolves as https://doi.org/. DOI identifiers are rejected for battery passports, which accept only GS1 and ISO/IEC 15459.
This server has a remotely hosted endpoint. Open its connector listing for connection details, authentication requirements, and setup instructions:
TracePass MCP Server
A Model Context Protocol server for TracePass — the EU Digital Product Passport platform. It lets AI assistants (Claude, Cursor, IDE agents) manage products, Digital Product Passports, economic-operator parties, and GS1 EPCIS 2.0 supply-chain events.
It speaks the full MCP protocol — tools, resources, resource templates, and prompts.
Two ways to use it
The same server core ships two ways:
Hosted — point your MCP client at
https://ai.tracepass.eu/mcp. Nothing to install; always current.Local (npm) — run
tracepass-mcp-servervianpx. The MCP client launches it as a subprocess and speaks MCP over stdio.
Related MCP server: fallpharm-mcp
Authentication
The server accepts either of TracePass's two v1 auth methods on the
same Authorization: Bearer … header — it forwards whatever you send to
the API, which decides. Pick the one that fits how you're connecting:
API key | OAuth 2.0 | |
Best for | A single user, scripts, server-to-server | AI assistants / apps acting on a user's behalf |
What you send | A static | A scoped access token obtained via the OAuth flow |
Setup | Mint at Developer → API Keys | The user clicks Connect and approves scopes |
Scope | All-or-nothing (the whole workspace) | Exactly the scopes the user granted; revocable |
Works with | Hosted and local (npx) | Hosted endpoint only (needs a browser consent step) |
Which should an AI assistant use? If your MCP client supports OAuth (Claude.ai, ChatGPT, and others), prefer OAuth — the user authorizes the connection once on a TracePass consent screen, you never handle a secret, and access is least-privilege and revocable. If your client only takes a header/token, use an API key.
OAuth 2.0 (recommended for hosted clients)
No config beyond pointing your client at the hosted endpoint — discovery
is automatic. On the first unauthenticated request the server returns a
401 whose WWW-Authenticate header carries a resource_metadata URL
(RFC 9728) pointing at /.well-known/oauth-protected-resource, which
names the TracePass authorization server. The client runs the standard
authorization-code flow with PKCE (/api/oauth/authorize →
/api/oauth/token), the user approves scopes, and the client stores +
refreshes the token. If you distribute your own client, register an app
under Developer → OAuth Apps to get a client_id; many hosted
clients self-register via Dynamic Client Registration automatically.
Request only the scopes you need, e.g. passports:read passports:write offline_access. Users manage connected apps (and revoke) under
Developer → OAuth Apps → Connected Apps.
API key
Mint a tp_… key under Developer → API Keys and send it as a Bearer
token.
Hosted:
{
"mcpServers": {
"tracepass": {
"url": "https://ai.tracepass.eu/mcp",
"headers": { "Authorization": "Bearer tp_YOUR_KEY" }
}
}
}Local (npx / stdio) — the local subprocess can't do an interactive
OAuth consent step, so it's API-key only, via the TRACEPASS_API_KEY env:
{
"mcpServers": {
"tracepass": {
"command": "npx",
"args": ["-y", "tracepass-mcp-server"],
"env": {
"TRACEPASS_API_KEY": "tp_YOUR_KEY"
}
}
}
}Optional env vars:
Var | Default | Purpose |
|
| Override the TracePass API base URL (e.g. point at a local stack). |
|
| Public OAuth authorization-server origin advertised in discovery metadata. Set this when |
|
| Maximum request body size. Requests over the limit are rejected with 413 before reaching the SDK. |
|
| Maximum JSON-RPC messages in a single batch request. Oversized batches are rejected with 400 |
|
| Listen port for the hosted HTTP service. |
The hosted endpoint accepts POST only on /mcp, /compliance/mcp, and the supplier paths. GET and DELETE are rejected with 405 to prevent the SDK's stateless transport from opening an unbounded SSE stream.
Tools
The TracePass v1 API operations are grouped into 7 tools. Each takes an action enum plus action-specific arguments. The tools are:
tracepass_products- manage the product catalogue (list, get, create, create_batch, update, archive products).tracepass_passports- manage Digital Product Passports (list, get, compliance check, registry-readiness check, place a battery passport on the market (async EU registry entry), get/set condition flags, create, suspend, archive, get QR, list snapshots, get snapshot), by id or by serial. Passports are identified via GS1 or four additional EN 18219 schemes (iso15459, iec61406, did, doi); battery passports accept only gs1 and iso15459 (Art. 77(3)). Condition flags are approved yes/no facts (e.g. battery:hasBMS,rechargeable,externalStorageOnly,isStationaryBess) that gate conditional legal duties — setting an approved flag may make additional fields required and block publishing if those fields are empty.tracepass_passport_fields- update a passport's category-specific data fields, by id or by serial.tracepass_passport_parties- set or remove a passport's economic-operator parties (manufacturer, importer, etc.).tracepass_epcis- export, capture, and query a passport's GS1 EPCIS 2.0 supply-chain events.tracepass_templates- list and get the DPP category field schemas, each field traced to the EU instrument that mandates it.tracepass_scope- check whether a product type needs a Digital Product Passport (check), read the company's catalogue-wide scope report (catalogue_report), and trigger or poll a catalogue refresh run (catalogue_run, catalogue_run_status).
Each tool's full action set:
Tool | Actions |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The *_by_serial actions address a passport by the customer's own serial
number instead of its TracePass id. A serial is unique only within a GTIN, so
if the same serial exists under two GTINs in your account a serial-only call
returns 409 ambiguous_serial — pass the optional gtin arg to disambiguate
(or use the by-id action). The same gtin disambiguator applies to every
*_by_serial action.
The tracepass_passports compliance action returns a three-tier
compliance verdict (compliant / compliant_with_warnings /
incomplete) with regulation-cited findings — missing required fields,
missing economic-operator parties, format issues, and per-category
conditional rules. Read-only; use it to gap-check a passport, fix the
cited gaps, then re-check.
A compliant verdict means this passport satisfies the rules encoded here,
not this product may be placed on the market. The field specifications are
hand-authored from the regulations, not an official EU artefact, and delegated
acts are still landing. It is not legal advice.
Second-life batteries
A repurposed, remanufactured or reused battery needs a new passport
linked to the original one(s) (Battery Regulation Art. 77(7)). Pass a
lineage block to tracepass_passports create:
{ "predecessors": [ { "internalPassportId": "<your original passport id>", "trigger": "repurposing" } ] }A predecessor is named by internalPassportId (one of your own passports)
or by its resolvable identifier. trigger is one of
preparation_for_reuse, preparation_for_repurposing, repurposing or
remanufacturing. The platform derives batteryStatus from the
triggers, stores the block immutably, and links your own originals back
to the new passport (successors). A battery placed on the market before
18 Feb 2027 has no original passport: send an empty list with
noPredecessorReason. Rule violations return 422 with the rule code.
Battery measurements (living record)
capture_measurements pushes over-life data from your own equipment into a
published battery passport: state of health, fades, cycle counts, dynamic
values, state of charge, negative events, temperature history (Battery
Regulation Annex XIII point 4). Every measurement is kept; the newest per
field becomes the passport's current value and sets dynamicDataAsOf.
list_measurements and latest_measurements read them back.
They are metered against the plan's monthly measurement allowance, not the daily write budget. Paid plans keep counting past the allowance at no charge; the Free plan stops at its allowance. Reading a passport is never metered.
A note on writes
Some actions cost money or are irreversible — the server's tool descriptions tell the model so:
tracepass_passportscreatemakes a draft passport. A draft takes no plan DPP slot and is not billed; the slot, and any overage past the plan cap, is taken when the passport is published in the TracePass dashboard. Each create counts against the daily write budget.tracepass_passportsarchiveis irreversible — the public QR permanently 404s. Usesuspend(reversible) when a change might be undone.tracepass_epcis—export,captureandqueryare on every plan, Free included; captured events count against the plan's event allowance, andcaptureover it returns an overage or hard-block message.
Resources
Read-only entity data you can attach as conversation context:
tracepass://products— the product cataloguetracepass://product/{id}— one producttracepass://passport/{id}— one passport, full field detailtracepass://passport/{id}/epcis— a passport's EPCIS 2.0 eventstracepass://passport/{id}/compliance— a passport's compliance verdicttracepass://passport/{id}/registry-readiness— a mechanical pre-submission check modelled on the EU DPP Registry's formal gate: mandatory-field presence, formatting, a resolvable public link, item-level granularity, and a well-formed commodity code only where the category carries one (none for batteries: a battery passport's registration identifier is not entered in the customs declaration). Not the substantive compliance verdict, and not a prediction of the real registry's response — its registration API has no published spec. Battery only.tracepass://passport/{id}/snapshots— the snapshot history of a passport (newest first): a snapshot on publish and after every change to a non-draft passport; each entry carries version, reason (e.g. published, field_edit, status_change, baseline), actor, snapshotAt, contentHash, hashValid (re-verified on read), restorable flag, and field count.tracepass://templates— all 13 DPP category field schemastracepass://template/{category}— one category's full field schema
Prompts
Reusable DPP workflows the client surfaces as slash-commands:
audit_passport— review a passport for completeness and compliance readinessonboard_product— create a product and its first passportexplain_dpp_requirements— explain what a category's compliant DPP must contain, and the regulation behind each fieldcompliance_gap_check— produce a prioritised, regulation-cited list of what's blocking a passport's compliant publicationreview_epcis_events— summarise a passport's supply-chain trail
For suppliers: answering a data request
A second, separate endpoint serves suppliers who receive a TracePass data request. It is not part of the tools above. The request email carries a personal address:
https://ai.tracepass.eu/supplier/mcp/<token>Add it to an AI assistant as a custom connector. For clients that can set
headers, https://ai.tracepass.eu/supplier/mcp with
Authorization: Bearer <token> works too. The token authorises that one
request only; there is no account and no OAuth. The assistant can then:
Tool | What it does |
| Start here: who is asking, for which product, and every field asked for, each with its meaning, unit, format and legal source; plus answers already sent and the review outcome |
| A dry run: what would be stored, which keys were not requested, and what does not fit. Writes nothing |
| The best way to attach a datasheet or certificate when the assistant can run shell commands: returns a |
| Attach a very small file (a few kilobytes) inline as base64; prefer |
| Send answers with evidence per value to the requester's human review; can be repeated (answers merge) until reviewed |
| Whether the requester has reviewed them, the outcome, accepted fields, and when the link expires |
Answers go to the requester's human review; they never publish anything on their own.
DPP Assistant: EU passport questions
A third endpoint answers questions about EU Digital Product Passport law for anyone with a free TracePass account:
https://ai.tracepass.eu/compliance/mcpAdd it to an AI assistant as a custom connector (in Claude: Customize → Connectors → "+ Add" → "Add custom connector"), then sign in with a TracePass account when asked. It uses OAuth; no API key is needed. Answers cite their source, and each company gets about 100 answers a day. Full guide: tracepass.eu/docs/dpp-assistant.
Tool | What it does |
| Whether EU law requires a passport for a type of product, from when, and what it requires of each role |
| The data a passport must contain for a category, with the article requiring each field and who may see it |
| Changes in EU passport law and standards for your categories since a date; planned items are marked as not yet law |
| The EU DPP Registry and the technical side: registration, identifiers, data carriers, formats and standards |
| Creates a draft passport in your own account; publishing stays with you in TracePass |
| Reports an answer you think is wrong or unclear, for review |
| The company facts the assistant remembers between questions, so you do not repeat them |
Development
npm install
npm run build # tsc -> dist/
npm run typecheck
npm test # vitest
npm run lint
npm start # run the hosted HTTP service locally (:8080)
npm run start:stdio # run the stdio server locallyThe hosted service is a plain Node HTTP server (dist/http.js),
stateless — each request carries its own API key and builds a fresh
MCP session. It is containerised via the Dockerfile; the hosted endpoint
runs in the EU.
Listed on Glama
This server is published in the official MCP Registry
as eu.tracepass/tracepass and listed on Glama:
License
MIT
Available Tools
7 toolstracepass_epcisTracePass EPCIS 2.0AInspect
GS1 EPCIS 2.0 supply-chain events. export, capture, capture_job and query are on every plan, Free included. Captured events count against the plan's event allowance (10 a month on Free); over it, capture returns an overage or hard-block message.
Actions (pass via action, with args):
export — args: { id }. Export a passport's events as an EPCIS 2.0 JSON-LD document. Read-only.
export_by_serial — args: { serial, gtin? }. Same as export, addressed by your own serial. A serial is unique only WITHIN a GTIN — if it isn't unique in your account the call returns 409 ambiguous_serial; pass
gtin(or use export by id). Read-only.capture — args: { events }.
eventsis an EPCISDocument, a single event, or an array of events (JSON-LD). Returns a 202 with a captureJobId.capture_job — args: { jobId }. Poll an async capture job. Read-only.
query — args: { params? }.
paramsis a key/value map of standard EPCIS query parameters (EQ_bizStep, GE_eventTime, MATCH_epc, …). Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| args | No | Arguments for the chosen action; required fields depend on `action`. | |
| action | Yes | EPCIS 2.0: export a passport's events (export | export_by_serial), capture new events, poll a capture job, or query events. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | The resource's TracePass id, when the response is a single entity. |
| page | No | Current page number (list actions). |
| error | No | Machine-readable error code, when the API rejected the request. |
| items | No | The page of results, when the action is a list. |
| limit | No | Page size (list actions). |
| total | No | Total matching records across all pages (list actions). |
| result | No | Wraps a non-object response body (e.g. a QR code string). |
| message | No | Human-readable error or status detail, when present. |
| totalPages | No | Total number of pages (list actions). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the top-level annotations (readOnlyHint=false with no per-action nuance) by labeling export, export_by_serial, capture_job and query individually as read-only. It also discloses quota behavior (10/month Free, overage or hard-block on capture), the 202 + captureJobId async pattern, and the 409 ambiguous_serial failure mode — none of which is derivable from annotations or schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the resource, the action list, and the plan/quota caveat, then a scannable per-action bullet block. Nearly every sentence earns its place; the plan-allowance sentence is the only one that leans informational rather than operational.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return shapes need not be described, and the description instead covers what the schema can't: quota enforcement, the async job-poll workflow, and the ambiguity error with its remedy. Nothing an agent needs in order to invoke an action correctly is absent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so 3 is the floor, but the description adds real meaning: `events` may be an EPCISDocument, a single event, or an array; `params` is a standard EPCIS query map with concrete examples (EQ_bizStep, GE_eventTime, MATCH_epc); and `serial` uniqueness is scoped WITHIN a GTIN. That last point is the key semantic the schema only hints at.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States the resource precisely (GS1 EPCIS 2.0 supply-chain events) and enumerates five named actions with their argument shapes. No sibling tool covers EPCIS events, so the domain boundary is unambiguous from the name plus first sentence.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Each action carries its required args and a routing hint: export vs export_by_serial is disambiguated, and the 409 ambiguous_serial case explicitly redirects to passing `gtin` or using export by id. What's missing is guidance on when to pick `query` over `export`/`export_by_serial` for retrieving events.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tracepass_passport_fieldsTracePass passport fieldsADestructiveIdempotentInspect
Update field values on a Digital Product Passport. Every change is recorded in the passport's audit trail with the credential that made it (API key or connected app) and this MCP channel.
source (optional) says where the value came from. Omit it, or pass "manual", when the user gave you the value or it comes from their own records: it is written with the user's rights (approved for an API key or an admin; sent to review for a connected app acting for an editor). Pass "ai_suggested" when you found or inferred the value yourself (web research, reading a document): it lands in the dashboard review queue for a human to approve, and it is refused on fields only the economic operator may state or that must be measured (e.g. battery stateOfHealth).
Actions (pass via action, with args):
update — args: { id, fieldKey, value, source? }.
valuetype matches the field's dataType (string, number, boolean, array, object).update_by_serial — args: { serial, fieldKey, value, gtin?, source? }. Same as update, addressed by your own serial. A serial is unique only WITHIN a GTIN — if it isn't unique in your account the call returns 409 ambiguous_serial; pass
gtin(or use update by id) to resolve exactly.
| Name | Required | Description | Default |
|---|---|---|---|
| args | No | Arguments for the chosen action; required fields depend on `action`. | |
| action | Yes | Update one passport field, addressed by passport id (update) or by your serial (update_by_serial). |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | The resource's TracePass id, when the response is a single entity. |
| page | No | Current page number (list actions). |
| error | No | Machine-readable error code, when the API rejected the request. |
| items | No | The page of results, when the action is a list. |
| limit | No | Page size (list actions). |
| total | No | Total matching records across all pages (list actions). |
| result | No | Wraps a non-object response body (e.g. a QR code string). |
| message | No | Human-readable error or status detail, when present. |
| totalPages | No | Total number of pages (list actions). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations, it discloses that every change is recorded in an audit trail with credential and channel information, that `ai_suggested` values enter a human review queue, that some fields are refused for AI-suggested values, and that ambiguous serials return 409. These are important behavioral details not captured by the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but well-structured. It front-loads the core purpose, then uses a clearly delimited paragraph for `source` semantics and a bulleted list for actions. Each section carries necessary operational information without filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the nested schema, rich annotations, output schema, and mutation semantics, the description covers everything an agent needs: action selection, argument requirements, source behavior, audit implications, and error handling for ambiguous serials.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is 100%, the description adds substantial semantic value: it explains what `source` means in practice, why `gtin` may be needed, how `value` type depends on the field's dataType, and which args belong to each action. This goes well beyond the schema's field descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Update field values on a Digital Product Passport.' It clearly distinguishes the tool from broader passport/product tools by focusing on field-level mutation, and it names the two supported actions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly explains when to use `update` versus `update_by_serial`, when to pass `gtin` to resolve a 409 ambiguous_serial, and when to use `manual` versus `ai_suggested` based on who supplied the value. The guidance is precise and actionable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tracepass_passport_partiesTracePass passport partiesADestructiveIdempotentInspect
Manage the economic-operator parties on a passport — manufacturer, importer, authorisedRepresentative, distributor, recycler, producerResponsibilityOrg. Each party carries a legal name and at least one identifier.
Identifier rules (EN 18219 §6.2–6.5): a party needs at least one of gln, legacyOperatorId, or operatorIdentifier. If both gln and operatorIdentifier (scheme gln) are provided they must agree — a mismatch returns 400. An operatorIdentifier of scheme gln also fills the top-level gln field; facilityIdentifier never does. Typed identifiers (operatorIdentifier / facilityIdentifier) are not set by AI extraction or CSV import.
operatorIdentifier schemes:
• iso6523 — { scheme:"iso6523", icd:"<4 digits>", value } — ICD 0199 = LEI (ISO 17442), 0088 = GLN, 0060 = DUNS.
• gln — { scheme:"gln", gln:"<13 digits>" } — also fills top-level gln.
• did — { scheme:"did", did:"did::" } — syntax-checked only (EN 18219 §6.4.2(b)).
• doi — { scheme:"doi", doi:"10./" } — doi:/https://doi.org/ prefix accepted and stripped.
facilityIdentifier schemes: same four; the gln variant also accepts optional extension (GS1 SGLN sub-location).
Actions (pass via action, with args):
set — args: { id, role, legalName, gln?, country?, url?, tradeName?, postalAddress?, email?, legacyOperatorId?, operatorIdentifier?, facilityIdentifier? }. Sets or updates one role. tradeName, postalAddress and email are shown on the public passport; for a battery importer they are its Battery Regulation Art. 41(3) details.
remove — args: { id, role }. Clears one role. Once a battery passport is placed on the market, a change to a stored operatorIdentifier or facilityIdentifier returns 409 operator_locked; other party edits stay allowed. To correct a locked identifier, the passport is marked for deletion and a new one created.
| Name | Required | Description | Default |
|---|---|---|---|
| args | No | Arguments for the chosen action; required fields depend on `action`. | |
| action | Yes | Set (add/replace) or remove an economic-operator party on a passport by its role. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | The resource's TracePass id, when the response is a single entity. |
| page | No | Current page number (list actions). |
| error | No | Machine-readable error code, when the API rejected the request. |
| items | No | The page of results, when the action is a list. |
| limit | No | Page size (list actions). |
| total | No | Total matching records across all pages (list actions). |
| result | No | Wraps a non-object response body (e.g. a QR code string). |
| message | No | Human-readable error or status detail, when present. |
| totalPages | No | Total number of pages (list actions). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: discloses the 400 returned on gln/operatorIdentifier mismatch, the 409 operator_locked condition after market placement, which fields become public on the passport, and that typed identifiers are never populated by AI extraction or CSV import. This is exactly the operational context the destructive/idempotent hints cannot convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Long but deliberately structured with labelled sections and bullets, and the identifier rules are genuinely load-bearing for correct invocation. The action list sits after the identifier rules rather than up front, and several sentences restate what the nested schema already says.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists and annotations cover the safety profile, so the description's remaining burden is mutation semantics and failure modes — both covered (400 mismatch, 409 lock, public-field visibility, non-AI-populated identifiers). Nothing an agent needs to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds real meaning: the EN 18219 identifier rules, the ICD table (0199=LEI, 0088=GLN, 0060=DUNS), doi prefix stripping, and the side effect that scheme gln fills the top-level `gln` while facilityIdentifier never does. It largely mirrors the nested-schema text, which keeps it from a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource ("Manage the economic-operator parties on a passport") and enumerates the exact roles covered, plus the two actions set/remove. The scope is unmistakably about party records rather than general passport fields, but it never names the sibling tool (tracepass_passport_fields) that an agent might reach for instead.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives concrete per-action guidance (args for `set` vs `remove`), states that `set` both creates and updates a role, and describes the recovery path when an identifier is locked ("the passport is marked for deletion and a new one created"). It stops short of explicit when-not-to-use or sibling alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tracepass_passportsTracePass passportsADestructiveInspect
Manage Digital Product Passports — create, read, and run lifecycle actions.
IMPORTANT: create makes a DRAFT passport. A draft takes no plan DPP slot and is never billed; the slot, and any per-DPP overage past the plan cap, is taken when the passport is published, which happens in the TracePass dashboard, not through this tool. Each create counts against the daily write budget. archive is IRREVERSIBLE (the public QR permanently 404s); prefer suspend when a change might be undone.
IDENTIFIER SCHEMES (EN 18219): passports are identified by one of five schemes. Battery passports (Battery Regulation Art. 77(3)) accept ONLY gs1 and iso15459. • gs1 — { scheme:"gs1", gtin, serialNumber } — GS1 GTIN + serial; gtin is 8/12/13/14 digits, stored as GTIN-14. • iso15459 — { scheme:"iso15459", issuingAgencyCode, primaryId, serial? } — ISO/IEC 15459; the server derives raw (IAC + primaryId + serial). • iec61406 — { scheme:"iec61406", uri } — IEC 61406 Identification Link (https URI). Not valid for batteries. • did — { scheme:"did", did, method } — W3C DID Core. Not valid for batteries. • doi — { scheme:"doi", doi, granularity:"model"|"batch"|"item" } — ISO 26324 DOI, stored as bare 10./ (any https://doi.org/ or doi: prefix stripped on input; resolves as https://doi.org/); granularity REQUIRED per EN 18219 §5.6.2(b). Not valid for batteries. The legacy top-level gtin + serialNumber pair is still accepted as a deprecated alias for scheme:"gs1".
Actions (pass via action, with args):
list — args: { page?, limit? (≤100), productId?, status?, search? }. status ∈ draft|in_review|approved|published|suspended|expired|archived. Read-only.
get — args: { id, format? (summary|full), lang? }. Read-only. Response includes
identifier,identifierKey, and (for GS1 passports)gs1.get_by_serial — args: { serial, format?, lang?, gtin? }. Read-only. Addresses the passport by your own serial. A serial is unique only WITHIN a GTIN — if the same serial exists under two GTINs in your account the call returns 409 ambiguous_serial; pass
gtin(or use the by-id action) to resolve exactly.compliance — args: { id }. Read-only. Returns a three-tier compliance verdict (compliant | compliant_with_warnings | incomplete) with regulation-cited findings — use to gap-check a passport against the rules for its category, fix the cited fields/parties, then re-check. Also returns byRegulation[]: the same findings grouped per regulation, worst first, so you can tell WHICH regime is failing instead of reading one
incompleteas everything being wrong. A regulation absent from that array raised no finding — that is not the same as it having passed.registry_readiness — args: { id }. Read-only. Returns { ready, findings[] } — whether the passport would pass the EU DPP Registry's FORMAL submission gate (mandatory fields present, correct formatting, a resolvable public link, item-level granularity via a serial number, and a well-formed commodity code where the category carries one, and, for EV, LMT and industrial (over 2 kWh) batteries, conformance to the Commission's battery data model that the Registry validates against (REG-READY-SEMANTIC)). This is the registry's mechanical pre-submission check, NOT the substantive compliance verdict; a passport can be registry-ready yet not substantively compliant. Battery passports only.
place_on_market — args: { id }. WRITE, battery passports only, published only. Places the passport on the market: registers it in the EU DPP registry under the account's own registry access (the upload Battery Regulation Art. 77(10) requires of the operator placing the battery on the market). A legal act for the economic operator — confirm with the user first. ASYNCHRONOUS: answers 202 { passport, passportId, status: "placing", dppId } and the registry call follows in the background; read the passport (get) to follow placeOnMarketStatus: placing → placed (placedOnMarketAt, registrationId) or rejected (registrationRejectionReason). To retry a rejected passport, fix the cause and call place_on_market again. Refusals are 409 with a code: not_a_battery | not_published | already_placed (placing or placed) | operator_id_missing (no economic-operator identifier on the account) | registry_not_configured (an admin sets registry access in the dashboard) | registry_not_ready (findings[] lists what stands in the way — run registry_readiness for the structured findings; an empty list means the configured registry is not open to submissions from TracePass yet). After placement the passport's party operatorIdentifier / facilityIdentifier are locked (409 operator_locked on a change). Counts 1 write.
create — args: { productId, identifier?, gtin?, serialNumber?, lineage? }. Creates a draft (not billed). Provide identifier (preferred) or legacy gtin + serialNumber. Battery passports accept only gs1 and iso15459 schemes — other schemes return 400. A duplicate identifier returns 409. lineage (battery only) — a repurposed, remanufactured or reused battery needs a NEW passport linked to the original(s) (Battery Regulation Art. 77(7)): { predecessors: [ { internalPassportId? | identifier?, trigger: preparation_for_reuse|preparation_for_repurposing|repurposing|remanufacturing } ] (≤10), noPredecessorReason? (only with an empty list, e.g. placed on the market before 18 Feb 2027) }. The server derives batteryStatus from the triggers and links your own predecessor passports back. Immutable after create. Rule violations return 422 with the rule code (duplicate_predecessor, predecessor_not_found, status_trigger_mismatch, …).
suspend — args: { id }. Reversible — public QR shows 'suspended'.
suspend_by_serial — args: { serial, gtin? }. Same as suspend, addressed by your serial. 409 ambiguous_serial if the serial isn't unique in your account — pass
gtin.archive — args: { id, reason? (recycled|never_placed|duplicate_or_error) }. IRREVERSIBLE and destructive — confirm with the user first. reason is REQUIRED for battery passports (400 reason_required / invalid_reason without a valid one): recycled, never_placed (never placed on the market or put into service), or duplicate_or_error (a duplicate or erroneous record); for other categories it is optional. A passport marked for deletion cannot be archived (409).
archive_by_serial — args: { serial, gtin?, reason? }. IRREVERSIBLE and destructive, addressed by your serial — confirm first. Same reason rule as archive. 409 ambiguous_serial if the serial isn't unique — pass
gtin.get_qr — args: { id, format? (svg|png), symbology? (qr|datamatrix) }. Read-only. symbology=datamatrix renders an ISO/IEC 16022 Data Matrix instead of a QR (EN 18220 permits both; same passport URL).
get_qr_by_serial — args: { serial, format? (svg|png), symbology? (qr|datamatrix), gtin? }. Read-only. Same as get_qr, addressed by your own serial. A serial is unique only WITHIN a GTIN — if the same serial exists under two GTINs in your account the call returns 409 ambiguous_serial; pass
gtin(or use get_qr by id) to resolve exactly.list_snapshots — args: { id, page?, limit? (≤100), at? (ISO 8601) }. Read-only. Returns a paginated list of snapshots for the passport (newest first). A snapshot is written on publish and after every change to a non-draft passport (EN 18221 change archive); each carries id, version, reason (e.g. published|field_edit|status_change|baseline), actor (who caused it, when known), snapshotAt, contentHash, hashValid (re-verified on every read), restorable, fieldCount. With
at, returns instead the single snapshot valid at that instant (full record plus validFrom/validUntil) — answers "what did this passport say on date D"; 404 before the first snapshot. Counts 1 against the daily read budget.get_snapshot — args: { id, snapshotId }. Read-only. Returns the full archival record of one snapshot: the complete JSON-LD the passport asserted at that time, plus hash and hashValid. Counts 1 against the daily read budget.
get_condition_flags — args: { id }. Read-only. Returns the resolved condition profile Record<flagKey,{value,status,source}>. Condition flags are reviewer-approved yes/no facts gating conditional legal duties. Battery flags: hasBMS, rechargeable, externalStorageOnly, isStationaryBess. An approved flag makes specific fields required — a missing gated field is a hard publish block (conditional_missing). Counts 1 against the daily read budget.
get_condition_flags_by_serial — args: { serial, gtin? }. Read-only. Same as get_condition_flags, addressed by your own serial. 409 ambiguous_serial if serial not unique — pass gtin.
set_condition_flags — args: { id, flags: Record<flagKey, boolean|null> }. WRITE. Set or clear condition flags (null clears). Keys must be registered for the passport category (battery: hasBMS, rechargeable, externalStorageOnly, isStationaryBess). WARNING: approving a flag can make fields required and block publishing if those fields are empty — fix any gated fields before or immediately after setting the flag. Writes are approved + audited. Idempotency-Key supported. Counts 1 write.
set_condition_flags_by_serial — args: { serial, gtin?, flags }. WRITE. Same as set_condition_flags, addressed by your own serial. 409 ambiguous_serial if serial not unique — pass gtin.
capture_measurements — args: { id, measurements: [ { fieldKey, value, measuredAt (ISO 8601), externalId?, unit? } ] (≤500) }. WRITE, battery passports only, published only. Pushes over-life measurements from the customer's own equipment (e.g. a BMS reporting stateOfHealth, numberOfFullEquivalentChargingCycles; the Annex XIII point 4 use-data keys). Every measurement is stored; the newest per field becomes the passport's current value and sets dynamicDataAsOf. externalId makes a measurement idempotent. A value may be at most 16 KB serialised. batteryStatus is NOT a measurement (400 invalid_field_key). A key the Regulation keeps off this battery category returns 422 field_not_applicable (e.g. stateOfCertifiedEnergy on an LMT battery). Metered against the plan's monthly measurement allowance, not the daily write budget: paid plans keep counting past it at no charge; Free stops at its allowance. Reading a passport is never metered.
capture_measurements_by_serial — args: { serial, gtin?, measurements }. Same, addressed by your own serial.
list_measurements — args: { id, fieldKey?, from?, to? (ISO 8601), limit? (≤200), cursor? }. Read-only. Measurement history, newest first; page with the returned nextCursor.
list_measurements_by_serial — args: { serial, gtin?, fieldKey?, from?, to?, limit?, cursor? }. Read-only.
latest_measurements — args: { id }. Read-only. The newest measurement per accepted key (null where none yet).
latest_measurements_by_serial — args: { serial, gtin? }. Read-only.
| Name | Required | Description | Default |
|---|---|---|---|
| args | No | Arguments for the chosen action; required fields depend on `action` (see each action above). | |
| action | Yes | Which passport operation to run. Reads: list | get | get_by_serial | compliance | registry_readiness | get_condition_flags | get_condition_flags_by_serial | get_qr | get_qr_by_serial | list_snapshots | get_snapshot | list_measurements(_by_serial) | latest_measurements(_by_serial). Writes: set_condition_flags | set_condition_flags_by_serial | capture_measurements(_by_serial) | create (a draft, not billed). Lifecycle: place_on_market (battery, published; async EU registry entry — confirm first) | suspend (reversible) | archive (IRREVERSIBLE; battery passports need `reason`), the last two each with a _by_serial variant. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | The resource's TracePass id, when the response is a single entity. |
| page | No | Current page number (list actions). |
| error | No | Machine-readable error code, when the API rejected the request. |
| items | No | The page of results, when the action is a list. |
| limit | No | Page size (list actions). |
| total | No | Total matching records across all pages (list actions). |
| result | No | Wraps a non-object response body (e.g. a QR code string). |
| message | No | Human-readable error or status detail, when present. |
| totalPages | No | Total number of pages (list actions). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial context the coarse annotations (destructiveHint=true covers 25 heterogeneous actions) cannot convey: drafts take no slot and are never billed, the slot is consumed on publish in the dashboard, archive permanently 404s the public QR, place_on_market is async (202 → placing/placed/rejected) and locks operator identifiers, and measurements are metered against a separate allowance. Deducting one point only because the per-action read/write labels are scattered across a very long body rather than summarized up front, making the destructive surface easy to miss.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loading is good — the draft/billing and archive-irreversibility warnings come before anything else, and headers plus bullets make a 25-action surface navigable. However the body is very long and duplicates structured data it does not need to restate (the archive reason enum, the measurements array shape, the action enum), so a meaningful share of sentences do not earn their place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, yet the description still explains return shapes (compliance's three-tier verdict and byRegulation[], registry_readiness { ready, findings[] }, snapshot fields) — helpful but partly redundant. For a 25-action dispatcher with nested args, the coverage of error codes, limits, and preconditions is thorough enough that an agent could call any action correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description genuinely adds meaning: GTIN stored as GTIN-14, DOI prefixes stripped on input, granularity required per EN 18219 §5.6.2(b), battery passports restricted to gs1/iso15459, lineage capped at 10 predecessors and immutable after create, and reason required only for battery passports. It does repeat several schema facts (the reason enum, measurements shape), which keeps it from a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource statement ("Manage Digital Product Passports — create, read, and run lifecycle actions") and then enumerates all 25 actions with concrete semantics, so an agent knows exactly what domain this covers. The only gap at the 5 level is that it never distinguishes itself from the siblings (tracepass_passport_fields, tracepass_passport_parties, tracepass_epcis), so an agent must infer that field/party edits live elsewhere.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Intra-tool routing is unusually explicit: suspend vs archive ("prefer `suspend` when a change might be undone"), compliance vs registry_readiness ("mechanical pre-submission check, NOT the substantive compliance verdict"), by-id vs by-serial (409 ambiguous_serial, pass `gtin`), and confirm-before-place_on_market. What is missing is cross-tool guidance — nothing says when to reach for this tool rather than a sibling.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tracepass_productsTracePass productsADestructiveInspect
Manage the TracePass product catalogue. A product is the catalogue layer — one product can have many passports (one per serialised unit). Products are not billable on their own.
Actions (pass via action, with args):
list — args: { page?, limit? (≤100), category?, status?, search? }. Read-only.
get — args: { id }. Read-only.
create — args: { name, model, category, description? }.
categoryis one of: battery, textile, electronics, construction, steel, detergents, paints-coatings, packaging, furniture, tyres, jewelry, toys, fmcg.update — args: { id, name?, model?, description? }; pass at least one field to change.
create_batch — args: { products: [ { name, model, category, description? }, … ] }, up to 100. Partial-success: the response carries a per-item status, so some items can be created while others error. The whole batch consumes N writes upfront; if that would exceed the daily cap NOTHING is created (429).
archive — args: { id }. Soft-archive a product. Blocked with 409 while any non-archived passport still references it — archive those passports first. This is reversible and is NOT deletion.
| Name | Required | Description | Default |
|---|---|---|---|
| args | No | Arguments for the chosen action; required fields depend on `action` (see each action above). | |
| action | Yes | Which product operation to run: list | get | create | create_batch | update | archive. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | The resource's TracePass id, when the response is a single entity. |
| page | No | Current page number (list actions). |
| error | No | Machine-readable error code, when the API rejected the request. |
| items | No | The page of results, when the action is a list. |
| limit | No | Page size (list actions). |
| total | No | Total matching records across all pages (list actions). |
| result | No | Wraps a non-object response body (e.g. a QR code string). |
| message | No | Human-readable error or status detail, when present. |
| totalPages | No | Total number of pages (list actions). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the annotations: create_batch partial-success semantics, per-item statuses, the N-writes-upfront reservation rule and the 429 fail-closed behavior, the 409 guard, and explicit reversibility ('This is reversible and is NOT deletion') for archive. These are exactly the operational traits an agent cannot infer from the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the domain model, then a scannable per-action bullet list. Every line carries a constraint, enum, or failure mode; there is no filler prose despite the density.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists so return values need no explanation, yet the description still flags the one output detail that matters operationally (per-item status in batch responses). Combined with the write-cap, conflict, and reversibility notes, nothing an agent needs is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description still adds action-scoped meaning the schema cannot express, such as which fields are required per action and 'pass at least one field to change' for update, though the category enum and limit≤100 are restatements of the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States the resource (TracePass product catalogue) and defines it precisely against the adjacent concept: 'one product can have many passports (one per serialised unit)'. That single clause removes the ambiguity with the sibling tracepass_passports tool, and the action list spells out the verb set.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Each action carries its own argument contract and preconditions: archive is blocked with 409 while a non-archived passport references the product, with an explicit instruction to archive passports first. It also states the products/limit cap and that products are not billable, so the agent knows when this tool is not the right lever.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tracepass_scopeTracePass DPP scope checkerAInspect
Check what EU law says for a product type matching the facts given: whether it needs a Digital Product Passport, from when, at which level, under which act and article, and the duties of each role. Also read the company's catalogue-wide scope report and trigger a catalogue refresh run.
HARD RULES — always apply these to scope answers:
Answers state what the law says for product types matching the given facts. This is not legal advice; never write 'you must' — use product-type phrasing ('LMT batteries need a passport from 18 February 2027, Battery Regulation Art. 77(1)').
Cite the article the answer gives for every verdict and duty. A regulation without an article is not a citation. A
not_coveredanswer rests on no article: say our rules do not cover the product and name the nearest act it gives, never cite that act as the basis.A date with precision 'quarter', 'month' or 'year' is a plan, NEVER a deadline or a start date. Write 'planned for Q2 2027 (planned, not law yet)', never 'from', 'by' or 'deadline'.
Relay
not_coveredandneeds_inputas the results they are, never as errors.not_covered= the product falls outside these scope rules.needs_input= blocking questions remain unanswered — relay the questions inask[]to the user before giving a verdict.Items in an act's
plannedlist are not law yet: always label them 'Planned, not law yet' (labelText), with theirwording.ruleStatus: 'needs_recheck'means the law behind an act changed and our rules for it are being re-checked (sincerecheckSince): say so with the answer.
Actions (pass via action, with args):
check — args: { category?, facts?, role?, placedOnMarket?, lifecycleEvent?, contains?, productId?, passportId?, locale? }. Ask whether a product type needs a DPP. Available on every plan, Free included. Counts against the per-company scope-answer cap (~100/day, 30/min burst), NOT the v1 read/write budget. 429
scope_answer_capwhen the daily cap is spent.not_coveredandneeds_inputare 200 results, not errors. category: one of battery | detergents | toys | construction | textile | steel | tyres | furniture | electronics | packaging | paints-coatings | jewelry | fmcg | vehicles | other. facts: namespace-prefixed keys from the loaded rules (e.g. battery.use, battery.capacityKWh). Unknown keys or wrong values return 400 invalid_input. role: your role in the act's terms: manufacturer | importer | distributor | authorised_representative | fulfilment_service_provider. Selling under your own name = manufacturer with facts.ownBrand: true. Omit to list every role's duties. placedOnMarket: { date: 'YYYY-MM-DD' | 'YYYY-MM' | 'YYYY', precision?: 'day'|'month'|'year' } or null. contains: array (≤5) of component queries — use to ask about a battery inside a toy: [ { label?, facts? } ]. locale: en | bg | de | it (default: from Accept-Language header, else en).catalogue_report — args: { classification?, category?, act?, changed?, q?, cursor?, limit? (≤500) }. Read the catalogue-wide scope report (one row per product). Requires the Growth plan or above; 403
plan_upgrade_requiredbelow Growth — tell the user this needs a plan upgrade, do not retry. Counts 1 against the daily read budget per page. classification filter: missing | draft_only | published_incomplete | published | not_required | open_questions | not_covered. Response: { run, running, totals, items: [{ productId, passportIds, gtin, category, summary, classification, changedAt }], nextCursor, disclaimer }. Full answer for one product: call check with that productId.catalogue_run — args: {}. Start a catalogue scope refresh run (also runs nightly). Returns 202 { runId, status }; poll with catalogue_run_status. Requires Growth plan. 409 run_in_progress if already active; 429 run_cooldown with retryAfter within 10 minutes of a finished run.
catalogue_run_status — args: { runId }. Get the status (queued | running | done | failed) and result counts for one catalogue run. Requires Growth plan.
| Name | Required | Description | Default |
|---|---|---|---|
| args | No | Arguments for the chosen action; required fields depend on `action` (see above). | |
| action | Yes | Which scope operation to run: check | catalogue_report | catalogue_run | catalogue_run_status. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | The resource's TracePass id, when the response is a single entity. |
| page | No | Current page number (list actions). |
| error | No | Machine-readable error code, when the API rejected the request. |
| items | No | The page of results, when the action is a list. |
| limit | No | Page size (list actions). |
| total | No | Total matching records across all pages (list actions). |
| result | No | Wraps a non-object response body (e.g. a QR code string). |
| message | No | Human-readable error or status detail, when present. |
| totalPages | No | Total number of pages (list actions). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well past the annotations with operational detail an agent cannot infer: per-company cap (~100/day, 30/min burst, 429 scope_answer_cap), that scope answers do NOT draw on the v1 read/write budget, 202 + polling for catalogue_run, 409 run_in_progress, 429 run_cooldown with retryAfter inside 10 minutes, and that not_covered/needs_input are 200 results rather than errors. It also explains the semantics of ruleStatus 'needs_recheck' and the 'planned' list, which are domain behaviours, not schema facts. Nothing here contradicts the annotations (readOnlyHint=false is consistent with catalogue_run starting a run).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded and the HARD RULES are operationally necessary, but the text is long and repeats itself: 'not_covered and needs_input are 200 results, not errors' appears in the rules and again under check, and 'Requires Growth plan' is restated for three separate actions. Trimming that duplication would lose no information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Although an output schema exists, the description still sketches the catalogue_report payload ({ run, running, totals, items, nextCursor, disclaimer }) and covers the parts an agent could otherwise get wrong: plan gating, error codes, polling flow, and how to phrase legal verdicts. For a four-action dispatcher with nested args this is complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so 3 is the floor, but the description adds real meaning: the full category enum, that facts keys are namespace-prefixed ('battery.use', 'battery.capacityKWh') and that unknown keys or wrong values yield 400 invalid_input, that ownBrand:true maps to manufacturer, and that placedOnMarket precision changes whether a date is a plan or a deadline. It also documents the 5-item cap on contains and the meaning of limit ≤500.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence names a specific verb and resource ('check what EU law says for a product type… whether it needs a Digital Product Passport') and the Actions block enumerates all four operations (check, catalogue_report, catalogue_run, catalogue_run_status), so the multi-action surface is unambiguous. It never references the sibling tools (tracepass_passports, tracepass_products, tracepass_epcis), so an agent gets no explicit help separating this from them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Each action carries routing context: 'Full answer for one product: call check with that productId', 'poll with catalogue_run_status', and plan thresholds ('Requires the Growth plan or above; 403 plan_upgrade_required below Growth — tell the user this needs a plan upgrade, do not retry'). What is missing is guidance relative to the sibling tools — nothing says when to use tracepass_scope versus tracepass_passports for the same underlying data.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
tracepass_templatesTracePass DPP templates (regulatory schemas)ARead-onlyIdempotentInspect
Discover the regulatory field schema for each DPP category — what a COMPLIANT passport must contain, per the governing EU regulation. Read-only reference data. Use this to advise on requirements before creating products/passports, and to gap-check a draft against the rules.
Actions (pass via action, with args):
list — args: {}. Lists all 13 categories with their field count, required-field count, and governing regulation (name + number + effective/mandatory dates).
get — args: { category }. Full field schema for one category: every field's key, label, dataType, whether it is REQUIRED, its access level (public/restricted/authority), enum options, validation bounds, and — where known — the regulation article/annex that mandates it.
categoryis one of: battery, textile, electronics, construction, steel, detergents, paints-coatings, packaging, furniture, tyres, jewelry, toys, fmcg.
BATTERY — required-ness is per-category, so required alone is the wrong answer. Resolve it in this order:
SCOPE FIRST. Only EV, LMT and industrial_gt_2kwh batteries owe a passport at all (Art. 77(1), Reg (EU) 2023/1542). For portable, SLI or industrial_lte_2kwh, NO field is required — do not list mandatory fields for them; say the battery is out of scope.
Then
requiredBy[batteryCategory]where the field carries that map (required | conditional | notApplicable).conditionalmeans owed only when a fact holds (a commercial warranty offered, a BMS present, the battery in service); an approved condition flag can make it required. Annex XIII 4(d) use data (charging cycles, negative events, temperature history, state of charge) is expected once the battery is in service and never blocks publishing.Then fall back to
required. The map is keyed ONLY by the three in-scope categories, so skipping step 1 falls through torequiredand invents an obligation the Regulation does not impose. Note also that EV and LMT report state-of-health through MUTUALLY EXCLUSIVE field sets — an EV battery must leave the remaining-capacity cluster empty and an LMT battery must leave stateOfCertifiedEnergy empty, so no single battery ever fills every field.
| Name | Required | Description | Default |
|---|---|---|---|
| args | No | Arguments for the chosen action; `category` is required for get, ignored for list. | |
| action | Yes | List all DPP category templates, or get one template by category. |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | The resource's TracePass id, when the response is a single entity. |
| page | No | Current page number (list actions). |
| error | No | Machine-readable error code, when the API rejected the request. |
| items | No | The page of results, when the action is a list. |
| limit | No | Page size (list actions). |
| total | No | Total matching records across all pages (list actions). |
| result | No | Wraps a non-object response body (e.g. a QR code string). |
| message | No | Human-readable error or status detail, when present. |
| totalPages | No | Total number of pages (list actions). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare read-only/idempotent/non-destructive, yet the description goes far beyond them: it discloses that required-ness is scoped by battery category, that out-of-scope chemistries owe no fields, the precedence order (scope → requiredBy → required), that Annex XIII use data never blocks publishing, and that EV/LMT health fields are mutually exclusive. It even flags the specific failure mode ('skipping step 1 ... invents an obligation').
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with a one-line purpose, then a tight bulleted action list before the deep-dive. The BATTERY block is long and dense, but every sentence prevents a concrete error (false obligations, contradictory field sets), so the length is largely earned rather than padded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists and the description still summarizes return shapes; for a two-parameter, read-only reference tool with nested return objects this is fully sufficient. The domain traps an agent would otherwise hit (out-of-scope batteries, conditional fields, mutually exclusive clusters) are all pre-empted.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds real meaning: it specifies that `args` is empty for list and ignored-for-list/required-for-get for category, and enumerates what each action returns (field count, required count, regulation, dataType, access level, enum options, validation bounds, article/annex). That is more than the schema supplies, though `category` itself is not expanded beyond the enum list.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Discover the regulatory field schema for each DPP category') and enumerates both actions with their arguments, so an agent can distinguish this reference-lookup tool from siblings like tracepass_passport_fields. The 'what a COMPLIANT passport must contain, per the governing EU regulation' clause pins the scope precisely.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives clear use contexts: 'advise on requirements before creating products/passports' and 'gap-check a draft against the rules.' It does not name a sibling alternative or state when not to use it (e.g., versus tracepass_passport_fields for an actual passport's values), so it falls short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
7 tool updates
v1.23.2- Changed
tracepass_epcis4 fields changed- removed
Output schema / properties / error / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / error / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / message / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / message / typeAdded value: +[ + "string", + "null" +]
- Changed
tracepass_passport_fields4 fields changed- removed
Output schema / properties / error / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / error / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / message / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / message / typeAdded value: +[ + "string", + "null" +]
- Changed
tracepass_passport_parties4 fields changed- removed
Output schema / properties / error / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / error / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / message / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / message / typeAdded value: +[ + "string", + "null" +]
- Changed
tracepass_passports6 fields changed- removed
Input schema / properties / args / properties / flags / additionalProperties / anyOfRemoved value: -[ - { - "type": "boolean" - }, - { - "type": "null" - } -] - added
Input schema / properties / args / properties / flags / additionalProperties / typeAdded value: +[ + "boolean", + "null" +] - removed
Output schema / properties / error / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / error / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / message / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / message / typeAdded value: +[ + "string", + "null" +]
- Changed
tracepass_products4 fields changed- removed
Output schema / properties / error / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / error / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / message / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / message / typeAdded value: +[ + "string", + "null" +]
- Changed
tracepass_scope10 fields changed- removed
Input schema / properties / args / properties / lifecycleEvent / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Input schema / properties / args / properties / lifecycleEvent / typeAdded value: +[ + "string", + "null" +] - removed
Input schema / properties / args / properties / passportId / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Input schema / properties / args / properties / passportId / typeAdded value: +[ + "string", + "null" +] - removed
Input schema / properties / args / properties / productId / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Input schema / properties / args / properties / productId / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / error / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / error / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / message / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / message / typeAdded value: +[ + "string", + "null" +]
- Changed
tracepass_templates4 fields changed- removed
Output schema / properties / error / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / error / typeAdded value: +[ + "string", + "null" +] - removed
Output schema / properties / message / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / message / typeAdded value: +[ + "string", + "null" +]
1 tool update
v1.23.1- Changed
tracepass_passports3 fields changed- changed
Input schema / properties / action / descriptionPrevious value: -"Which passport operation to run. Reads: list | get | get_by_serial | compliance | registry_readiness | get_condition_flags | get_condition_flags_by_serial | get_qr | get_qr_by_serial | list_snapshots | get_snapshot | list_measurements(_by_serial) | latest_measurements(_by_serial). Writes: set_condition_flags | set_condition_flags_by_serial | capture_measurements(_by_serial) | create (a draft, not billed). Lifecycle: suspend (reversible) | archive (IRREVERSIBLE; battery passports need `reason`), each with a _by_serial variant."New value: +"Which passport operation to run. Reads: list | get | get_by_serial | compliance | registry_readiness | get_condition_flags | get_condition_flags_by_serial | get_qr | get_qr_by_serial | list_snapshots | get_snapshot | list_measurements(_by_serial) | latest_measurements(_by_serial). Writes: set_condition_flags | set_condition_flags_by_serial | capture_measurements(_by_serial) | create (a draft, not billed). Lifecycle: place_on_market (battery, published; async EU registry entry — confirm first) | suspend (reversible) | archive (IRREVERSIBLE; battery passports need `reason`), the last two each with a _by_serial variant." - changed
Input schema / properties / action / enumPrevious value: -[ - "list", - "get", - "get_by_serial", - "compliance", - "registry_readiness", - "get_condition_flags", - "get_condition_flags_by_serial", - "set_condition_flags", - "set_condition_flags_by_serial", - "capture_measurements", - "capture_measurements_by_serial", - "list_measurements", - "list_measurements_by_serial", - "latest_measurements", - "latest_measurements_by_serial", - "create", - "suspend", - "suspend_by_serial", - "archive", - "archive_by_serial", - "get_qr", - "get_qr_by_serial", - "list_snapshots", - "get_snapshot" -]New value: +[ + "list", + "get", + "get_by_serial", + "compliance", + "registry_readiness", + "place_on_market", + "get_condition_flags", + "get_condition_flags_by_serial", + "set_condition_flags", + "set_condition_flags_by_serial", + "capture_measurements", + "capture_measurements_by_serial", + "list_measurements", + "list_measurements_by_serial", + "latest_measurements", + "latest_measurements_by_serial", + "create", + "suspend", + "suspend_by_serial", + "archive", + "archive_by_serial", + "get_qr", + "get_qr_by_serial", + "list_snapshots", + "get_snapshot" +] - changed
Input schema / properties / args / properties / id / descriptionPrevious value: -"Passport id. Required for get/compliance/suspend/archive/get_qr (the by-id actions)."New value: +"Passport id. Required for get/compliance/place_on_market/suspend/archive/get_qr (the by-id actions)."
2 tool updates
v1.22.0- Changed
tracepass_passport_parties4 fields changed- added
Input schema / properties / args / properties / emailAdded value: +{ + "description": "Company contact e-mail, not a person's (set, optional). Public.", + "type": "string" +} - added
Input schema / properties / args / properties / postalAddressAdded value: +{ + "description": "Postal address with a single contact point, one line, max 500 chars (set, optional). Public.", + "type": "string" +} - added
Input schema / properties / args / properties / tradeNameAdded value: +{ + "description": "Registered trade name or registered trade mark, max 200 chars (set, optional). Public.", + "type": "string" +} - added
Input schema / properties / args / properties / urlAdded value: +{ + "description": "Organisation web address (set, optional).", + "type": "string" +}
- Changed
tracepass_passports3 fields changed- changed
Input schema / properties / action / descriptionPrevious value: -"Which passport operation to run. Reads: list | get | get_by_serial | compliance | registry_readiness | get_condition_flags | get_condition_flags_by_serial | get_qr | get_qr_by_serial | list_snapshots | get_snapshot | list_measurements(_by_serial) | latest_measurements(_by_serial). Writes: set_condition_flags | set_condition_flags_by_serial | capture_measurements(_by_serial) | create (BILLABLE). Lifecycle: suspend (reversible) | archive (IRREVERSIBLE), each with a _by_serial variant."New value: +"Which passport operation to run. Reads: list | get | get_by_serial | compliance | registry_readiness | get_condition_flags | get_condition_flags_by_serial | get_qr | get_qr_by_serial | list_snapshots | get_snapshot | list_measurements(_by_serial) | latest_measurements(_by_serial). Writes: set_condition_flags | set_condition_flags_by_serial | capture_measurements(_by_serial) | create (a draft, not billed). Lifecycle: suspend (reversible) | archive (IRREVERSIBLE; battery passports need `reason`), each with a _by_serial variant." - removed
Input schema / properties / args / properties / confirmOverageRemoved value: -{ - "description": "Set true to accept per-passport overage charges when over the plan quota (402). Applies to create.", - "type": "boolean" -} - added
Input schema / properties / args / properties / reasonAdded value: +{ + "description": "archive / archive_by_serial: Required for battery passports: why the passport ends. A battery passport ends only when the battery has been recycled, so the choices are recycled, never placed on the market or put into service, or a duplicate or erroneous record.", + "enum": [ + "recycled", + "never_placed", + "duplicate_or_error" + ], + "type": "string" +}
7 tool updates
v1.19.1- Changed
tracepass_epcis4 fields changed- added
Output schema / properties / error / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / error / typeRemoved value: -"string" - added
Output schema / properties / message / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / message / typeRemoved value: -"string"
- Changed
tracepass_passport_fields4 fields changed- added
Output schema / properties / error / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / error / typeRemoved value: -"string" - added
Output schema / properties / message / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / message / typeRemoved value: -"string"
- Changed
tracepass_passport_parties4 fields changed- added
Output schema / properties / error / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / error / typeRemoved value: -"string" - added
Output schema / properties / message / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / message / typeRemoved value: -"string"
- Changed
tracepass_passports4 fields changed- added
Output schema / properties / error / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / error / typeRemoved value: -"string" - added
Output schema / properties / message / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / message / typeRemoved value: -"string"
- Changed
tracepass_products4 fields changed- added
Output schema / properties / error / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / error / typeRemoved value: -"string" - added
Output schema / properties / message / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / message / typeRemoved value: -"string"
- Added
tracepass_scope - Changed
tracepass_templates4 fields changed- added
Output schema / properties / error / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / error / typeRemoved value: -"string" - added
Output schema / properties / message / anyOfAdded value: +[ + { + "type": "string" + }, + { + "type": "null" + } +] - removed
Output schema / properties / message / typeRemoved value: -"string"
1 tool update
v1.17.0- Changed
tracepass_passport_fields1 field changed- added
Input schema / properties / args / properties / sourceAdded value: +{ + "description": "Where the value came from: \"manual\" (default — the user's statement) or \"ai_suggested\" (you found it; it goes to human review).", + "enum": [ + "manual", + "ai_suggested" + ], + "type": "string" +}
1 tool update
v1.16.2- Changed
tracepass_passports8 fields changed- changed
Input schema / properties / action / descriptionPrevious value: -"Which passport operation to run. Reads: list | get | get_by_serial | compliance | registry_readiness | get_condition_flags | get_condition_flags_by_serial | get_qr | get_qr_by_serial | list_snapshots | get_snapshot. Writes: set_condition_flags | set_condition_flags_by_serial | create (BILLABLE). Lifecycle: suspend (reversible) | archive (IRREVERSIBLE), each with a _by_serial variant."New value: +"Which passport operation to run. Reads: list | get | get_by_serial | compliance | registry_readiness | get_condition_flags | get_condition_flags_by_serial | get_qr | get_qr_by_serial | list_snapshots | get_snapshot | list_measurements(_by_serial) | latest_measurements(_by_serial). Writes: set_condition_flags | set_condition_flags_by_serial | capture_measurements(_by_serial) | create (BILLABLE). Lifecycle: suspend (reversible) | archive (IRREVERSIBLE), each with a _by_serial variant." - changed
Input schema / properties / action / enumPrevious value: -[ - "list", - "get", - "get_by_serial", - "compliance", - "registry_readiness", - "get_condition_flags", - "get_condition_flags_by_serial", - "set_condition_flags", - "set_condition_flags_by_serial", - "create", - "suspend", - "suspend_by_serial", - "archive", - "archive_by_serial", - "get_qr", - "get_qr_by_serial", - "list_snapshots", - "get_snapshot" -]New value: +[ + "list", + "get", + "get_by_serial", + "compliance", + "registry_readiness", + "get_condition_flags", + "get_condition_flags_by_serial", + "set_condition_flags", + "set_condition_flags_by_serial", + "capture_measurements", + "capture_measurements_by_serial", + "list_measurements", + "list_measurements_by_serial", + "latest_measurements", + "latest_measurements_by_serial", + "create", + "suspend", + "suspend_by_serial", + "archive", + "archive_by_serial", + "get_qr", + "get_qr_by_serial", + "list_snapshots", + "get_snapshot" +] - added
Input schema / properties / args / properties / cursorAdded value: +{ + "description": "list_measurements(_by_serial): nextCursor from the previous page.", + "type": "string" +} - added
Input schema / properties / args / properties / fieldKeyAdded value: +{ + "description": "list_measurements(_by_serial): only this field key.", + "type": "string" +} - added
Input schema / properties / args / properties / fromAdded value: +{ + "description": "list_measurements(_by_serial): measuredAt from (ISO 8601).", + "type": "string" +} - added
Input schema / properties / args / properties / lineageAdded value: +{ + "description": "create, battery only: link a second-life battery's new passport to the original passport(s) (Art. 77(7)). See the create action.", + "properties": { + "noPredecessorReason": { + "maxLength": 500, + "type": "string" + }, + "predecessors": { + "items": { + "properties": { + "identifier": { + "maxLength": 2000, + "type": "string" + }, + "internalPassportId": { + "type": "string" + }, + "trigger": { + "enum": [ + "preparation_for_reuse", + "preparation_for_repurposing", + "repurposing", + "remanufacturing" + ], + "type": "string" + } + }, + "required": [ + "trigger" + ], + "type": "object" + }, + "maxItems": 10, + "type": "array" + } + }, + "required": [ + "predecessors" + ], + "type": "object" +} - added
Input schema / properties / args / properties / measurementsAdded value: +{ + "description": "capture_measurements(_by_serial): [{ fieldKey, value, measuredAt, externalId?, unit? }], max 500.", + "items": { + "properties": { + "externalId": { + "type": "string" + }, + "fieldKey": { + "minLength": 1, + "type": "string" + }, + "measuredAt": { + "minLength": 1, + "type": "string" + }, + "unit": { + "type": "string" + }, + "value": {} + }, + "required": [ + "fieldKey", + "value", + "measuredAt" + ], + "type": "object" + }, + "type": "array" +} - added
Input schema / properties / args / properties / toAdded value: +{ + "description": "list_measurements(_by_serial): measuredAt to (ISO 8601).", + "type": "string" +}
2 tool updates
v1.14.1- Changed
tracepass_passport_parties6 fields changed- changed
Input schema / properties / args / properties / country / descriptionPrevious value: -"Party country code (set, optional)."New value: +"ISO 3166-1 alpha-2 country code (set, optional)." - added
Input schema / properties / args / properties / facilityIdentifierAdded value: +{ + "additionalProperties": {}, + "description": "Structured facility identifier per EN 18219 §6.2–6.5. Same four schemes as operatorIdentifier; the gln variant also accepts optional `extension` (GS1 SGLN sub-location). Never fills the top-level gln field. Not set by AI extraction.", + "propertyNames": { + "type": "string" + }, + "type": "object" +} - changed
Input schema / properties / args / properties / gln / descriptionPrevious value: -"GS1 Global Location Number for the party (set, optional)."New value: +"GS1 Global Location Number (13 digits). Strongly recommended for multi-role disambiguation." - changed
Input schema / properties / args / properties / legacyOperatorId / descriptionPrevious value: -"Your internal operator id for the party (set, optional)."New value: +"Free-text fallback identifier (VAT, EORI, supplier code). Required when gln and operatorIdentifier are both absent." - added
Input schema / properties / args / properties / operatorIdentifierAdded value: +{ + "additionalProperties": {}, + "description": "Structured operator identifier per EN 18219 §6.2–6.5. Must have `scheme` plus scheme fields. Schemes: iso6523 {icd:\"<4 digits>\", value} | gln {gln:\"<13 digits>\"} | did {did:\"did:<method>:<id>\"} | doi {doi:\"10.<registrant>/<suffix>\"}. scheme gln also fills the top-level gln field; they must match if both are set (400 otherwise). Not set by AI extraction.", + "propertyNames": { + "type": "string" + }, + "type": "object" +} - changed
Input schema / properties / args / properties / role / descriptionPrevious value: -"Economic-operator role, e.g. manufacturer | importer | distributor | authorised_representative (required)."New value: +"Economic-operator role: manufacturer | importer | authorisedRepresentative | distributor | recycler | producerResponsibilityOrg (required)."
- Changed
tracepass_passports11 fields changed- changed
Input schema / properties / action / descriptionPrevious value: -"Which passport operation to run. Reads: list | get | get_by_serial | compliance | registry_readiness | get_qr | get_qr_by_serial. Lifecycle: create (BILLABLE) | suspend (reversible) | archive (IRREVERSIBLE), each with a _by_serial variant."New value: +"Which passport operation to run. Reads: list | get | get_by_serial | compliance | registry_readiness | get_condition_flags | get_condition_flags_by_serial | get_qr | get_qr_by_serial | list_snapshots | get_snapshot. Writes: set_condition_flags | set_condition_flags_by_serial | create (BILLABLE). Lifecycle: suspend (reversible) | archive (IRREVERSIBLE), each with a _by_serial variant." - changed
Input schema / properties / action / enumPrevious value: -[ - "list", - "get", - "get_by_serial", - "compliance", - "registry_readiness", - "create", - "suspend", - "suspend_by_serial", - "archive", - "archive_by_serial", - "get_qr", - "get_qr_by_serial" -]New value: +[ + "list", + "get", + "get_by_serial", + "compliance", + "registry_readiness", + "get_condition_flags", + "get_condition_flags_by_serial", + "set_condition_flags", + "set_condition_flags_by_serial", + "create", + "suspend", + "suspend_by_serial", + "archive", + "archive_by_serial", + "get_qr", + "get_qr_by_serial", + "list_snapshots", + "get_snapshot" +] - added
Input schema / properties / args / properties / atAdded value: +{ + "description": "list_snapshots: ISO 8601 instant — return the snapshot valid then instead of the list.", + "type": "string" +} - changed
Input schema / properties / args / properties / confirmOverage / descriptionPrevious value: -"Set true to accept a per-passport overage charge when create is over the plan quota (402)."New value: +"Set true to accept per-passport overage charges when over the plan quota (402). Applies to create." - added
Input schema / properties / args / properties / flagsAdded value: +{ + "additionalProperties": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ] + }, + "description": "For set_condition_flags / set_condition_flags_by_serial: Record<flagKey, boolean|null>. null clears the flag.", + "propertyNames": { + "type": "string" + }, + "type": "object" +} - changed
Input schema / properties / args / properties / gtin / descriptionPrevious value: -"GTIN disambiguator for *_by_serial actions when a serial isn't unique across GTINs (else 409 ambiguous_serial)."New value: +"For create (legacy): GS1 GTIN. Also used as a disambiguator for *_by_serial actions when a serial isn't unique (else 409 ambiguous_serial)." - changed
Input schema / properties / args / properties / id / descriptionPrevious value: -"Passport id. Required for get/compliance/create-result/suspend/archive/get_qr (the by-id actions)."New value: +"Passport id. Required for get/compliance/suspend/archive/get_qr (the by-id actions)." - added
Input schema / properties / args / properties / identifierAdded value: +{ + "additionalProperties": {}, + "description": "EN 18219 scheme-tagged identifier for create. Must have `scheme` plus scheme-specific fields. Schemes: gs1 {gtin, serialNumber} | iso15459 {issuingAgencyCode, primaryId, serial?} | iec61406 {uri} | did {did, method} | doi {doi, granularity} (granularity: \"model\"|\"batch\"|\"item\" REQUIRED per EN 18219 §5.6.2(b)). Battery passports: gs1 and iso15459 only.", + "propertyNames": { + "type": "string" + }, + "type": "object" +} - changed
Input schema / properties / args / properties / serialNumber / descriptionPrevious value: -"Serial for the new passport. Required for create."New value: +"Serial for the new passport (create, legacy gs1 path)." - added
Input schema / properties / args / properties / snapshotIdAdded value: +{ + "description": "Snapshot id. Required for get_snapshot.", + "type": "string" +} - added
Input schema / properties / args / properties / symbologyAdded value: +{ + "description": "get_qr/get_qr_by_serial: qr (default) | datamatrix.", + "type": "string" +}
2 tool updates
v1.8.0- Changed
tracepass_products1 field changed- changed
Input schema / properties / args / properties / category / descriptionPrevious value: -"DPP category for create: battery | textile | electronics | construction | steel | chemicals | packaging | furniture | tyres | jewelry | toys | fmcg."New value: +"DPP category for create: battery | textile | electronics | construction | steel | detergents | paints-coatings | packaging | furniture | tyres | jewelry | toys | fmcg."
- Changed
tracepass_templates1 field changed- changed
Input schema / properties / args / properties / category / descriptionPrevious value: -"DPP category to fetch (required for get): battery | textile | electronics | construction | steel | chemicals | packaging | furniture | tyres | jewelry | toys | fmcg."New value: +"DPP category to fetch (required for get): battery | textile | electronics | construction | steel | detergents | paints-coatings | packaging | furniture | tyres | jewelry | toys | fmcg."
6 tool updates
v1.7.3- Changed
tracepass_epcis7 fields changed- added
Input schema / properties / action / descriptionAdded value: +"EPCIS 2.0: export a passport's events (export | export_by_serial), capture new events, poll a capture job, or query events." - changed
Input schema / properties / action / enumPrevious value: -[ - "export", - "capture", - "capture_job", - "query" -]New value: +[ + "export", + "export_by_serial", + "capture", + "capture_job", + "query" +] - removed
Input schema / properties / args / additionalPropertiesRemoved value: -{} - changed
Input schema / properties / args / descriptionPrevious value: -"Action-specific arguments — see the description."New value: +"Arguments for the chosen action; required fields depend on `action`." - added
Input schema / properties / args / propertiesAdded value: +{ + "events": { + "description": "EPCIS 2.0 event payload (an EPCISDocument or event list). Required for capture." + }, + "gtin": { + "description": "GTIN disambiguator for export_by_serial when the serial isn't unique (else 409).", + "type": "string" + }, + "id": { + "description": "Passport id. Required for export.", + "type": "string" + }, + "jobId": { + "description": "Capture job id to poll. Required for capture_job.", + "type": "string" + }, + "params": { + "additionalProperties": { + "type": "string" + }, + "description": "EPCIS query parameters as key→value strings (query, optional).", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "serial": { + "description": "Your serial. Required for export_by_serial.", + "type": "string" + } +} - removed
Input schema / properties / args / propertyNamesRemoved value: -{ - "type": "string" -} - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "error": { + "description": "Machine-readable error code, when the API rejected the request.", + "type": "string" + }, + "id": { + "description": "The resource's TracePass id, when the response is a single entity.", + "type": "string" + }, + "items": { + "description": "The page of results, when the action is a list.", + "items": {}, + "type": "array" + }, + "limit": { + "description": "Page size (list actions).", + "type": "number" + }, + "message": { + "description": "Human-readable error or status detail, when present.", + "type": "string" + }, + "page": { + "description": "Current page number (list actions).", + "type": "number" + }, + "result": { + "description": "Wraps a non-object response body (e.g. a QR code string)." + }, + "total": { + "description": "Total matching records across all pages (list actions).", + "type": "number" + }, + "totalPages": { + "description": "Total number of pages (list actions).", + "type": "number" + } + }, + "type": "object" +}
- Changed
tracepass_passport_fields7 fields changed- added
Input schema / properties / action / descriptionAdded value: +"Update one passport field, addressed by passport id (update) or by your serial (update_by_serial)." - changed
Input schema / properties / action / enumPrevious value: -[ - "update" -]New value: +[ + "update", + "update_by_serial" +] - removed
Input schema / properties / args / additionalPropertiesRemoved value: -{} - changed
Input schema / properties / args / descriptionPrevious value: -"Action-specific arguments — see the description."New value: +"Arguments for the chosen action; required fields depend on `action`." - added
Input schema / properties / args / propertiesAdded value: +{ + "fieldKey": { + "description": "The field key to set (required).", + "type": "string" + }, + "gtin": { + "description": "GTIN disambiguator for update_by_serial when the serial isn't unique (else 409).", + "type": "string" + }, + "id": { + "description": "Passport id. Required for update.", + "type": "string" + }, + "serial": { + "description": "Your serial. Required for update_by_serial.", + "type": "string" + }, + "value": { + "description": "The new value for the field (required). Type depends on the field's dataType." + } +} - removed
Input schema / properties / args / propertyNamesRemoved value: -{ - "type": "string" -} - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "error": { + "description": "Machine-readable error code, when the API rejected the request.", + "type": "string" + }, + "id": { + "description": "The resource's TracePass id, when the response is a single entity.", + "type": "string" + }, + "items": { + "description": "The page of results, when the action is a list.", + "items": {}, + "type": "array" + }, + "limit": { + "description": "Page size (list actions).", + "type": "number" + }, + "message": { + "description": "Human-readable error or status detail, when present.", + "type": "string" + }, + "page": { + "description": "Current page number (list actions).", + "type": "number" + }, + "result": { + "description": "Wraps a non-object response body (e.g. a QR code string)." + }, + "total": { + "description": "Total matching records across all pages (list actions).", + "type": "number" + }, + "totalPages": { + "description": "Total number of pages (list actions).", + "type": "number" + } + }, + "type": "object" +}
- Changed
tracepass_passport_parties6 fields changed- added
Input schema / properties / action / descriptionAdded value: +"Set (add/replace) or remove an economic-operator party on a passport by its role." - removed
Input schema / properties / args / additionalPropertiesRemoved value: -{} - changed
Input schema / properties / args / descriptionPrevious value: -"Action-specific arguments — see the description."New value: +"Arguments for the chosen action; required fields depend on `action`." - added
Input schema / properties / args / propertiesAdded value: +{ + "country": { + "description": "Party country code (set, optional).", + "type": "string" + }, + "gln": { + "description": "GS1 Global Location Number for the party (set, optional).", + "type": "string" + }, + "id": { + "description": "Passport id (required).", + "type": "string" + }, + "legacyOperatorId": { + "description": "Your internal operator id for the party (set, optional).", + "type": "string" + }, + "legalName": { + "description": "Party legal name. Required for set.", + "type": "string" + }, + "role": { + "description": "Economic-operator role, e.g. manufacturer | importer | distributor | authorised_representative (required).", + "type": "string" + } +} - removed
Input schema / properties / args / propertyNamesRemoved value: -{ - "type": "string" -} - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "error": { + "description": "Machine-readable error code, when the API rejected the request.", + "type": "string" + }, + "id": { + "description": "The resource's TracePass id, when the response is a single entity.", + "type": "string" + }, + "items": { + "description": "The page of results, when the action is a list.", + "items": {}, + "type": "array" + }, + "limit": { + "description": "Page size (list actions).", + "type": "number" + }, + "message": { + "description": "Human-readable error or status detail, when present.", + "type": "string" + }, + "page": { + "description": "Current page number (list actions).", + "type": "number" + }, + "result": { + "description": "Wraps a non-object response body (e.g. a QR code string)." + }, + "total": { + "description": "Total matching records across all pages (list actions).", + "type": "number" + }, + "totalPages": { + "description": "Total number of pages (list actions).", + "type": "number" + } + }, + "type": "object" +}
- Changed
tracepass_passports7 fields changed- added
Input schema / properties / action / descriptionAdded value: +"Which passport operation to run. Reads: list | get | get_by_serial | compliance | registry_readiness | get_qr | get_qr_by_serial. Lifecycle: create (BILLABLE) | suspend (reversible) | archive (IRREVERSIBLE), each with a _by_serial variant." - changed
Input schema / properties / action / enumPrevious value: -[ - "list", - "get", - "get_by_serial", - "create", - "suspend", - "archive", - "get_qr" -]New value: +[ + "list", + "get", + "get_by_serial", + "compliance", + "registry_readiness", + "create", + "suspend", + "suspend_by_serial", + "archive", + "archive_by_serial", + "get_qr", + "get_qr_by_serial" +] - removed
Input schema / properties / args / additionalPropertiesRemoved value: -{} - changed
Input schema / properties / args / descriptionPrevious value: -"Action-specific arguments — see the description for each action's shape."New value: +"Arguments for the chosen action; required fields depend on `action` (see each action above)." - added
Input schema / properties / args / propertiesAdded value: +{ + "confirmOverage": { + "description": "Set true to accept a per-passport overage charge when create is over the plan quota (402).", + "type": "boolean" + }, + "format": { + "description": "get/get_by_serial: summary|full. get_qr/get_qr_by_serial: svg|png.", + "type": "string" + }, + "gtin": { + "description": "GTIN disambiguator for *_by_serial actions when a serial isn't unique across GTINs (else 409 ambiguous_serial).", + "type": "string" + }, + "id": { + "description": "Passport id. Required for get/compliance/create-result/suspend/archive/get_qr (the by-id actions).", + "type": "string" + }, + "lang": { + "description": "Resolve field values to one of the 24 EU locales server-side (get/get_by_serial).", + "type": "string" + }, + "limit": { + "description": "Page size for list, max 100.", + "type": "number" + }, + "page": { + "description": "Page number for list (1-based).", + "type": "number" + }, + "productId": { + "description": "Parent product id. Required for create.", + "type": "string" + }, + "search": { + "description": "Filter list by a search term.", + "type": "string" + }, + "serial": { + "description": "Your own serial number. Required for the *_by_serial actions.", + "type": "string" + }, + "serialNumber": { + "description": "Serial for the new passport. Required for create.", + "type": "string" + }, + "status": { + "description": "Filter list by status: draft|in_review|approved|published|suspended|expired|archived.", + "type": "string" + } +} - removed
Input schema / properties / args / propertyNamesRemoved value: -{ - "type": "string" -} - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "error": { + "description": "Machine-readable error code, when the API rejected the request.", + "type": "string" + }, + "id": { + "description": "The resource's TracePass id, when the response is a single entity.", + "type": "string" + }, + "items": { + "description": "The page of results, when the action is a list.", + "items": {}, + "type": "array" + }, + "limit": { + "description": "Page size (list actions).", + "type": "number" + }, + "message": { + "description": "Human-readable error or status detail, when present.", + "type": "string" + }, + "page": { + "description": "Current page number (list actions).", + "type": "number" + }, + "result": { + "description": "Wraps a non-object response body (e.g. a QR code string)." + }, + "total": { + "description": "Total matching records across all pages (list actions).", + "type": "number" + }, + "totalPages": { + "description": "Total number of pages (list actions).", + "type": "number" + } + }, + "type": "object" +}
- Changed
tracepass_products7 fields changed- added
Input schema / properties / action / descriptionAdded value: +"Which product operation to run: list | get | create | create_batch | update | archive." - changed
Input schema / properties / action / enumPrevious value: -[ - "list", - "get", - "create", - "update" -]New value: +[ + "list", + "get", + "create", + "create_batch", + "update", + "archive" +] - removed
Input schema / properties / args / additionalPropertiesRemoved value: -{} - changed
Input schema / properties / args / descriptionPrevious value: -"Action-specific arguments — see the description for each action's shape."New value: +"Arguments for the chosen action; required fields depend on `action` (see each action above)." - added
Input schema / properties / args / propertiesAdded value: +{ + "category": { + "description": "DPP category for create: battery | textile | electronics | construction | steel | chemicals | packaging | furniture | tyres | jewelry | toys | fmcg.", + "type": "string" + }, + "description": { + "description": "Free-text product description (create/update).", + "type": "string" + }, + "id": { + "description": "Product id. Required for get and update.", + "type": "string" + }, + "limit": { + "description": "Page size for list, max 100.", + "type": "number" + }, + "model": { + "description": "Manufacturer model / SKU. Required for create; optional on update.", + "type": "string" + }, + "name": { + "description": "Product name. Required for create; optional on update.", + "type": "string" + }, + "page": { + "description": "Page number for list (1-based).", + "type": "number" + }, + "products": { + "description": "Products to create for create_batch: [{ name, model, category, description? }], max 100.", + "items": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "type": "array" + }, + "search": { + "description": "Filter list by a search term.", + "type": "string" + }, + "status": { + "description": "Filter list by product status.", + "type": "string" + } +} - removed
Input schema / properties / args / propertyNamesRemoved value: -{ - "type": "string" -} - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "error": { + "description": "Machine-readable error code, when the API rejected the request.", + "type": "string" + }, + "id": { + "description": "The resource's TracePass id, when the response is a single entity.", + "type": "string" + }, + "items": { + "description": "The page of results, when the action is a list.", + "items": {}, + "type": "array" + }, + "limit": { + "description": "Page size (list actions).", + "type": "number" + }, + "message": { + "description": "Human-readable error or status detail, when present.", + "type": "string" + }, + "page": { + "description": "Current page number (list actions).", + "type": "number" + }, + "result": { + "description": "Wraps a non-object response body (e.g. a QR code string)." + }, + "total": { + "description": "Total matching records across all pages (list actions).", + "type": "number" + }, + "totalPages": { + "description": "Total number of pages (list actions).", + "type": "number" + } + }, + "type": "object" +}
- Changed
tracepass_templates6 fields changed- added
Input schema / properties / action / descriptionAdded value: +"List all DPP category templates, or get one template by category." - removed
Input schema / properties / args / additionalPropertiesRemoved value: -{} - changed
Input schema / properties / args / descriptionPrevious value: -"Action-specific arguments — see the description for each action's shape."New value: +"Arguments for the chosen action; `category` is required for get, ignored for list." - added
Input schema / properties / args / propertiesAdded value: +{ + "category": { + "description": "DPP category to fetch (required for get): battery | textile | electronics | construction | steel | chemicals | packaging | furniture | tyres | jewelry | toys | fmcg.", + "type": "string" + } +} - removed
Input schema / properties / args / propertyNamesRemoved value: -{ - "type": "string" -} - changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "error": { + "description": "Machine-readable error code, when the API rejected the request.", + "type": "string" + }, + "id": { + "description": "The resource's TracePass id, when the response is a single entity.", + "type": "string" + }, + "items": { + "description": "The page of results, when the action is a list.", + "items": {}, + "type": "array" + }, + "limit": { + "description": "Page size (list actions).", + "type": "number" + }, + "message": { + "description": "Human-readable error or status detail, when present.", + "type": "string" + }, + "page": { + "description": "Current page number (list actions).", + "type": "number" + }, + "result": { + "description": "Wraps a non-object response body (e.g. a QR code string)." + }, + "total": { + "description": "Total matching records across all pages (list actions).", + "type": "number" + }, + "totalPages": { + "description": "Total number of pages (list actions).", + "type": "number" + } + }, + "type": "object" +}
6 tool updates
v1.1.4- First observed
tracepass_epcis - First observed
tracepass_passport_fields - First observed
tracepass_passport_parties - First observed
tracepass_passports - First observed
tracepass_products - First observed
tracepass_templates
TDQS
Scored across 7 tools
The seven top-level tools map to clearly distinct domains: product catalogue, passport lifecycle, passport fields, economic-operator parties, EPCIS events, template schemas, and legal scope. Boundary risk is mostly inside tracepass_passports, whose many paired *_by_serial actions overlap conceptually with their by-id siblings, though the descriptions explicitly disambiguate (serial uniqueness, 409 ambiguous_serial, GTIN disambiguation).
All tools share the tracepass_ prefix and a consistent resource-noun layout, with actions expressed uniformly as verbs via the action parameter. Minor deviations: plural 'products'/'passports' vs singular 'passport_fields'/'passport_parties', and parties uses set/remove while fields uses update, but the pattern is still highly predictable.
Seven top-level tools is well within the ideal 3-15 range for a DPP platform and each tool earns its place. However tracepass_passports concentrates roughly 25 actions (list/get/QR/snapshots/condition flags/measurements/compliance/registry/place_on_market plus many *_by_serial variants), making that one tool disproportionately heavy.
The surface covers products, passport create/read/lifecycle, field updates, parties, EPCIS capture/query/export, templates, and scope checking, which is broad lifecycle coverage. Gaps remain: publishing is explicitly out of band (dashboard only), products have archive but no unarchive, and parties offer set/remove but no dedicated read action.
Maintenance
Related MCP Connectors
MCP server for Boson Protocol — on-chain agentic commerce for physical & digital goods.
MCP server for Product Management
MCP Server for agents to onboard, pay, and provision services autonomously with InFlow
Demo banking MCP server: accounts, transactions and payments. OAuth 2.1 with DPoP.
Related MCP Servers
- AlicenseAqualityAmaintenanceModel Context Protocol (MCP) server for French Electronic Invoicing (NF XP Z12-013). Provide tools to validate, generate, and explore API specifications for PDP/OD interoperability.34Apache 2.0
- AlicenseNot gradedqualityCmaintenanceProvides a sovereign, MIT-licensed MCP server for professional-service workflows, running entirely on your infrastructure with Ed25519 cryptographic signing for every action.MIT
- AlicenseNot gradedqualityDmaintenanceA hosted remote MCP server for verifying C2PA intakes, classifying source risk, issuing media receipts, and exporting intake logs. Designed for AI governance, trust and safety, and compliance teams.MIT
- AlicenseAqualityAmaintenanceMCP server for GoCertius: certified evidence, dossiers, notifications and chats via AI agents.4164 npmMIT