tracepass-mcp-server 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,96 @@
1
+ /**
2
+ * MCP resources for the TracePass server.
3
+ *
4
+ * Resources differ from tools: a tool is something the model
5
+ * *calls*; a resource is data the user (or client) *attaches as
6
+ * context*. For TracePass that's read-only entity data — a product,
7
+ * a passport, its EPCIS event history — addressed by a `tracepass://`
8
+ * URI so a user can drop "this passport" into a conversation.
9
+ *
10
+ * Two kinds, both registered here:
11
+ * - a STATIC resource — `tracepass://products` — the catalogue;
12
+ * - RESOURCE TEMPLATES — `tracepass://passport/{id}` etc. —
13
+ * parameterised URIs the client can complete.
14
+ *
15
+ * All resource reads are read-only and go through the same v1 API
16
+ * client as the tools, so auth / plan-gating / rate-limits behave
17
+ * identically.
18
+ */
19
+ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
20
+ /** Build a ReadResourceResult `contents` entry carrying JSON text. */
21
+ function jsonContents(uri, data) {
22
+ return {
23
+ contents: [
24
+ {
25
+ uri,
26
+ mimeType: "application/json",
27
+ text: JSON.stringify(data, null, 2),
28
+ },
29
+ ],
30
+ };
31
+ }
32
+ /** Build a contents entry for an error — resources can't return an
33
+ * isError flag, so an unreadable resource yields a JSON error body
34
+ * the model can still read. */
35
+ function errorContents(uri, status, body) {
36
+ return jsonContents(uri, {
37
+ error: `TracePass API returned ${status}`,
38
+ detail: body,
39
+ });
40
+ }
41
+ /**
42
+ * Register every TracePass resource + resource template on the
43
+ * server, bound to a v1 API client.
44
+ */
45
+ export function registerResources(server, client) {
46
+ // ── Static resource: the product catalogue ────────────────────
47
+ server.registerResource("products", "tracepass://products", {
48
+ title: "Product catalogue",
49
+ description: "The account's TracePass product catalogue (first page). Attach this for an overview of what products exist.",
50
+ mimeType: "application/json",
51
+ }, async (uri) => {
52
+ const res = await client.get("/api/v1/products?limit=100");
53
+ return res.ok
54
+ ? jsonContents(uri.href, res.body)
55
+ : errorContents(uri.href, res.status, res.body);
56
+ });
57
+ // ── Template: a product by id ─────────────────────────────────
58
+ server.registerResource("product", new ResourceTemplate("tracepass://product/{id}", { list: undefined }), {
59
+ title: "Product",
60
+ description: "One TracePass product by its id — tracepass://product/{id}.",
61
+ mimeType: "application/json",
62
+ }, async (uri, variables) => {
63
+ const id = String(variables.id);
64
+ const res = await client.get(`/api/v1/products/${encodeURIComponent(id)}`);
65
+ return res.ok
66
+ ? jsonContents(uri.href, res.body)
67
+ : errorContents(uri.href, res.status, res.body);
68
+ });
69
+ // ── Template: a passport by id ────────────────────────────────
70
+ server.registerResource("passport", new ResourceTemplate("tracepass://passport/{id}", { list: undefined }), {
71
+ title: "Digital Product Passport",
72
+ description: "One DPP by its id, full field detail — tracepass://passport/{id}. Attach this to give the model a passport's complete state.",
73
+ mimeType: "application/json",
74
+ }, async (uri, variables) => {
75
+ const id = String(variables.id);
76
+ const res = await client.get(`/api/v1/passports/${encodeURIComponent(id)}?format=full`);
77
+ return res.ok
78
+ ? jsonContents(uri.href, res.body)
79
+ : errorContents(uri.href, res.status, res.body);
80
+ });
81
+ // ── Template: a passport's EPCIS event history ────────────────
82
+ server.registerResource("passport-epcis", new ResourceTemplate("tracepass://passport/{id}/epcis", {
83
+ list: undefined,
84
+ }), {
85
+ title: "Passport EPCIS events",
86
+ description: "A passport's supply-chain event history as an EPCIS 2.0 JSON-LD document — tracepass://passport/{id}/epcis.",
87
+ mimeType: "application/ld+json",
88
+ }, async (uri, variables) => {
89
+ const id = String(variables.id);
90
+ const res = await client.get(`/api/v1/passports/${encodeURIComponent(id)}/epcis`);
91
+ return res.ok
92
+ ? jsonContents(uri.href, res.body)
93
+ : errorContents(uri.href, res.status, res.body);
94
+ });
95
+ }
96
+ //# sourceMappingURL=resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAI3E,sEAAsE;AACtE,SAAS,YAAY,CAAC,GAAW,EAAE,IAAa;IAC9C,OAAO;QACL,QAAQ,EAAE;YACR;gBACE,GAAG;gBACH,QAAQ,EAAE,kBAAkB;gBAC5B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;aACpC;SACF;KACF,CAAC;AACJ,CAAC;AAED;;gCAEgC;AAChC,SAAS,aAAa,CAAC,GAAW,EAAE,MAAc,EAAE,IAAa;IAC/D,OAAO,YAAY,CAAC,GAAG,EAAE;QACvB,KAAK,EAAE,0BAA0B,MAAM,EAAE;QACzC,MAAM,EAAE,IAAI;KACb,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAiB,EACjB,MAAuB;IAEvB,iEAAiE;IACjE,MAAM,CAAC,gBAAgB,CACrB,UAAU,EACV,sBAAsB,EACtB;QACE,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EACT,6GAA6G;QAC/G,QAAQ,EAAE,kBAAkB;KAC7B,EACD,KAAK,EAAE,GAAG,EAAE,EAAE;QACZ,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAC3D,OAAO,GAAG,CAAC,EAAE;YACX,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC;YAClC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACpD,CAAC,CACF,CAAC;IAEF,iEAAiE;IACjE,MAAM,CAAC,gBAAgB,CACrB,SAAS,EACT,IAAI,gBAAgB,CAAC,0BAA0B,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EACrE;QACE,KAAK,EAAE,SAAS;QAChB,WAAW,EACT,6DAA6D;QAC/D,QAAQ,EAAE,kBAAkB;KAC7B,EACD,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE;QACvB,MAAM,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,oBAAoB,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3E,OAAO,GAAG,CAAC,EAAE;YACX,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC;YAClC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACpD,CAAC,CACF,CAAC;IAEF,iEAAiE;IACjE,MAAM,CAAC,gBAAgB,CACrB,UAAU,EACV,IAAI,gBAAgB,CAAC,2BAA2B,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EACtE;QACE,KAAK,EAAE,0BAA0B;QACjC,WAAW,EACT,8HAA8H;QAChI,QAAQ,EAAE,kBAAkB;KAC7B,EACD,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE;QACvB,MAAM,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAC1B,qBAAqB,kBAAkB,CAAC,EAAE,CAAC,cAAc,CAC1D,CAAC;QACF,OAAO,GAAG,CAAC,EAAE;YACX,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC;YAClC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACpD,CAAC,CACF,CAAC;IAEF,iEAAiE;IACjE,MAAM,CAAC,gBAAgB,CACrB,gBAAgB,EAChB,IAAI,gBAAgB,CAAC,iCAAiC,EAAE;QACtD,IAAI,EAAE,SAAS;KAChB,CAAC,EACF;QACE,KAAK,EAAE,uBAAuB;QAC9B,WAAW,EACT,6GAA6G;QAC/G,QAAQ,EAAE,qBAAqB;KAChC,EACD,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE;QACvB,MAAM,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAC1B,qBAAqB,kBAAkB,CAAC,EAAE,CAAC,QAAQ,CACpD,CAAC;QACF,OAAO,GAAG,CAAC,EAAE;YACX,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC;YAClC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACpD,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Tool-result shaping for the MCP server.
3
+ *
4
+ * Every MCP tool returns a `CallToolResult` — `{ content: [...],
5
+ * isError? }`. This module gives the tools one consistent way to
6
+ * turn a `TracePassApiResponse` into that shape, and — critically —
7
+ * to translate the v1 API's *meaningful* non-2xx responses into
8
+ * results an AI agent can read and act on, rather than opaque
9
+ * errors.
10
+ *
11
+ * The three responses an agent MUST be able to act on:
12
+ * - 402 overage_required — the write would exceed the plan's DPP
13
+ * quota; the agent can retry with `confirmOverage: true`.
14
+ * - 403 plan-gated (e.g. epcis_capture_not_available) — the
15
+ * feature isn't on the tenant's plan; the agent should tell the
16
+ * user, not retry.
17
+ * - 429 rate-limited — the daily write/read budget is spent; retry
18
+ * tomorrow.
19
+ * Surfacing these as readable `isError: true` text (not a thrown
20
+ * exception) lets the model explain the situation to the user.
21
+ */
22
+ /** The MCP CallToolResult shape (text content only — sufficient for
23
+ * every TracePass tool; no tool returns binary). */
24
+ export interface ToolResult {
25
+ content: Array<{
26
+ type: "text";
27
+ text: string;
28
+ }>;
29
+ isError?: boolean;
30
+ structuredContent?: Record<string, unknown>;
31
+ }
32
+ /** A plain text result. */
33
+ export declare function textResult(text: string): ToolResult;
34
+ /** A successful result carrying JSON data — pretty-printed so the
35
+ * model reads it cleanly, plus structuredContent for clients that
36
+ * consume it. */
37
+ export declare function jsonResult(data: unknown): ToolResult;
38
+ /** An error result — `isError: true` so the client renders it as a
39
+ * failed tool call, with a human-readable explanation. */
40
+ export declare function errorResult(message: string): ToolResult;
41
+ /**
42
+ * Turn a v1 API response into a ToolResult. 2xx → jsonResult. The
43
+ * meaningful non-2xx cases get a specific, actionable explanation;
44
+ * anything else gets a generic error carrying the status + body.
45
+ */
46
+ export declare function apiResult(res: {
47
+ status: number;
48
+ ok: boolean;
49
+ body: unknown;
50
+ }): ToolResult;
51
+ //# sourceMappingURL=result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH;qDACqD;AACrD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7C;AAED,2BAA2B;AAC3B,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAEnD;AAED;;kBAEkB;AAClB,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,CAQpD;AAED;2DAC2D;AAC3D,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,OAAO,CAAC;IACZ,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,UAAU,CA8Cb"}
package/dist/result.js ADDED
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Tool-result shaping for the MCP server.
3
+ *
4
+ * Every MCP tool returns a `CallToolResult` — `{ content: [...],
5
+ * isError? }`. This module gives the tools one consistent way to
6
+ * turn a `TracePassApiResponse` into that shape, and — critically —
7
+ * to translate the v1 API's *meaningful* non-2xx responses into
8
+ * results an AI agent can read and act on, rather than opaque
9
+ * errors.
10
+ *
11
+ * The three responses an agent MUST be able to act on:
12
+ * - 402 overage_required — the write would exceed the plan's DPP
13
+ * quota; the agent can retry with `confirmOverage: true`.
14
+ * - 403 plan-gated (e.g. epcis_capture_not_available) — the
15
+ * feature isn't on the tenant's plan; the agent should tell the
16
+ * user, not retry.
17
+ * - 429 rate-limited — the daily write/read budget is spent; retry
18
+ * tomorrow.
19
+ * Surfacing these as readable `isError: true` text (not a thrown
20
+ * exception) lets the model explain the situation to the user.
21
+ */
22
+ /** A plain text result. */
23
+ export function textResult(text) {
24
+ return { content: [{ type: "text", text }] };
25
+ }
26
+ /** A successful result carrying JSON data — pretty-printed so the
27
+ * model reads it cleanly, plus structuredContent for clients that
28
+ * consume it. */
29
+ export function jsonResult(data) {
30
+ return {
31
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
32
+ structuredContent: data && typeof data === "object" && !Array.isArray(data)
33
+ ? data
34
+ : { result: data },
35
+ };
36
+ }
37
+ /** An error result — `isError: true` so the client renders it as a
38
+ * failed tool call, with a human-readable explanation. */
39
+ export function errorResult(message) {
40
+ return { content: [{ type: "text", text: message }], isError: true };
41
+ }
42
+ /**
43
+ * Turn a v1 API response into a ToolResult. 2xx → jsonResult. The
44
+ * meaningful non-2xx cases get a specific, actionable explanation;
45
+ * anything else gets a generic error carrying the status + body.
46
+ */
47
+ export function apiResult(res) {
48
+ if (res.ok)
49
+ return jsonResult(res.body);
50
+ const body = (res.body ?? {});
51
+ const errorCode = typeof body.error === "string" ? body.error : undefined;
52
+ const message = typeof body.message === "string" ? body.message : undefined;
53
+ switch (res.status) {
54
+ case 401:
55
+ return errorResult("Authentication failed — the TracePass API key is missing or invalid. Check the key configured for this MCP server.");
56
+ case 402:
57
+ // The overage flow. The v1 create-passport routes return this
58
+ // with the plan limit + per-DPP price; the agent can re-call
59
+ // the same tool with confirmOverage: true to accept the charge.
60
+ return errorResult(`This action would exceed the plan's included quota. ${message ?? ""}`.trim() +
61
+ " Re-run the tool with confirmOverage: true to accept the per-passport overage charge, or tell the user they've hit their plan limit.");
62
+ case 403:
63
+ return errorResult(`Not permitted on this account's plan${errorCode ? ` (${errorCode})` : ""}. ${message ?? "This feature may require a higher plan or a paid add-on."}`.trim() +
64
+ " Do not retry — tell the user this needs a plan change.");
65
+ case 404:
66
+ return errorResult(`Not found — ${message ?? "no resource matches that id or serial."}`);
67
+ case 409:
68
+ return errorResult(`Conflict — ${message ?? "this would collide with existing data (e.g. a duplicate GTIN or serial)."}`);
69
+ case 422:
70
+ return errorResult(`Validation error — ${message ?? "the request payload was rejected. Check the tool arguments against the schema."}`);
71
+ case 429:
72
+ return errorResult("Rate limit reached — the account's daily API budget is spent. It resets at 00:00 UTC; retry then.");
73
+ default:
74
+ return errorResult(`TracePass API returned ${res.status}. ${message ?? errorCode ?? JSON.stringify(res.body)}`);
75
+ }
76
+ }
77
+ //# sourceMappingURL=result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"result.js","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAUH,2BAA2B;AAC3B,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AAC/C,CAAC;AAED;;kBAEkB;AAClB,MAAM,UAAU,UAAU,CAAC,IAAa;IACtC,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QAChE,iBAAiB,EACf,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YACtD,CAAC,CAAE,IAAgC;YACnC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE;KACvB,CAAC;AACJ,CAAC;AAED;2DAC2D;AAC3D,MAAM,UAAU,WAAW,CAAC,OAAe;IACzC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACvE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,GAIzB;IACC,IAAI,GAAG,CAAC,EAAE;QAAE,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAExC,MAAM,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAA4B,CAAC;IACzD,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1E,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAE5E,QAAQ,GAAG,CAAC,MAAM,EAAE,CAAC;QACnB,KAAK,GAAG;YACN,OAAO,WAAW,CAChB,oHAAoH,CACrH,CAAC;QACJ,KAAK,GAAG;YACN,8DAA8D;YAC9D,6DAA6D;YAC7D,gEAAgE;YAChE,OAAO,WAAW,CAChB,uDAAuD,OAAO,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE;gBAC3E,sIAAsI,CACzI,CAAC;QACJ,KAAK,GAAG;YACN,OAAO,WAAW,CAChB,uCAAuC,SAAS,CAAC,CAAC,CAAC,KAAK,SAAS,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,OAAO,IAAI,0DAA0D,EAAE,CAAC,IAAI,EAAE;gBAC1J,yDAAyD,CAC5D,CAAC;QACJ,KAAK,GAAG;YACN,OAAO,WAAW,CAChB,eAAe,OAAO,IAAI,wCAAwC,EAAE,CACrE,CAAC;QACJ,KAAK,GAAG;YACN,OAAO,WAAW,CAChB,cAAc,OAAO,IAAI,0EAA0E,EAAE,CACtG,CAAC;QACJ,KAAK,GAAG;YACN,OAAO,WAAW,CAChB,sBAAsB,OAAO,IAAI,gFAAgF,EAAE,CACpH,CAAC;QACJ,KAAK,GAAG;YACN,OAAO,WAAW,CAChB,mGAAmG,CACpG,CAAC;QACJ;YACE,OAAO,WAAW,CAChB,0BAA0B,GAAG,CAAC,MAAM,KAAK,OAAO,IAAI,SAAS,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAC5F,CAAC;IACN,CAAC;AACH,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The TracePass MCP server factory.
3
+ *
4
+ * `createMcpServer(config)` builds an `McpServer` with every
5
+ * TracePass tool registered, bound to an API client. It is
6
+ * TRANSPORT-AGNOSTIC — it knows nothing about stdio vs HTTP. The
7
+ * caller connects it to a transport:
8
+ * - the hosted `/mcp` route → WebStandardStreamableHTTPServerTransport
9
+ * - the standalone npm package → StdioServerTransport
10
+ *
11
+ * One factory, every transport — so the tool surface can never
12
+ * drift between the hosted endpoint and the local package.
13
+ */
14
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
15
+ /** Server identity reported to MCP clients. Bump `version` on a
16
+ * meaningful tool-surface change. */
17
+ export declare const MCP_SERVER_INFO: {
18
+ readonly name: "tracepass";
19
+ readonly version: "1.0.0";
20
+ };
21
+ export interface CreateMcpServerConfig {
22
+ /** Base URL of the TracePass app — e.g. "https://app.tracepass.eu".
23
+ * The hosted route passes its own origin; the npm package passes
24
+ * the public URL (or a customer override). */
25
+ baseUrl: string;
26
+ /** The caller's tp_ API key — every v1 call is made as this key. */
27
+ apiKey: string;
28
+ }
29
+ /**
30
+ * Build a fully-wired TracePass `McpServer`. The caller is
31
+ * responsible for connecting it to a transport via `.connect()`.
32
+ */
33
+ export declare function createMcpServer(config: CreateMcpServerConfig): McpServer;
34
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOpE;sCACsC;AACtC,eAAO,MAAM,eAAe;;;CAGlB,CAAC;AAEX,MAAM,WAAW,qBAAqB;IACpC;;mDAE+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,SAAS,CAqDxE"}
package/dist/server.js ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The TracePass MCP server factory.
3
+ *
4
+ * `createMcpServer(config)` builds an `McpServer` with every
5
+ * TracePass tool registered, bound to an API client. It is
6
+ * TRANSPORT-AGNOSTIC — it knows nothing about stdio vs HTTP. The
7
+ * caller connects it to a transport:
8
+ * - the hosted `/mcp` route → WebStandardStreamableHTTPServerTransport
9
+ * - the standalone npm package → StdioServerTransport
10
+ *
11
+ * One factory, every transport — so the tool surface can never
12
+ * drift between the hosted endpoint and the local package.
13
+ */
14
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
15
+ import { TracePassClient } from "./api-client.js";
16
+ import { buildTools } from "./tools.js";
17
+ import { registerResources } from "./resources.js";
18
+ import { registerPrompts } from "./prompts.js";
19
+ import { errorResult } from "./result.js";
20
+ /** Server identity reported to MCP clients. Bump `version` on a
21
+ * meaningful tool-surface change. */
22
+ export const MCP_SERVER_INFO = {
23
+ name: "tracepass",
24
+ version: "1.0.0",
25
+ };
26
+ /**
27
+ * Build a fully-wired TracePass `McpServer`. The caller is
28
+ * responsible for connecting it to a transport via `.connect()`.
29
+ */
30
+ export function createMcpServer(config) {
31
+ const server = new McpServer(MCP_SERVER_INFO, {
32
+ instructions: "Tools for the TracePass Digital Product Passport platform. " +
33
+ "Reads are free; writes that create passports are billable and " +
34
+ "consume plan quota — never create passports in bulk or accept " +
35
+ "an overage charge without the user's explicit consent. " +
36
+ "archive_passport is irreversible; prefer suspend_passport when " +
37
+ "a change might need undoing.",
38
+ });
39
+ const client = new TracePassClient({
40
+ baseUrl: config.baseUrl,
41
+ apiKey: config.apiKey,
42
+ });
43
+ for (const tool of buildTools(client)) {
44
+ // The SDK passes validated args (typed from the Zod shape) as the
45
+ // first param + a RequestHandlerExtra second param we don't use.
46
+ // A handler that throws would surface as an opaque protocol
47
+ // error, so we catch and convert to a readable isError result.
48
+ // The result is cast to the SDK's CallToolResult — our ToolResult
49
+ // is a structurally-compatible subset (text content only).
50
+ const cb = async (args) => {
51
+ try {
52
+ return await tool.handler(args);
53
+ }
54
+ catch (err) {
55
+ return errorResult(`Tool "${tool.name}" failed: ${err instanceof Error ? err.message : String(err)}`);
56
+ }
57
+ };
58
+ server.registerTool(tool.name, {
59
+ title: tool.title,
60
+ description: tool.description,
61
+ inputSchema: tool.inputSchema,
62
+ annotations: tool.annotations,
63
+ },
64
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
+ cb);
66
+ }
67
+ // Resources (entity data the user attaches as context) + prompts
68
+ // (reusable DPP workflows the client surfaces as slash-commands).
69
+ // `ping` and capability negotiation are handled by the SDK itself
70
+ // once a transport is connected — no wiring needed.
71
+ registerResources(server, client);
72
+ registerPrompts(server);
73
+ return server;
74
+ }
75
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C;sCACsC;AACtC,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,IAAI,EAAE,WAAW;IACjB,OAAO,EAAE,OAAO;CACR,CAAC;AAWX;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,MAA6B;IAC3D,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,eAAe,EAAE;QAC5C,YAAY,EACV,6DAA6D;YAC7D,gEAAgE;YAChE,gEAAgE;YAChE,yDAAyD;YACzD,iEAAiE;YACjE,8BAA8B;KACjC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM;KACtB,CAAC,CAAC;IAEH,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,kEAAkE;QAClE,iEAAiE;QACjE,4DAA4D;QAC5D,+DAA+D;QAC/D,kEAAkE;QAClE,2DAA2D;QAC3D,MAAM,EAAE,GAAG,KAAK,EAAE,IAA6B,EAAE,EAAE;YACjD,IAAI,CAAC;gBACH,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAClC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,WAAW,CAChB,SAAS,IAAI,CAAC,IAAI,aAAa,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAClF,CAAC;YACJ,CAAC;QACH,CAAC,CAAC;QACF,MAAM,CAAC,YAAY,CACjB,IAAI,CAAC,IAAI,EACT;YACE,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B;QACD,8DAA8D;QAC9D,EAAS,CACV,CAAC;IACJ,CAAC;IAED,iEAAiE;IACjE,kEAAkE;IAClE,kEAAkE;IAClE,oDAAoD;IACpD,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,eAAe,CAAC,MAAM,CAAC,CAAC;IAExB,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * stdio entrypoint — the local / npx form of the TracePass MCP
4
+ * server.
5
+ *
6
+ * An MCP client (Claude Desktop, Cursor, an IDE agent) launches this
7
+ * as a subprocess and speaks MCP over stdin/stdout. The API key and
8
+ * the TracePass base URL come from environment variables the client
9
+ * sets in its server config:
10
+ *
11
+ * TRACEPASS_API_KEY (required) — a tp_ API key
12
+ * TRACEPASS_BASE_URL (optional) — defaults to https://app.tracepass.eu
13
+ *
14
+ * This is the SAME server core (`createMcpServer`) the hosted HTTP
15
+ * service uses — only the transport differs.
16
+ */
17
+ export {};
18
+ //# sourceMappingURL=stdio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;GAcG"}
package/dist/stdio.js ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * stdio entrypoint — the local / npx form of the TracePass MCP
4
+ * server.
5
+ *
6
+ * An MCP client (Claude Desktop, Cursor, an IDE agent) launches this
7
+ * as a subprocess and speaks MCP over stdin/stdout. The API key and
8
+ * the TracePass base URL come from environment variables the client
9
+ * sets in its server config:
10
+ *
11
+ * TRACEPASS_API_KEY (required) — a tp_ API key
12
+ * TRACEPASS_BASE_URL (optional) — defaults to https://app.tracepass.eu
13
+ *
14
+ * This is the SAME server core (`createMcpServer`) the hosted HTTP
15
+ * service uses — only the transport differs.
16
+ */
17
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
18
+ import { createMcpServer } from "./server.js";
19
+ const DEFAULT_BASE_URL = "https://app.tracepass.eu";
20
+ async function main() {
21
+ const apiKey = process.env.TRACEPASS_API_KEY;
22
+ if (!apiKey || apiKey.trim() === "") {
23
+ // Write to stderr — stdout is the MCP protocol channel and must
24
+ // not carry anything but JSON-RPC.
25
+ process.stderr.write("tracepass-mcp-server: TRACEPASS_API_KEY is not set. " +
26
+ "Add it to the MCP server's env config (a tp_ key from " +
27
+ "the TracePass dashboard → Developer → API Keys).\n");
28
+ process.exit(1);
29
+ }
30
+ const baseUrl = process.env.TRACEPASS_BASE_URL?.trim() || DEFAULT_BASE_URL;
31
+ const server = createMcpServer({ apiKey, baseUrl });
32
+ const transport = new StdioServerTransport();
33
+ await server.connect(transport);
34
+ // The process now stays alive serving MCP over stdio until the
35
+ // client closes the pipe.
36
+ }
37
+ main().catch((err) => {
38
+ process.stderr.write(`tracepass-mcp-server: fatal — ${err instanceof Error ? err.message : String(err)}\n`);
39
+ process.exit(1);
40
+ });
41
+ //# sourceMappingURL=stdio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stdio.js","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,gBAAgB,GAAG,0BAA0B,CAAC;AAEpD,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAC7C,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACpC,gEAAgE;QAChE,mCAAmC;QACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,sDAAsD;YACpD,wDAAwD;YACxD,oDAAoD,CACvD,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,IAAI,gBAAgB,CAAC;IAE3E,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IACpD,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,+DAA+D;IAC/D,0BAA0B;AAC5B,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,iCAAiC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CACtF,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * MCP tool definitions for the TracePass v1 API.
3
+ *
4
+ * The v1 surface has ~23 endpoints. Exposing 23 flat MCP tools would
5
+ * swamp the model's tool list and slow tool selection. Instead the
6
+ * surface is grouped into FIVE resource tools, each taking:
7
+ * - `action` — a required enum naming the operation;
8
+ * - `args` — an object whose required shape DEPENDS on `action`.
9
+ *
10
+ * MCP's `inputSchema` is one Zod shape per tool and can't natively
11
+ * branch on `action`. So `args` is declared permissively, and each
12
+ * handler validates `args` against the SPECIFIC per-action Zod
13
+ * schema — the per-action rigor is kept, it just lives in the
14
+ * handler. A model that omits a required arg gets a precise error
15
+ * (`ACTION_SCHEMAS` powers both the validation and the messages).
16
+ *
17
+ * Every tool's `description` documents each action and its `args`.
18
+ * `annotations` carry MCP hint flags at the tool level; per-action
19
+ * risk (billable / irreversible) is spelled out in the description
20
+ * so the model warns the user before a destructive action.
21
+ *
22
+ * Transport-agnostic: `buildTools(client)` binds the handlers to a
23
+ * `TracePassClient`; the server factory registers them.
24
+ */
25
+ import { z } from "zod";
26
+ import type { TracePassClient } from "./api-client.js";
27
+ import { type ToolResult } from "./result.js";
28
+ export interface McpToolDefinition {
29
+ name: string;
30
+ title: string;
31
+ description: string;
32
+ inputSchema: z.ZodRawShape;
33
+ annotations: {
34
+ readOnlyHint?: boolean;
35
+ destructiveHint?: boolean;
36
+ idempotentHint?: boolean;
37
+ };
38
+ handler: (args: Record<string, unknown>) => Promise<ToolResult>;
39
+ }
40
+ /**
41
+ * Build the 5 grouped tools, bound to a TracePass API client.
42
+ */
43
+ export declare function buildTools(client: TracePassClient): McpToolDefinition[];
44
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAA0B,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAEtE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,CAAC,CAAC,WAAW,CAAC;IAC3B,WAAW,EAAE;QACX,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;KAC1B,CAAC;IACF,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CACjE;AAmID;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,eAAe,GAAG,iBAAiB,EAAE,CA2PvE"}