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.
- package/LICENSE.md +21 -0
- package/README.md +124 -0
- package/dist/api-client.d.ts +60 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +87 -0
- package/dist/api-client.js.map +1 -0
- package/dist/http.d.ts +23 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +138 -0
- package/dist/http.js.map +1 -0
- package/dist/prompts.d.ts +23 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +82 -0
- package/dist/prompts.js.map +1 -0
- package/dist/resources.d.ts +26 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +96 -0
- package/dist/resources.js.map +1 -0
- package/dist/result.d.ts +51 -0
- package/dist/result.d.ts.map +1 -0
- package/dist/result.js +77 -0
- package/dist/result.js.map +1 -0
- package/dist/server.d.ts +34 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +75 -0
- package/dist/server.js.map +1 -0
- package/dist/stdio.d.ts +18 -0
- package/dist/stdio.d.ts.map +1 -0
- package/dist/stdio.js +41 -0
- package/dist/stdio.js.map +1 -0
- package/dist/tools.d.ts +44 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +386 -0
- package/dist/tools.js.map +1 -0
- package/package.json +65 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 TracePass LTD
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# TracePass MCP Server
|
|
2
|
+
|
|
3
|
+
A [Model Context Protocol](https://modelcontextprotocol.io) server for
|
|
4
|
+
**[TracePass](https://www.tracepass.eu)** — the EU Digital Product
|
|
5
|
+
Passport platform. It lets AI assistants (Claude, Cursor, IDE agents)
|
|
6
|
+
manage products, Digital Product Passports, economic-operator parties,
|
|
7
|
+
and GS1 EPCIS 2.0 supply-chain events.
|
|
8
|
+
|
|
9
|
+
It speaks the full MCP protocol — **tools**, **resources**, **resource
|
|
10
|
+
templates**, and **prompts**.
|
|
11
|
+
|
|
12
|
+
## Two ways to use it
|
|
13
|
+
|
|
14
|
+
The same server core ships two ways:
|
|
15
|
+
|
|
16
|
+
1. **Hosted** — point your MCP client at `https://ai.tracepass.eu/mcp`.
|
|
17
|
+
Nothing to install; always current.
|
|
18
|
+
2. **Local (npm)** — run `tracepass-mcp-server` via `npx`. The MCP
|
|
19
|
+
client launches it as a subprocess and speaks MCP over stdio.
|
|
20
|
+
|
|
21
|
+
Both need a TracePass API key — mint one in the dashboard under
|
|
22
|
+
**Developer → API Keys** (a `tp_…` key).
|
|
23
|
+
|
|
24
|
+
## Configuration
|
|
25
|
+
|
|
26
|
+
### Hosted
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"mcpServers": {
|
|
31
|
+
"tracepass": {
|
|
32
|
+
"url": "https://ai.tracepass.eu/mcp",
|
|
33
|
+
"headers": { "Authorization": "Bearer tp_YOUR_KEY" }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Local (npx / stdio)
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"tracepass": {
|
|
45
|
+
"command": "npx",
|
|
46
|
+
"args": ["-y", "tracepass-mcp-server"],
|
|
47
|
+
"env": {
|
|
48
|
+
"TRACEPASS_API_KEY": "tp_YOUR_KEY"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Optional env var: `TRACEPASS_BASE_URL` (defaults to
|
|
56
|
+
`https://app.tracepass.eu`) — point the tools at a different
|
|
57
|
+
TracePass deployment.
|
|
58
|
+
|
|
59
|
+
## Tools
|
|
60
|
+
|
|
61
|
+
The ~23 TracePass v1 API operations are grouped into **5 tools**,
|
|
62
|
+
each taking an `action` plus action-specific `args`:
|
|
63
|
+
|
|
64
|
+
| Tool | Actions |
|
|
65
|
+
|------|---------|
|
|
66
|
+
| `tracepass_products` | `list`, `get`, `create`, `update` |
|
|
67
|
+
| `tracepass_passports` | `list`, `get`, `get_by_serial`, `create`, `suspend`, `archive`, `get_qr` |
|
|
68
|
+
| `tracepass_passport_fields` | `update` |
|
|
69
|
+
| `tracepass_passport_parties` | `set`, `remove` |
|
|
70
|
+
| `tracepass_epcis` | `export`, `capture`, `capture_job`, `query` |
|
|
71
|
+
|
|
72
|
+
### A note on writes
|
|
73
|
+
|
|
74
|
+
Some actions **cost money or are irreversible** — the server's tool
|
|
75
|
+
descriptions tell the model so:
|
|
76
|
+
|
|
77
|
+
- **`tracepass_passports` `create`** consumes a billable DPP slot on
|
|
78
|
+
the account's plan. Over-quota creation incurs a per-passport
|
|
79
|
+
overage charge; the tool surfaces a 402-style message and only
|
|
80
|
+
proceeds with `args.confirmOverage: true` after the user agrees.
|
|
81
|
+
- **`tracepass_passports` `archive`** is irreversible — the public QR
|
|
82
|
+
permanently 404s. Use `suspend` (reversible) when a change might be
|
|
83
|
+
undone.
|
|
84
|
+
- **`tracepass_epcis` `capture` / `query`** require the paid EPCIS
|
|
85
|
+
add-on; `export` is included on Starter plans and up.
|
|
86
|
+
|
|
87
|
+
## Resources
|
|
88
|
+
|
|
89
|
+
Read-only entity data you can attach as conversation context:
|
|
90
|
+
|
|
91
|
+
- `tracepass://products` — the product catalogue
|
|
92
|
+
- `tracepass://product/{id}` — one product
|
|
93
|
+
- `tracepass://passport/{id}` — one passport, full field detail
|
|
94
|
+
- `tracepass://passport/{id}/epcis` — a passport's EPCIS 2.0 events
|
|
95
|
+
|
|
96
|
+
## Prompts
|
|
97
|
+
|
|
98
|
+
Reusable DPP workflows the client surfaces as slash-commands:
|
|
99
|
+
|
|
100
|
+
- `audit_passport` — review a passport for completeness and
|
|
101
|
+
compliance readiness
|
|
102
|
+
- `onboard_product` — create a product and its first passport
|
|
103
|
+
- `review_epcis_events` — summarise a passport's supply-chain trail
|
|
104
|
+
|
|
105
|
+
## Development
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npm install
|
|
109
|
+
npm run build # tsc -> dist/
|
|
110
|
+
npm run typecheck
|
|
111
|
+
npm test # vitest
|
|
112
|
+
npm run lint
|
|
113
|
+
npm start # run the hosted HTTP service locally (:8080)
|
|
114
|
+
npm run start:stdio # run the stdio server locally
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The hosted service is a plain Node HTTP server (`dist/http.js`),
|
|
118
|
+
stateless — each request carries its own API key and builds a fresh
|
|
119
|
+
MCP session. It is containerised via the `Dockerfile` and deployed to
|
|
120
|
+
Hetzner; see `tracepass-environment/docker-mcp.yml`.
|
|
121
|
+
|
|
122
|
+
## License
|
|
123
|
+
|
|
124
|
+
MIT
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client for the TracePass v1 API, used by the MCP tools.
|
|
3
|
+
*
|
|
4
|
+
* Design decision — why the MCP tools talk to the v1 API over HTTP
|
|
5
|
+
* rather than calling `lib/` functions in-process:
|
|
6
|
+
* - The v1 route handlers already encapsulate API-key auth,
|
|
7
|
+
* idempotency, the overage (402) flow, plan-gating, and the
|
|
8
|
+
* per-day write/read counter bumps. Re-implementing that inside
|
|
9
|
+
* the MCP tools would inevitably drift from the routes.
|
|
10
|
+
* - It is the SAME code path the standalone npm package will use,
|
|
11
|
+
* so the MCP core is genuinely transport-agnostic — the hosted
|
|
12
|
+
* /mcp endpoint and the local npm package differ only in base
|
|
13
|
+
* URL and where the key comes from.
|
|
14
|
+
* - One bug surface, not two.
|
|
15
|
+
* The cost is a loopback HTTP hop when hosted — negligible.
|
|
16
|
+
*
|
|
17
|
+
* The client is intentionally thin: it forwards the Bearer key,
|
|
18
|
+
* sets Idempotency-Key on writes, and returns the parsed JSON plus
|
|
19
|
+
* the status code. Interpreting a 402 overage / 403 plan-gate /
|
|
20
|
+
* 429 rate-limit is the tool's job — those are meaningful results an
|
|
21
|
+
* AI agent must see and act on, not errors to swallow.
|
|
22
|
+
*/
|
|
23
|
+
export interface TracePassApiResponse {
|
|
24
|
+
/** HTTP status code. */
|
|
25
|
+
status: number;
|
|
26
|
+
/** `true` for 2xx. */
|
|
27
|
+
ok: boolean;
|
|
28
|
+
/** Parsed JSON body, or null when the body was empty / not JSON. */
|
|
29
|
+
body: unknown;
|
|
30
|
+
}
|
|
31
|
+
export interface TracePassClientConfig {
|
|
32
|
+
/** Base URL of the TracePass app, no trailing slash —
|
|
33
|
+
* e.g. "https://app.tracepass.eu". */
|
|
34
|
+
baseUrl: string;
|
|
35
|
+
/** The caller's tp_ API key. */
|
|
36
|
+
apiKey: string;
|
|
37
|
+
}
|
|
38
|
+
export declare class TracePassClient {
|
|
39
|
+
private readonly baseUrl;
|
|
40
|
+
private readonly apiKey;
|
|
41
|
+
constructor(config: TracePassClientConfig);
|
|
42
|
+
/**
|
|
43
|
+
* Perform a v1 API request.
|
|
44
|
+
*
|
|
45
|
+
* @param method HTTP method
|
|
46
|
+
* @param path path under the base URL, must start with "/"
|
|
47
|
+
* @param body JSON body for write methods (omitted for GET)
|
|
48
|
+
*
|
|
49
|
+
* Write methods automatically carry an `Idempotency-Key` header so
|
|
50
|
+
* a retried tool call doesn't double-execute. Never throws on a
|
|
51
|
+
* non-2xx response — the status + body come back for the tool to
|
|
52
|
+
* interpret. Throws only on a genuine network/transport failure.
|
|
53
|
+
*/
|
|
54
|
+
request(method: "GET" | "POST" | "PATCH" | "DELETE", path: string, body?: unknown): Promise<TracePassApiResponse>;
|
|
55
|
+
get(path: string): Promise<TracePassApiResponse>;
|
|
56
|
+
post(path: string, body?: unknown): Promise<TracePassApiResponse>;
|
|
57
|
+
patch(path: string, body?: unknown): Promise<TracePassApiResponse>;
|
|
58
|
+
delete(path: string): Promise<TracePassApiResponse>;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=api-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,WAAW,oBAAoB;IACnC,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,EAAE,EAAE,OAAO,CAAC;IACZ,oEAAoE;IACpE,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC;2CACuC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;CAChB;AASD,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,MAAM,EAAE,qBAAqB;IAKzC;;;;;;;;;;;OAWG;IACG,OAAO,CACX,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,EAC3C,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,OAAO,GACb,OAAO,CAAC,oBAAoB,CAAC;IA8BhC,GAAG,CAAC,IAAI,EAAE,MAAM;IAGhB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;IAGjC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;IAGlC,MAAM,CAAC,IAAI,EAAE,MAAM;CAGpB"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP client for the TracePass v1 API, used by the MCP tools.
|
|
3
|
+
*
|
|
4
|
+
* Design decision — why the MCP tools talk to the v1 API over HTTP
|
|
5
|
+
* rather than calling `lib/` functions in-process:
|
|
6
|
+
* - The v1 route handlers already encapsulate API-key auth,
|
|
7
|
+
* idempotency, the overage (402) flow, plan-gating, and the
|
|
8
|
+
* per-day write/read counter bumps. Re-implementing that inside
|
|
9
|
+
* the MCP tools would inevitably drift from the routes.
|
|
10
|
+
* - It is the SAME code path the standalone npm package will use,
|
|
11
|
+
* so the MCP core is genuinely transport-agnostic — the hosted
|
|
12
|
+
* /mcp endpoint and the local npm package differ only in base
|
|
13
|
+
* URL and where the key comes from.
|
|
14
|
+
* - One bug surface, not two.
|
|
15
|
+
* The cost is a loopback HTTP hop when hosted — negligible.
|
|
16
|
+
*
|
|
17
|
+
* The client is intentionally thin: it forwards the Bearer key,
|
|
18
|
+
* sets Idempotency-Key on writes, and returns the parsed JSON plus
|
|
19
|
+
* the status code. Interpreting a 402 overage / 403 plan-gate /
|
|
20
|
+
* 429 rate-limit is the tool's job — those are meaningful results an
|
|
21
|
+
* AI agent must see and act on, not errors to swallow.
|
|
22
|
+
*/
|
|
23
|
+
/** Generate a random Idempotency-Key for a write request. */
|
|
24
|
+
function newIdempotencyKey() {
|
|
25
|
+
// crypto.randomUUID is available on Node 18+ and every Web runtime
|
|
26
|
+
// the MCP server can plausibly run on.
|
|
27
|
+
return `mcp-${crypto.randomUUID()}`;
|
|
28
|
+
}
|
|
29
|
+
export class TracePassClient {
|
|
30
|
+
baseUrl;
|
|
31
|
+
apiKey;
|
|
32
|
+
constructor(config) {
|
|
33
|
+
this.baseUrl = config.baseUrl.replace(/\/+$/, "");
|
|
34
|
+
this.apiKey = config.apiKey;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Perform a v1 API request.
|
|
38
|
+
*
|
|
39
|
+
* @param method HTTP method
|
|
40
|
+
* @param path path under the base URL, must start with "/"
|
|
41
|
+
* @param body JSON body for write methods (omitted for GET)
|
|
42
|
+
*
|
|
43
|
+
* Write methods automatically carry an `Idempotency-Key` header so
|
|
44
|
+
* a retried tool call doesn't double-execute. Never throws on a
|
|
45
|
+
* non-2xx response — the status + body come back for the tool to
|
|
46
|
+
* interpret. Throws only on a genuine network/transport failure.
|
|
47
|
+
*/
|
|
48
|
+
async request(method, path, body) {
|
|
49
|
+
const headers = {
|
|
50
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
51
|
+
Accept: "application/json",
|
|
52
|
+
};
|
|
53
|
+
const init = { method, headers };
|
|
54
|
+
if (body !== undefined && method !== "GET") {
|
|
55
|
+
headers["Content-Type"] = "application/json";
|
|
56
|
+
headers["Idempotency-Key"] = newIdempotencyKey();
|
|
57
|
+
init.body = JSON.stringify(body);
|
|
58
|
+
}
|
|
59
|
+
const res = await fetch(`${this.baseUrl}${path}`, init);
|
|
60
|
+
let parsed = null;
|
|
61
|
+
const text = await res.text();
|
|
62
|
+
if (text.length > 0) {
|
|
63
|
+
try {
|
|
64
|
+
parsed = JSON.parse(text);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Non-JSON body (e.g. an HTML error page from a proxy) —
|
|
68
|
+
// keep the raw text so the tool can still report something.
|
|
69
|
+
parsed = { raw: text };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { status: res.status, ok: res.ok, body: parsed };
|
|
73
|
+
}
|
|
74
|
+
get(path) {
|
|
75
|
+
return this.request("GET", path);
|
|
76
|
+
}
|
|
77
|
+
post(path, body) {
|
|
78
|
+
return this.request("POST", path, body ?? {});
|
|
79
|
+
}
|
|
80
|
+
patch(path, body) {
|
|
81
|
+
return this.request("PATCH", path, body ?? {});
|
|
82
|
+
}
|
|
83
|
+
delete(path) {
|
|
84
|
+
return this.request("DELETE", path);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=api-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAmBH,6DAA6D;AAC7D,SAAS,iBAAiB;IACxB,mEAAmE;IACnE,uCAAuC;IACvC,OAAO,OAAO,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC;AACtC,CAAC;AAED,MAAM,OAAO,eAAe;IACT,OAAO,CAAS;IAChB,MAAM,CAAS;IAEhC,YAAY,MAA6B;QACvC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC9B,CAAC;IAED;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,OAAO,CACX,MAA2C,EAC3C,IAAY,EACZ,IAAc;QAEd,MAAM,OAAO,GAA2B;YACtC,aAAa,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;YACtC,MAAM,EAAE,kBAAkB;SAC3B,CAAC;QACF,MAAM,IAAI,GAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAE9C,IAAI,IAAI,KAAK,SAAS,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YAC3C,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;YAC7C,OAAO,CAAC,iBAAiB,CAAC,GAAG,iBAAiB,EAAE,CAAC;YACjD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;QAExD,IAAI,MAAM,GAAY,IAAI,CAAC;QAC3B,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,yDAAyD;gBACzD,4DAA4D;gBAC5D,MAAM,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;YACzB,CAAC;QACH,CAAC;QAED,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC1D,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IACD,IAAI,CAAC,IAAY,EAAE,IAAc;QAC/B,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC;IACD,KAAK,CAAC,IAAY,EAAE,IAAc;QAChC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;IACjD,CAAC;IACD,MAAM,CAAC,IAAY;QACjB,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;CACF"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP entrypoint — the hosted form of the TracePass MCP server.
|
|
3
|
+
*
|
|
4
|
+
* Runs as a standalone Node service (deployed to Hetzner, served at
|
|
5
|
+
* https://ai.tracepass.eu/mcp). An MCP client connects over
|
|
6
|
+
* Streamable HTTP; the customer's TracePass API key travels in the
|
|
7
|
+
* `Authorization: Bearer tp_...` header of every request.
|
|
8
|
+
*
|
|
9
|
+
* Stateless by design: each MCP request is self-contained, so we
|
|
10
|
+
* build a fresh server + transport per request, bound to that
|
|
11
|
+
* request's API key. No server-side session state — which means the
|
|
12
|
+
* service scales horizontally and a restart drops nothing.
|
|
13
|
+
*
|
|
14
|
+
* This is the SAME server core (`createMcpServer`) the stdio
|
|
15
|
+
* entrypoint uses — only the transport + the key source differ.
|
|
16
|
+
*
|
|
17
|
+
* Env:
|
|
18
|
+
* PORT (optional) — listen port, default 8080
|
|
19
|
+
* TRACEPASS_BASE_URL (optional) — the TracePass API base URL the
|
|
20
|
+
* tools call, default https://app.tracepass.eu
|
|
21
|
+
*/
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG"}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP entrypoint — the hosted form of the TracePass MCP server.
|
|
3
|
+
*
|
|
4
|
+
* Runs as a standalone Node service (deployed to Hetzner, served at
|
|
5
|
+
* https://ai.tracepass.eu/mcp). An MCP client connects over
|
|
6
|
+
* Streamable HTTP; the customer's TracePass API key travels in the
|
|
7
|
+
* `Authorization: Bearer tp_...` header of every request.
|
|
8
|
+
*
|
|
9
|
+
* Stateless by design: each MCP request is self-contained, so we
|
|
10
|
+
* build a fresh server + transport per request, bound to that
|
|
11
|
+
* request's API key. No server-side session state — which means the
|
|
12
|
+
* service scales horizontally and a restart drops nothing.
|
|
13
|
+
*
|
|
14
|
+
* This is the SAME server core (`createMcpServer`) the stdio
|
|
15
|
+
* entrypoint uses — only the transport + the key source differ.
|
|
16
|
+
*
|
|
17
|
+
* Env:
|
|
18
|
+
* PORT (optional) — listen port, default 8080
|
|
19
|
+
* TRACEPASS_BASE_URL (optional) — the TracePass API base URL the
|
|
20
|
+
* tools call, default https://app.tracepass.eu
|
|
21
|
+
*/
|
|
22
|
+
import { createServer } from "node:http";
|
|
23
|
+
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
24
|
+
import { createMcpServer } from "./server.js";
|
|
25
|
+
const PORT = Number(process.env.PORT) || 8080;
|
|
26
|
+
const DEFAULT_BASE_URL = "https://app.tracepass.eu";
|
|
27
|
+
const BASE_URL = process.env.TRACEPASS_BASE_URL?.trim() || DEFAULT_BASE_URL;
|
|
28
|
+
/** The MCP endpoint path. `/mcp` is the conventional path. */
|
|
29
|
+
const MCP_PATH = "/mcp";
|
|
30
|
+
/** Extract the tp_ API key from the Authorization header. */
|
|
31
|
+
function extractApiKey(req) {
|
|
32
|
+
const auth = req.headers["authorization"];
|
|
33
|
+
if (typeof auth !== "string")
|
|
34
|
+
return null;
|
|
35
|
+
const m = auth.match(/^Bearer\s+(.+)$/i);
|
|
36
|
+
return m ? m[1].trim() : null;
|
|
37
|
+
}
|
|
38
|
+
/** Read a Node request body into a string. */
|
|
39
|
+
function readBody(req) {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
const chunks = [];
|
|
42
|
+
req.on("data", (c) => chunks.push(c));
|
|
43
|
+
req.on("end", () => resolve(Buffer.concat(chunks).toString("utf8")));
|
|
44
|
+
req.on("error", reject);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** Convert a Node IncomingMessage to a Web `Request`. */
|
|
48
|
+
async function toWebRequest(req) {
|
|
49
|
+
const host = req.headers["host"] ?? `localhost:${PORT}`;
|
|
50
|
+
const url = `http://${host}${req.url ?? "/"}`;
|
|
51
|
+
const headers = new Headers();
|
|
52
|
+
for (const [k, v] of Object.entries(req.headers)) {
|
|
53
|
+
if (v === undefined)
|
|
54
|
+
continue;
|
|
55
|
+
headers.set(k, Array.isArray(v) ? v.join(", ") : v);
|
|
56
|
+
}
|
|
57
|
+
const method = req.method ?? "GET";
|
|
58
|
+
const hasBody = method !== "GET" && method !== "HEAD";
|
|
59
|
+
const body = hasBody ? await readBody(req) : undefined;
|
|
60
|
+
return new Request(url, { method, headers, body: body || undefined });
|
|
61
|
+
}
|
|
62
|
+
/** Write a Web `Response` back through a Node ServerResponse. */
|
|
63
|
+
async function writeWebResponse(webRes, res) {
|
|
64
|
+
res.statusCode = webRes.status;
|
|
65
|
+
webRes.headers.forEach((value, key) => res.setHeader(key, value));
|
|
66
|
+
const text = await webRes.text();
|
|
67
|
+
res.end(text);
|
|
68
|
+
}
|
|
69
|
+
function sendJson(res, status, body) {
|
|
70
|
+
res.statusCode = status;
|
|
71
|
+
res.setHeader("Content-Type", "application/json");
|
|
72
|
+
res.end(JSON.stringify(body));
|
|
73
|
+
}
|
|
74
|
+
const httpServer = createServer((req, res) => {
|
|
75
|
+
void (async () => {
|
|
76
|
+
try {
|
|
77
|
+
const url = req.url ?? "/";
|
|
78
|
+
// Health check — for the Hetzner container's liveness probe.
|
|
79
|
+
if (url === "/health" || url === "/healthz") {
|
|
80
|
+
sendJson(res, 200, { status: "ok", service: "tracepass-mcp" });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Only the MCP path is served.
|
|
84
|
+
if (url.split("?")[0] !== MCP_PATH) {
|
|
85
|
+
sendJson(res, 404, {
|
|
86
|
+
error: "not_found",
|
|
87
|
+
message: `This service serves MCP at ${MCP_PATH} only.`,
|
|
88
|
+
});
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// API key required — no anonymous MCP access.
|
|
92
|
+
const apiKey = extractApiKey(req);
|
|
93
|
+
if (!apiKey) {
|
|
94
|
+
sendJson(res, 401, {
|
|
95
|
+
error: "unauthorized",
|
|
96
|
+
message: "Missing API key. Send Authorization: Bearer <tp_ key>. Mint a key in the TracePass dashboard → Developer → API Keys.",
|
|
97
|
+
});
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
// Fresh, stateless server + transport per request, bound to
|
|
101
|
+
// this request's key. The transport's handleRequest takes a
|
|
102
|
+
// Web Request and returns a Web Response.
|
|
103
|
+
const server = createMcpServer({ apiKey, baseUrl: BASE_URL });
|
|
104
|
+
const transport = new WebStandardStreamableHTTPServerTransport({
|
|
105
|
+
sessionIdGenerator: undefined, // stateless
|
|
106
|
+
});
|
|
107
|
+
await server.connect(transport);
|
|
108
|
+
const webReq = await toWebRequest(req);
|
|
109
|
+
const webRes = await transport.handleRequest(webReq);
|
|
110
|
+
await writeWebResponse(webRes, res);
|
|
111
|
+
// Stateless — release the per-request server once answered.
|
|
112
|
+
await server.close();
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
// Never leak a stack trace; never leave the socket hanging.
|
|
116
|
+
if (!res.headersSent) {
|
|
117
|
+
sendJson(res, 500, {
|
|
118
|
+
error: "internal_error",
|
|
119
|
+
message: err instanceof Error ? err.message : "Unexpected error",
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
res.end();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
})();
|
|
127
|
+
});
|
|
128
|
+
httpServer.listen(PORT, () => {
|
|
129
|
+
process.stdout.write(`tracepass-mcp-server listening on :${PORT}${MCP_PATH} ` +
|
|
130
|
+
`(TracePass API: ${BASE_URL})\n`);
|
|
131
|
+
});
|
|
132
|
+
// Graceful shutdown so a container stop / redeploy drains cleanly.
|
|
133
|
+
for (const signal of ["SIGTERM", "SIGINT"]) {
|
|
134
|
+
process.on(signal, () => {
|
|
135
|
+
httpServer.close(() => process.exit(0));
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP prompts for the TracePass server.
|
|
3
|
+
*
|
|
4
|
+
* Prompts are reusable, parameterised instructions the MCP client
|
|
5
|
+
* surfaces to the user (typically as slash-commands). They are not
|
|
6
|
+
* tool calls — a prompt returns a `messages` array that seeds the
|
|
7
|
+
* conversation, after which the model uses the TracePass tools to
|
|
8
|
+
* carry out the workflow.
|
|
9
|
+
*
|
|
10
|
+
* Each prompt here encodes a common DPP workflow so a user doesn't
|
|
11
|
+
* have to phrase it from scratch — and so the model approaches the
|
|
12
|
+
* task the way TracePass intends (e.g. always reviewing before
|
|
13
|
+
* publishing, never bulk-creating billable passports without
|
|
14
|
+
* consent).
|
|
15
|
+
*
|
|
16
|
+
* Pure — no IO. `registerPrompts(server)` wires them onto the server.
|
|
17
|
+
*/
|
|
18
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
19
|
+
/**
|
|
20
|
+
* Register every TracePass prompt on the server.
|
|
21
|
+
*/
|
|
22
|
+
export declare function registerPrompts(server: McpServer): void;
|
|
23
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAczE;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA4EvD"}
|
package/dist/prompts.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP prompts for the TracePass server.
|
|
3
|
+
*
|
|
4
|
+
* Prompts are reusable, parameterised instructions the MCP client
|
|
5
|
+
* surfaces to the user (typically as slash-commands). They are not
|
|
6
|
+
* tool calls — a prompt returns a `messages` array that seeds the
|
|
7
|
+
* conversation, after which the model uses the TracePass tools to
|
|
8
|
+
* carry out the workflow.
|
|
9
|
+
*
|
|
10
|
+
* Each prompt here encodes a common DPP workflow so a user doesn't
|
|
11
|
+
* have to phrase it from scratch — and so the model approaches the
|
|
12
|
+
* task the way TracePass intends (e.g. always reviewing before
|
|
13
|
+
* publishing, never bulk-creating billable passports without
|
|
14
|
+
* consent).
|
|
15
|
+
*
|
|
16
|
+
* Pure — no IO. `registerPrompts(server)` wires them onto the server.
|
|
17
|
+
*/
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
/** Build a GetPromptResult from a single user-role text message. */
|
|
20
|
+
function userPrompt(text) {
|
|
21
|
+
return {
|
|
22
|
+
messages: [
|
|
23
|
+
{
|
|
24
|
+
role: "user",
|
|
25
|
+
content: { type: "text", text },
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Register every TracePass prompt on the server.
|
|
32
|
+
*/
|
|
33
|
+
export function registerPrompts(server) {
|
|
34
|
+
// ── Audit a passport's completeness ───────────────────────────
|
|
35
|
+
server.registerPrompt("audit_passport", {
|
|
36
|
+
title: "Audit a passport",
|
|
37
|
+
description: "Review one Digital Product Passport for completeness and compliance readiness — which required fields are missing, which economic-operator parties are unset, whether it's publishable.",
|
|
38
|
+
argsSchema: {
|
|
39
|
+
passportId: z
|
|
40
|
+
.string()
|
|
41
|
+
.describe("The TracePass id of the passport to audit."),
|
|
42
|
+
},
|
|
43
|
+
}, ({ passportId }) => userPrompt(`Audit the TracePass Digital Product Passport with id "${passportId}".\n\n` +
|
|
44
|
+
`1. Fetch it with the tracepass_passports tool (action: get, format: full).\n` +
|
|
45
|
+
`2. Report: which required template fields are still empty or unapproved; ` +
|
|
46
|
+
`which economic-operator parties (manufacturer / importer / recycler / etc.) are missing; ` +
|
|
47
|
+
`the passport's status and completion percentage.\n` +
|
|
48
|
+
`3. State plainly whether the passport is ready to publish, and if not, the exact gaps to close.\n` +
|
|
49
|
+
`Do not change anything — this is a read-only audit.`));
|
|
50
|
+
// ── Set up a product + its first passport ─────────────────────
|
|
51
|
+
server.registerPrompt("onboard_product", {
|
|
52
|
+
title: "Onboard a new product",
|
|
53
|
+
description: "Walk through creating a new product and its first Digital Product Passport — gathering the details, then creating both.",
|
|
54
|
+
argsSchema: {
|
|
55
|
+
productName: z.string().describe("The product's name."),
|
|
56
|
+
category: z
|
|
57
|
+
.string()
|
|
58
|
+
.describe("Category key — battery, textile, electronics, construction, steel, chemicals, packaging, furniture, tyres, jewelry, toys, or fmcg."),
|
|
59
|
+
},
|
|
60
|
+
}, ({ productName, category }) => userPrompt(`Help me onboard a new product into TracePass: "${productName}" in the "${category}" category.\n\n` +
|
|
61
|
+
`1. Confirm the product details with me (model / SKU, description), then create it with tracepass_products (action: create).\n` +
|
|
62
|
+
`2. Ask me for the first unit's GTIN and serial number.\n` +
|
|
63
|
+
`3. Before creating the passport, remind me that a passport is BILLABLE and consumes a plan DPP slot. ` +
|
|
64
|
+
`Only after I confirm, create it with tracepass_passports (action: create).\n` +
|
|
65
|
+
`4. If the account is over its plan quota, tell me the overage cost and wait for my explicit go-ahead before retrying with confirmOverage.`));
|
|
66
|
+
// ── Reconcile EPCIS supply-chain events ───────────────────────
|
|
67
|
+
server.registerPrompt("review_epcis_events", {
|
|
68
|
+
title: "Review a passport's EPCIS events",
|
|
69
|
+
description: "Inspect the EPCIS 2.0 supply-chain event history of a passport and summarise the product's traceability story.",
|
|
70
|
+
argsSchema: {
|
|
71
|
+
passportId: z
|
|
72
|
+
.string()
|
|
73
|
+
.describe("The TracePass id of the passport whose events to review."),
|
|
74
|
+
},
|
|
75
|
+
}, ({ passportId }) => userPrompt(`Review the EPCIS 2.0 supply-chain events for the TracePass passport "${passportId}".\n\n` +
|
|
76
|
+
`1. Export them with tracepass_epcis (action: export).\n` +
|
|
77
|
+
`2. Summarise the product's traceability story in plain language — the sequence of events ` +
|
|
78
|
+
`(commissioning, production steps, shipping, service, ownership changes), with dates and locations.\n` +
|
|
79
|
+
`3. Flag any gaps — e.g. a long unexplained period, a missing production step, events with no location.\n` +
|
|
80
|
+
`This is read-only; do not capture or modify any events.`));
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,oEAAoE;AACpE,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO;QACL,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAe;gBACrB,OAAO,EAAE,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE;aACzC;SACF;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,MAAiB;IAC/C,iEAAiE;IACjE,MAAM,CAAC,cAAc,CACnB,gBAAgB,EAChB;QACE,KAAK,EAAE,kBAAkB;QACzB,WAAW,EACT,yLAAyL;QAC3L,UAAU,EAAE;YACV,UAAU,EAAE,CAAC;iBACV,MAAM,EAAE;iBACR,QAAQ,CAAC,4CAA4C,CAAC;SAC1D;KACF,EACD,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CACjB,UAAU,CACR,yDAAyD,UAAU,QAAQ;QACzE,8EAA8E;QAC9E,2EAA2E;QAC3E,2FAA2F;QAC3F,oDAAoD;QACpD,mGAAmG;QACnG,qDAAqD,CACxD,CACJ,CAAC;IAEF,iEAAiE;IACjE,MAAM,CAAC,cAAc,CACnB,iBAAiB,EACjB;QACE,KAAK,EAAE,uBAAuB;QAC9B,WAAW,EACT,yHAAyH;QAC3H,UAAU,EAAE;YACV,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;YACvD,QAAQ,EAAE,CAAC;iBACR,MAAM,EAAE;iBACR,QAAQ,CACP,oIAAoI,CACrI;SACJ;KACF,EACD,CAAC,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,EAAE,CAC5B,UAAU,CACR,kDAAkD,WAAW,aAAa,QAAQ,iBAAiB;QACjG,+HAA+H;QAC/H,0DAA0D;QAC1D,uGAAuG;QACvG,8EAA8E;QAC9E,2IAA2I,CAC9I,CACJ,CAAC;IAEF,iEAAiE;IACjE,MAAM,CAAC,cAAc,CACnB,qBAAqB,EACrB;QACE,KAAK,EAAE,kCAAkC;QACzC,WAAW,EACT,gHAAgH;QAClH,UAAU,EAAE;YACV,UAAU,EAAE,CAAC;iBACV,MAAM,EAAE;iBACR,QAAQ,CAAC,0DAA0D,CAAC;SACxE;KACF,EACD,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CACjB,UAAU,CACR,wEAAwE,UAAU,QAAQ;QACxF,yDAAyD;QACzD,2FAA2F;QAC3F,sGAAsG;QACtG,0GAA0G;QAC1G,yDAAyD,CAC5D,CACJ,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
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 type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
20
|
+
import type { TracePassClient } from "./api-client.js";
|
|
21
|
+
/**
|
|
22
|
+
* Register every TracePass resource + resource template on the
|
|
23
|
+
* server, bound to a v1 API client.
|
|
24
|
+
*/
|
|
25
|
+
export declare function registerResources(server: McpServer, client: TracePassClient): void;
|
|
26
|
+
//# sourceMappingURL=resources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAyBvD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,GACtB,IAAI,CAiFN"}
|