@pokemontcgapi/mcp 0.1.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 pokemontcgapi.com
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,153 @@
1
+ # @pokemontcgapi/mcp
2
+
3
+ An [MCP](https://modelcontextprotocol.io) server for the Pokémon TCG API at
4
+ [pokemontcgapi.com](https://pokemontcgapi.com). It gives an agent eight tools over a
5
+ catalogue of **615 sets and 52,337 cards** — 379 Japanese sets, 176 international, 60 Simplified
6
+ Chinese — with card names in six languages, illustrators, images, and prices that carry their source,
7
+ basis, grade and sample size.
8
+
9
+ Unofficial. Not produced, endorsed, supported by or affiliated with Nintendo, Creatures Inc.,
10
+ GAME FREAK inc. or The Pokémon Company International. Pokémon and all related marks are trademarks of
11
+ their respective owners.
12
+
13
+ ## Install
14
+
15
+ Claude Code:
16
+
17
+ ```bash
18
+ claude mcp add pokemontcgapi --env PTCG_API_KEY=your-key -- npx -y @pokemontcgapi/mcp
19
+ ```
20
+
21
+ Claude Desktop — `claude_desktop_config.json`:
22
+
23
+ ```json
24
+ {
25
+ "mcpServers": {
26
+ "pokemontcgapi": {
27
+ "command": "npx",
28
+ "args": ["-y", "@pokemontcgapi/mcp"],
29
+ "env": { "PTCG_API_KEY": "your-key" }
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ Cursor — `.cursor/mcp.json`:
36
+
37
+ ```json
38
+ {
39
+ "mcpServers": {
40
+ "pokemontcgapi": {
41
+ "command": "npx",
42
+ "args": ["-y", "@pokemontcgapi/mcp"],
43
+ "env": { "PTCG_API_KEY": "${env:PTCG_API_KEY}" }
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ VS Code — `.vscode/mcp.json`. Note the top-level key is `servers`, not `mcpServers`, and `inputs`
50
+ keeps the key out of the committed file:
51
+
52
+ ```json
53
+ {
54
+ "inputs": [
55
+ { "id": "ptcg-key", "type": "promptString", "description": "pokemontcgapi key", "password": true }
56
+ ],
57
+ "servers": {
58
+ "pokemontcgapi": {
59
+ "command": "npx",
60
+ "args": ["-y", "@pokemontcgapi/mcp"],
61
+ "env": { "PTCG_API_KEY": "${input:ptcg-key}" }
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ Environment: `PTCG_API_KEY` (optional today), `PTCG_BASE_URL` (defaults to
68
+ `https://api.pokemontcgapi.com`). Node ≥ 20.
69
+
70
+ ## The tools
71
+
72
+ Eight tools, not one per endpoint. `tools/list` sits in the model's context on every turn, so the
73
+ whole surface is just over 9 KB, and each tool is shaped like a question rather than like a route —
74
+ the model does not have to chain four calls to answer one thing.
75
+
76
+ | Tool | Answers |
77
+ |---|---|
78
+ | `ptcg_search_cards` | "Charizard cards from Japanese sets", by name, set, region, rarity, artist or release window |
79
+ | `ptcg_get_cards` | Up to 100 ids in one call; `base1-4` and `bs-4` both resolve |
80
+ | `ptcg_get_card_prices` | Every current observation for a card, with printing, grade, `as_of` and `sample_n` |
81
+ | `ptcg_list_sets` | "Every Japanese set released in 2024" |
82
+ | `ptcg_get_reference` | The exact strings for types, supertypes and rarities, so filters are not guessed |
83
+ | `ptcg_list_artists` | Illustrators and how many cards each drew |
84
+ | `ptcg_get_catalogue_status` | What the catalogue does and does **not** contain, measured live |
85
+ | `ptcg_identify_card_from_image` | "Which card is this a photo of?" — ranked candidates, and an explicit refusal when reprints share the artwork. 25 credits a call |
86
+
87
+ Every tool is annotated `readOnlyHint: true` and `destructiveHint: false`. Nothing here writes.
88
+ `ptcg_identify_card_from_image` is the one marked `idempotentHint: false`, because the same photo costs
89
+ 25 credits every time it is sent — a client must not retry it on its own.
90
+
91
+ ## What this API does not have
92
+
93
+ The last tool exists because of this section, and it returns these facts from a live call rather
94
+ than leaving a model to infer them:
95
+
96
+ - **No Korean cards.** Zero `KR` sets and zero `ko` translations. The print region and the locale are
97
+ modelled in the schema and carry no data, so filtering on them returns an empty result, not an error.
98
+ - **No card game text.** `attacks`, `abilities`, `weaknesses`, `resistances`, `subtypes`,
99
+ `retreat_cost`, `rules`, `flavor_text` and `legalities` are empty for every card; `types` and
100
+ `national_pokedex_numbers` are populated only on part of the Scarlet & Violet era. If the question
101
+ is about what a card does in play, this API cannot answer it.
102
+
103
+ Both statements are measured, dated in the source, and repeated verbatim in the tool descriptions —
104
+ so an agent is told before it calls, not after.
105
+
106
+ ## Reading prices correctly
107
+
108
+ There is no printing filter. First Edition, Unlimited, holofoil, reverse holofoil and graded rows all
109
+ come back together, so read `printing`, `condition` and `grading` on each row rather than taking the
110
+ first number. `basis` separates `GUIDE` (published upstream) from `DERIVED` (computed by us);
111
+ `PTCG_INDEX` is our own composite in EUR and carries `sample_n`. Every observation has an `as_of`
112
+ date and is delayed by at least a day — never quote a price without it.
113
+
114
+ ## Context discipline
115
+
116
+ Results are capped at 50 rows regardless of what the API allows, sent as aligned tables rather than
117
+ JSON (about 40% fewer tokens, and models misread them less often), with a compact field projection.
118
+ Truncation is always announced along with the cursor to continue. Price rows are the one thing never
119
+ truncated.
120
+
121
+ ## Protocol
122
+
123
+ Built on `@modelcontextprotocol/server` v2, implementing the
124
+ [2026-07-28](https://modelcontextprotocol.io/specification/2026-07-28) revision. stdio transport.
125
+
126
+ ## Also available
127
+
128
+ - **TypeScript SDK**, zero runtime dependencies: [`@pokemontcgapi/sdk`](https://www.npmjs.com/package/@pokemontcgapi/sdk) — [source](https://github.com/pokemontcgapi/sdk-typescript)
129
+ - **Docs**: <https://pokemontcgapi.com/docs>
130
+ - **Coverage, measured live**: <https://pokemontcgapi.com/coverage>
131
+
132
+ ## Build from source
133
+
134
+ ```bash
135
+ npm ci
136
+ npm run typecheck
137
+ npm run build
138
+ ```
139
+
140
+ Node >= 20. No test suite lives here yet: what CI enforces is that the package
141
+ typechecks and builds on both Node 20 and Node 22, and that `npm pack` produces
142
+ the file list the registry is meant to receive.
143
+
144
+ This package is developed inside the private monorepo that runs
145
+ [pokemontcgapi.com](https://pokemontcgapi.com) and mirrored here on each release,
146
+ so a merged pull request travels back by hand rather than by merge button. That
147
+ is not a reason to send patches elsewhere — open the issue or the PR here, it is
148
+ the address that gets read.
149
+
150
+ ## Licence
151
+
152
+ MIT. Data served by the API carries per-source redistribution terms — see
153
+ <https://pokemontcgapi.com/legal/attribution>.
package/dist/api.d.ts ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Client HTTP minimo verso api.pokemontcgapi.com.
3
+ *
4
+ * PERCHE' NON DIPENDE DA @pokemontcgapi/sdk, che pure esiste in questo repo:
5
+ * questo server usa sei endpoint in sola lettura e non pagina mai in
6
+ * profondita' (ogni risposta e' tagliata a 50 righe prima di tornare a un
7
+ * modello), quindi dell'SDK userebbe il trasporto e nient'altro. In cambio
8
+ * porterebbe un secondo pacchetto da tenere allineato in versione a ogni
9
+ * pubblicazione. La copia e' ottanta righe e si legge tutta; la dipendenza
10
+ * sarebbe piu' cara della copia. Se un giorno questo server dovesse iterare
11
+ * collezioni intere, la scelta va rifatta al contrario.
12
+ *
13
+ * Provenienza: la gestione degli errori e la forma dell'envelope sono le stesse
14
+ * di packages/sdk-typescript/src/{client,errors}.ts.
15
+ */
16
+ export interface ApiErrorShape {
17
+ readonly code: string;
18
+ readonly message: string;
19
+ readonly details?: Record<string, unknown>;
20
+ readonly request_id?: string;
21
+ }
22
+ export declare class ApiError extends Error {
23
+ readonly code: string;
24
+ readonly status: number;
25
+ readonly details: Record<string, unknown> | undefined;
26
+ readonly requestId: string | undefined;
27
+ constructor(status: number, body: ApiErrorShape);
28
+ }
29
+ export interface Envelope<T> {
30
+ readonly data: readonly T[];
31
+ readonly meta?: {
32
+ readonly limit: number;
33
+ readonly count: number;
34
+ readonly total_count?: number;
35
+ readonly has_more: boolean;
36
+ };
37
+ readonly links?: {
38
+ readonly next?: string;
39
+ };
40
+ }
41
+ export declare class Api {
42
+ private readonly baseUrl;
43
+ private readonly apiKey;
44
+ constructor(baseUrl?: string, apiKey?: string | undefined);
45
+ get<T>(path: string, params?: Record<string, unknown>): Promise<T>;
46
+ /**
47
+ * POST di un'immagine, per il riconoscimento.
48
+ *
49
+ * Metodo separato e non un parametro di `get`: la forma della richiesta e'
50
+ * diversa in tutto — verbo, corpo, assenza di query — e schiacciarle in una
51
+ * sola funzione produrrebbe una firma con quattro argomenti opzionali di cui
52
+ * tre sempre inutilizzati.
53
+ */
54
+ postImage<T>(path: string, image: Uint8Array, fields?: Record<string, string>): Promise<T>;
55
+ }
package/dist/api.js ADDED
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Client HTTP minimo verso api.pokemontcgapi.com.
3
+ *
4
+ * PERCHE' NON DIPENDE DA @pokemontcgapi/sdk, che pure esiste in questo repo:
5
+ * questo server usa sei endpoint in sola lettura e non pagina mai in
6
+ * profondita' (ogni risposta e' tagliata a 50 righe prima di tornare a un
7
+ * modello), quindi dell'SDK userebbe il trasporto e nient'altro. In cambio
8
+ * porterebbe un secondo pacchetto da tenere allineato in versione a ogni
9
+ * pubblicazione. La copia e' ottanta righe e si legge tutta; la dipendenza
10
+ * sarebbe piu' cara della copia. Se un giorno questo server dovesse iterare
11
+ * collezioni intere, la scelta va rifatta al contrario.
12
+ *
13
+ * Provenienza: la gestione degli errori e la forma dell'envelope sono le stesse
14
+ * di packages/sdk-typescript/src/{client,errors}.ts.
15
+ */
16
+ const DEFAULT_BASE_URL = 'https://api.pokemontcgapi.com';
17
+ const TIMEOUT_MS = 20_000;
18
+ export class ApiError extends Error {
19
+ code;
20
+ status;
21
+ details;
22
+ requestId;
23
+ constructor(status, body) {
24
+ super(`${body.code}: ${body.message}`);
25
+ this.name = 'ApiError';
26
+ this.status = status;
27
+ this.code = body.code;
28
+ this.details = body.details;
29
+ this.requestId = body.request_id;
30
+ }
31
+ }
32
+ export class Api {
33
+ baseUrl;
34
+ apiKey;
35
+ constructor(baseUrl = process.env['PTCG_BASE_URL'] ?? DEFAULT_BASE_URL, apiKey = process.env['PTCG_API_KEY']) {
36
+ this.baseUrl = baseUrl.replace(/\/+$/, '');
37
+ this.apiKey = apiKey;
38
+ }
39
+ async get(path, params = {}) {
40
+ const search = new URLSearchParams();
41
+ for (const [key, value] of Object.entries(params)) {
42
+ if (value === undefined || value === null || value === '')
43
+ continue;
44
+ search.set(key, Array.isArray(value) ? value.join(',') : String(value));
45
+ }
46
+ const query = search.toString();
47
+ const url = `${this.baseUrl}${path}${query === '' ? '' : `?${query}`}`;
48
+ const headers = {
49
+ accept: 'application/json',
50
+ 'user-agent': '@pokemontcgapi/mcp',
51
+ };
52
+ if (this.apiKey !== undefined)
53
+ headers['x-api-key'] = this.apiKey;
54
+ const controller = new AbortController();
55
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
56
+ try {
57
+ const response = await fetch(url, { headers, signal: controller.signal });
58
+ if (!response.ok) {
59
+ let body = { code: `HTTP_${response.status}`, message: response.statusText };
60
+ try {
61
+ const parsed = (await response.json());
62
+ if (parsed.error?.code !== undefined)
63
+ body = parsed.error;
64
+ }
65
+ catch {
66
+ /* la risposta non era il nostro envelope: si tiene quella costruita sopra */
67
+ }
68
+ throw new ApiError(response.status, body);
69
+ }
70
+ return (await response.json());
71
+ }
72
+ finally {
73
+ clearTimeout(timer);
74
+ }
75
+ }
76
+ /**
77
+ * POST di un'immagine, per il riconoscimento.
78
+ *
79
+ * Metodo separato e non un parametro di `get`: la forma della richiesta e'
80
+ * diversa in tutto — verbo, corpo, assenza di query — e schiacciarle in una
81
+ * sola funzione produrrebbe una firma con quattro argomenti opzionali di cui
82
+ * tre sempre inutilizzati.
83
+ */
84
+ async postImage(path, image, fields = {}) {
85
+ const form = new FormData();
86
+ // Il tipo generico e non `image/jpeg`: il formato lo riconosce il server
87
+ // dai magic byte, e dichiarare quello sbagliato e' peggio che tacere.
88
+ form.set('image', new Blob([image], { type: "application/octet-stream" }), 'card');
89
+ for (const [key, value] of Object.entries(fields))
90
+ form.set(key, value);
91
+ const headers = {
92
+ accept: 'application/json',
93
+ 'user-agent': '@pokemontcgapi/mcp',
94
+ };
95
+ // Nessun content-type esplicito: il boundary del multipart lo scrive fetch,
96
+ // e impostarlo a mano produce un corpo che il server non sa separare.
97
+ if (this.apiKey !== undefined)
98
+ headers['x-api-key'] = this.apiKey;
99
+ const controller = new AbortController();
100
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
101
+ try {
102
+ const response = await fetch(`${this.baseUrl}${path}`, {
103
+ method: 'POST',
104
+ headers,
105
+ body: form,
106
+ signal: controller.signal,
107
+ });
108
+ if (!response.ok) {
109
+ let body = { code: `HTTP_${response.status}`, message: response.statusText };
110
+ try {
111
+ const parsed = (await response.json());
112
+ if (parsed.error?.code !== undefined)
113
+ body = parsed.error;
114
+ }
115
+ catch {
116
+ /* la risposta non era il nostro envelope */
117
+ }
118
+ throw new ApiError(response.status, body);
119
+ }
120
+ return (await response.json());
121
+ }
122
+ finally {
123
+ clearTimeout(timer);
124
+ }
125
+ }
126
+ }
127
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Le avvertenze che devono comparire IDENTICHE ovunque.
3
+ *
4
+ * Una descrizione di strumento MCP e' l'unico canale in cui mettiamo parole
5
+ * nostre dentro la memoria di lavoro di un modello, a ogni sessione, senza
6
+ * passare da un crawler. Vale la pena scriverle una volta sola e riusarle: se
7
+ * la descrizione di `ptcg_search_cards` dice una cosa e quella di
8
+ * `ptcg_get_cards` un'altra, il modello si fida della piu' recente e sbaglia.
9
+ *
10
+ * Misurate il 2026-08-27. Quando il dato a monte cambia, si cambia QUI.
11
+ */
12
+ export declare const MEASURED_ON = "2026-08-27";
13
+ /**
14
+ * La regola 4 del progetto — non dichiarare mai dati che non abbiamo — vive
15
+ * tecnicamente qui: nessun enum di questo server elenca `ko` o `KR` come se
16
+ * fossero popolati, e questa frase e' quella che uno strumento restituisce
17
+ * quando gli viene chiesto della copertura.
18
+ */
19
+ export declare const KOREAN_COVERAGE: string;
20
+ export declare const GAMEPLAY_FIELDS: string;
21
+ export declare const PRICE_CAVEAT: string;
22
+ export declare const LANGUAGE_CAVEAT: string;
23
+ export declare const REGION_CAVEAT: string;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Le avvertenze che devono comparire IDENTICHE ovunque.
3
+ *
4
+ * Una descrizione di strumento MCP e' l'unico canale in cui mettiamo parole
5
+ * nostre dentro la memoria di lavoro di un modello, a ogni sessione, senza
6
+ * passare da un crawler. Vale la pena scriverle una volta sola e riusarle: se
7
+ * la descrizione di `ptcg_search_cards` dice una cosa e quella di
8
+ * `ptcg_get_cards` un'altra, il modello si fida della piu' recente e sbaglia.
9
+ *
10
+ * Misurate il 2026-08-27. Quando il dato a monte cambia, si cambia QUI.
11
+ */
12
+ export const MEASURED_ON = '2026-08-27';
13
+ /**
14
+ * La regola 4 del progetto — non dichiarare mai dati che non abbiamo — vive
15
+ * tecnicamente qui: nessun enum di questo server elenca `ko` o `KR` come se
16
+ * fossero popolati, e questa frase e' quella che uno strumento restituisce
17
+ * quando gli viene chiesto della copertura.
18
+ */
19
+ export const KOREAN_COVERAGE = 'No Korean (KR) sets are present in the catalogue and no Korean (ko) card translations exist. ' +
20
+ 'The KR print region and the ko locale are modelled in the schema but carry no data, so filtering ' +
21
+ 'on them returns an empty result rather than an error.';
22
+ export const GAMEPLAY_FIELDS = 'This catalogue does NOT carry card game text. attacks, abilities, weaknesses, resistances, subtypes, ' +
23
+ 'retreat_cost, rules, flavor_text and legalities are empty for every card; types and ' +
24
+ 'national_pokedex_numbers are populated only on part of the Scarlet & Violet era. If the question is ' +
25
+ 'about what a card does in play, deck legality, or damage numbers, this API cannot answer it — say so ' +
26
+ 'rather than inferring from the card name.';
27
+ export const PRICE_CAVEAT = 'There is no printing filter: 1st Edition, Unlimited, holofoil, reverse holofoil and graded rows all come ' +
28
+ 'back together, so you must read printing, condition and grading on each row rather than taking the first ' +
29
+ 'price. basis distinguishes GUIDE (a figure published upstream) from DERIVED (computed by us). ' +
30
+ 'PTCG_INDEX is our own composite in EUR and carries sample_n. Every observation carries as_of and is ' +
31
+ 'delayed by at least a day — never quote a price without its as_of date and its source.';
32
+ export const LANGUAGE_CAVEAT = 'Card names exist in six locales: en, ja, fr, de, es, it. Passing lang= replaces the name field itself ' +
33
+ 'and falls back to English when a translation is missing. Set names are not translated.';
34
+ export const REGION_CAVEAT = 'Print regions are WEST (176 sets), JP (379 sets) and CN (60 sets, Simplified Chinese), measured ' +
35
+ MEASURED_ON +
36
+ '. Japanese sets are not translations of Western ones: they have their own boundaries, their own ' +
37
+ 'numbering and their own release dates, so a Japanese set and its international counterpart are two ' +
38
+ 'different rows.';
39
+ //# sourceMappingURL=caveats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caveats.js","sourceRoot":"","sources":["../src/caveats.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,CAAC,MAAM,WAAW,GAAG,YAAY,CAAC;AAExC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAC1B,+FAA+F;IAC/F,mGAAmG;IACnG,uDAAuD,CAAC;AAE1D,MAAM,CAAC,MAAM,eAAe,GAC1B,uGAAuG;IACvG,sFAAsF;IACtF,sGAAsG;IACtG,uGAAuG;IACvG,2CAA2C,CAAC;AAE9C,MAAM,CAAC,MAAM,YAAY,GACvB,2GAA2G;IAC3G,2GAA2G;IAC3G,gGAAgG;IAChG,sGAAsG;IACtG,wFAAwF,CAAC;AAE3F,MAAM,CAAC,MAAM,eAAe,GAC1B,wGAAwG;IACxG,wFAAwF,CAAC;AAE3F,MAAM,CAAC,MAAM,aAAa,GACxB,kGAAkG;IAClG,WAAW;IACX,kGAAkG;IAClG,qGAAqG;IACrG,iBAAiB,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { Envelope } from './api.js';
2
+ /**
3
+ * Raccolta paginata con filtro locale.
4
+ *
5
+ * Serve perche' due filtri che un agente chiede continuamente — "carte
6
+ * giapponesi" e "set usciti nel 2024" — l'endpoint non li accetta: la regione
7
+ * sta sul set e non e' un campo della grammatica di ricerca delle carte, e le
8
+ * finestre temporali sui set non sono parametri. Filtrare la PRIMA pagina e
9
+ * basta e' cio' che faceva la versione precedente di questo server, e su un
10
+ * elenco ordinato per data decrescente rispondeva "nessun risultato" a
11
+ * "set giapponesi del 2024" — perche' la prima pagina e' fatta di set del 2026.
12
+ *
13
+ * Due garanzie, entrambe necessarie:
14
+ *
15
+ * - `stop` permette di terminare presto quando la lista e' ordinata e siamo
16
+ * usciti dalla finestra: senza, scorrere fino al 1996 per prendere il 2024
17
+ * costerebbe decine di richieste.
18
+ * - `exhausted` dice se la scansione ha davvero visto tutto. Se e' falso il
19
+ * chiamante DEVE dirlo nella risposta: un elenco parziale presentato come
20
+ * completo e' il modo in cui un agente conclude che una carta non esiste.
21
+ */
22
+ export interface Collected<T> {
23
+ readonly rows: readonly T[];
24
+ /** true se la scansione e' arrivata in fondo o ha incontrato `stop`. */
25
+ readonly exhausted: boolean;
26
+ readonly scannedPages: number;
27
+ readonly nextCursor: string | undefined;
28
+ }
29
+ export type Verdict = 'keep' | 'skip' | 'stop';
30
+ export declare function collect<T>(fetchPage: (cursor: string | undefined) => Promise<Envelope<T>>, verdict: (row: T) => Verdict, limit: number, maxPages?: number): Promise<Collected<T>>;
31
+ /** Il cursore e' opaco: si estrae dall'URL che l'API ha costruito, mai ricomposto. */
32
+ export declare function cursorOf(nextUrl: string): string | undefined;
@@ -0,0 +1,44 @@
1
+ export async function collect(fetchPage, verdict, limit, maxPages = 8) {
2
+ const rows = [];
3
+ let cursor;
4
+ let pages = 0;
5
+ let stopped = false;
6
+ while (pages < maxPages) {
7
+ const body = await fetchPage(cursor);
8
+ pages += 1;
9
+ for (const row of body.data) {
10
+ const call = verdict(row);
11
+ if (call === 'stop') {
12
+ stopped = true;
13
+ break;
14
+ }
15
+ if (call === 'keep')
16
+ rows.push(row);
17
+ if (rows.length >= limit)
18
+ break;
19
+ }
20
+ const next = body.links?.next;
21
+ if (stopped || rows.length >= limit || next === undefined) {
22
+ return {
23
+ rows,
24
+ exhausted: stopped || next === undefined,
25
+ scannedPages: pages,
26
+ nextCursor: rows.length >= limit ? (next === undefined ? undefined : cursorOf(next)) : undefined,
27
+ };
28
+ }
29
+ cursor = cursorOf(next);
30
+ if (cursor === undefined)
31
+ break;
32
+ }
33
+ return { rows, exhausted: false, scannedPages: pages, nextCursor: cursor };
34
+ }
35
+ /** Il cursore e' opaco: si estrae dall'URL che l'API ha costruito, mai ricomposto. */
36
+ export function cursorOf(nextUrl) {
37
+ try {
38
+ return new URL(nextUrl).searchParams.get('cursor') ?? undefined;
39
+ }
40
+ catch {
41
+ return undefined;
42
+ }
43
+ }
44
+ //# sourceMappingURL=collect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect.js","sourceRoot":"","sources":["../src/collect.ts"],"names":[],"mappings":"AAgCA,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,SAA+D,EAC/D,OAA4B,EAC5B,KAAa,EACb,QAAQ,GAAG,CAAC;IAEZ,MAAM,IAAI,GAAQ,EAAE,CAAC;IACrB,IAAI,MAA0B,CAAC;IAC/B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,OAAO,KAAK,GAAG,QAAQ,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC;QACrC,KAAK,IAAI,CAAC,CAAC;QAEX,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;YAC1B,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;gBACpB,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM;YACR,CAAC;YACD,IAAI,IAAI,KAAK,MAAM;gBAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACpC,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK;gBAAE,MAAM;QAClC,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC;QAC9B,IAAI,OAAO,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YAC1D,OAAO;gBACL,IAAI;gBACJ,SAAS,EAAE,OAAO,IAAI,IAAI,KAAK,SAAS;gBACxC,YAAY,EAAE,KAAK;gBACnB,UAAU,EAAE,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;aACjG,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QACxB,IAAI,MAAM,KAAK,SAAS;YAAE,MAAM;IAClC,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;AAC7E,CAAC;AAED,sFAAsF;AACtF,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Entry stdio.
4
+ *
5
+ * `serveStdio` riceve una FABBRICA e non un'istanza: nella revisione 2026-07-28
6
+ * il server e' senza sessione, e l'istanza viene fissata dall'apertura della
7
+ * connessione. Passare un server gia' costruito e' il modo v1 di farlo, e qui
8
+ * non funzionerebbe.
9
+ *
10
+ * Nessun `console.log` in questo processo, mai: su stdio quel canale E' il
11
+ * protocollo, e una riga di log ci finisce dentro come frame malformato. La
12
+ * diagnostica va su stderr.
13
+ */
14
+ import { serveStdio } from '@modelcontextprotocol/server/stdio';
15
+ import { createServer } from './server.js';
16
+ const handle = serveStdio(() => createServer(), {
17
+ onerror: (error) => {
18
+ process.stderr.write(`[pokemontcgapi-mcp] ${error.message}\n`);
19
+ },
20
+ });
21
+ for (const signal of ['SIGINT', 'SIGTERM']) {
22
+ process.on(signal, () => {
23
+ void handle.close().finally(() => process.exit(0));
24
+ });
25
+ }
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,YAAY,EAAE,EAAE;IAC9C,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;IACjE,CAAC;CACF,CAAC,CAAC;AAEH,KAAK,MAAM,MAAM,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAU,EAAE,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE;QACtB,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Il budget di contesto.
3
+ *
4
+ * Questo file decide se un agente tiene il server caricato o lo toglie. Ogni
5
+ * risposta che esce da uno strumento finisce nella finestra di contesto del
6
+ * modello e ci resta per tutto il turno, quindi le regole sono tre e non sono
7
+ * negoziabili:
8
+ *
9
+ * 1. **Tabelle, non JSON.** Una tabella allineata costa circa il 40% dei token
10
+ * dello stesso dato in JSON, e i modelli la leggono con meno errori. Il JSON
11
+ * esatto va comunque in `structuredContent`, che il client puo' leggere
12
+ * senza che passi dal testo.
13
+ * 2. **Il taglio si dichiara.** Se restano righe, la risposta lo dice e dice
14
+ * come prenderle. Un troncamento silenzioso fa concludere al modello di aver
15
+ * visto tutto, ed e' il modo in cui un agente risponde con sicurezza a meta'
16
+ * del problema.
17
+ * 3. **I prezzi non si tagliano mai.** Sono poche righe e sono esattamente il
18
+ * motivo della chiamata: buttare via la riga giusta per risparmiare venti
19
+ * token e' il peggior scambio possibile.
20
+ */
21
+ /** Tetto nostro, piu' basso di quello dell'API: 250 righe non sopravvivono a un contesto. */
22
+ export declare const MAX_ROWS = 50;
23
+ export declare function clampLimit(requested: number | undefined): number;
24
+ type Cell = string | number | null | undefined;
25
+ /**
26
+ * Tabella a larghezza fissa. Niente bordi in ASCII: le pipe e i trattini
27
+ * costerebbero token quanto una colonna in piu' e non aggiungono niente per un
28
+ * lettore che non ha bisogno di vederci una griglia.
29
+ */
30
+ export declare function table(headers: readonly string[], rows: readonly (readonly Cell[])[]): string;
31
+ /**
32
+ * La riga che si aggiunge in fondo quando la collezione continua.
33
+ *
34
+ * Il cursore va ripetuto per intero: e' opaco e porta la firma
35
+ * dell'ordinamento, quindi un modello che prova a ricostruirlo ottiene un
36
+ * `INVALID_CURSOR`. Meglio spendere i token del cursore che una chiamata persa.
37
+ */
38
+ export declare function moreLine(meta: {
39
+ total_count?: number;
40
+ } | undefined, nextUrl: string | undefined): string;
41
+ export {};
package/dist/render.js ADDED
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Il budget di contesto.
3
+ *
4
+ * Questo file decide se un agente tiene il server caricato o lo toglie. Ogni
5
+ * risposta che esce da uno strumento finisce nella finestra di contesto del
6
+ * modello e ci resta per tutto il turno, quindi le regole sono tre e non sono
7
+ * negoziabili:
8
+ *
9
+ * 1. **Tabelle, non JSON.** Una tabella allineata costa circa il 40% dei token
10
+ * dello stesso dato in JSON, e i modelli la leggono con meno errori. Il JSON
11
+ * esatto va comunque in `structuredContent`, che il client puo' leggere
12
+ * senza che passi dal testo.
13
+ * 2. **Il taglio si dichiara.** Se restano righe, la risposta lo dice e dice
14
+ * come prenderle. Un troncamento silenzioso fa concludere al modello di aver
15
+ * visto tutto, ed e' il modo in cui un agente risponde con sicurezza a meta'
16
+ * del problema.
17
+ * 3. **I prezzi non si tagliano mai.** Sono poche righe e sono esattamente il
18
+ * motivo della chiamata: buttare via la riga giusta per risparmiare venti
19
+ * token e' il peggior scambio possibile.
20
+ */
21
+ /** Tetto nostro, piu' basso di quello dell'API: 250 righe non sopravvivono a un contesto. */
22
+ export const MAX_ROWS = 50;
23
+ export function clampLimit(requested) {
24
+ if (requested === undefined || !Number.isFinite(requested))
25
+ return 20;
26
+ return Math.max(1, Math.min(Math.trunc(requested), MAX_ROWS));
27
+ }
28
+ function cell(value) {
29
+ if (value === null || value === undefined || value === '')
30
+ return '—';
31
+ return String(value);
32
+ }
33
+ /**
34
+ * Tabella a larghezza fissa. Niente bordi in ASCII: le pipe e i trattini
35
+ * costerebbero token quanto una colonna in piu' e non aggiungono niente per un
36
+ * lettore che non ha bisogno di vederci una griglia.
37
+ */
38
+ export function table(headers, rows) {
39
+ if (rows.length === 0)
40
+ return 'No rows.';
41
+ const widths = headers.map((header, index) => Math.max(header.length, ...rows.map((row) => cell(row[index]).length)));
42
+ const line = (values) => values
43
+ .map((value, index) => cell(value).padEnd(widths[index] ?? 0))
44
+ .join(' ')
45
+ .trimEnd();
46
+ return [line(headers), widths.map((width) => '-'.repeat(width)).join(' '), ...rows.map(line)].join('\n');
47
+ }
48
+ /**
49
+ * La riga che si aggiunge in fondo quando la collezione continua.
50
+ *
51
+ * Il cursore va ripetuto per intero: e' opaco e porta la firma
52
+ * dell'ordinamento, quindi un modello che prova a ricostruirlo ottiene un
53
+ * `INVALID_CURSOR`. Meglio spendere i token del cursore che una chiamata persa.
54
+ */
55
+ export function moreLine(meta, nextUrl) {
56
+ if (nextUrl === undefined)
57
+ return '';
58
+ const cursor = new URL(nextUrl).searchParams.get('cursor') ?? '';
59
+ // Si dice il totale, non "quante ne restano": il resto dipende da quante
60
+ // pagine sono gia' state lette in questa conversazione, che questo processo
61
+ // non sa. Un numero sbagliato qui e' peggio di nessun numero.
62
+ const total = meta?.total_count;
63
+ const scope = total === undefined ? 'More rows' : `${total} rows match in total; more`;
64
+ return `
65
+
66
+ ${scope} available. Call again with cursor="${cursor}".`;
67
+ }
68
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,6FAA6F;AAC7F,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,CAAC;AAE3B,MAAM,UAAU,UAAU,CAAC,SAA6B;IACtD,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,EAAE,CAAC;IACtE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;AAChE,CAAC;AAID,SAAS,IAAI,CAAC,KAAW;IACvB,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,GAAG,CAAC;IACtE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,KAAK,CAAC,OAA0B,EAAE,IAAkC;IAClF,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,UAAU,CAAC;IAEzC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAC3C,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CACvE,CAAC;IAEF,MAAM,IAAI,GAAG,CAAC,MAAuB,EAAU,EAAE,CAC/C,MAAM;SACH,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;SAC7D,IAAI,CAAC,IAAI,CAAC;SACV,OAAO,EAAE,CAAC;IAEf,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5G,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,IAA0C,EAAE,OAA2B;IAC9F,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;IACjE,yEAAyE;IACzE,4EAA4E;IAC5E,8DAA8D;IAC9D,MAAM,KAAK,GAAG,IAAI,EAAE,WAAW,CAAC;IAChC,MAAM,KAAK,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,KAAK,4BAA4B,CAAC;IACvF,OAAO;;EAEP,KAAK,uCAAuC,MAAM,IAAI,CAAC;AACzD,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { McpServer } from '@modelcontextprotocol/server';
2
+ import { MEASURED_ON } from './caveats.js';
3
+ export declare function createServer(): McpServer;
4
+ export { MEASURED_ON };
package/dist/server.js ADDED
@@ -0,0 +1,515 @@
1
+ import { McpServer, fromJsonSchema } from '@modelcontextprotocol/server';
2
+ import { Api, ApiError } from './api.js';
3
+ import { GAMEPLAY_FIELDS, KOREAN_COVERAGE, LANGUAGE_CAVEAT, MEASURED_ON, PRICE_CAVEAT, REGION_CAVEAT, } from './caveats.js';
4
+ import { collect } from './collect.js';
5
+ import { MAX_ROWS, clampLimit, moreLine, table } from './render.js';
6
+ /**
7
+ * Sette strumenti, non sedici.
8
+ *
9
+ * La tentazione e' mappare uno strumento per endpoint: e' meccanico e sembra
10
+ * completo. Costa due cose. La prima e' che `tools/list` sta nel contesto a
11
+ * OGNI turno, quindi sedici schemi si pagano anche nelle conversazioni che non
12
+ * toccano le carte. La seconda, peggiore, e' che costringe il modello a
13
+ * incatenare chiamate — cerca il set, prendi il codice, elenca le carte,
14
+ * prendi i prezzi — e ogni anello e' un punto in cui puo' sbagliare.
15
+ *
16
+ * Qui gli strumenti hanno la forma delle DOMANDE ("quali set giapponesi sono
17
+ * usciti nel 2024", "quanto vale questo Charizard in euro"), e i parametri sono
18
+ * enumerati dove l'API accetterebbe testo libero: un enum e' un errore che il
19
+ * modello non puo' commettere.
20
+ *
21
+ * Cio' che NON e' esposto, e perche':
22
+ * - gli endpoint di prezzo dedicati e il feed delle modifiche: rispondono 404
23
+ * sul servizio in esercizio, e uno strumento che fallisce e' peggio di uno
24
+ * assente perche' il modello lo riprova;
25
+ * - gli export bulk: un agente non deve tirarsi in contesto un dump.
26
+ *
27
+ * L'ottavo strumento e' il riconoscimento da foto, ed e' l'unico che non e' una
28
+ * lettura. Sta qui perche' e' esattamente cio' che un agente multimodale non
29
+ * puo' fare da solo: guardare una carta e riconoscerla dall'illustrazione e'
30
+ * il compito su cui un modello generico allucina con la massima sicurezza —
31
+ * dice "Charizard Base Set" davanti a una ristampa e non ha modo di sapere di
32
+ * aver sbagliato. Qui la risposta e' misurata, e quando e' ambigua lo dichiara.
33
+ */
34
+ const api = new Api();
35
+ /** Solo cio' che esiste davvero. Un enum e' una promessa. */
36
+ const REGIONS = ['WEST', 'JP', 'CN'];
37
+ const LOCALES = ['en', 'ja', 'fr', 'de', 'es', 'it'];
38
+ const READ_ONLY = {
39
+ readOnlyHint: true,
40
+ destructiveHint: false,
41
+ idempotentHint: true,
42
+ openWorldHint: true,
43
+ };
44
+ /** Proiezione compatta: senza, ogni riga porta quaranta campi di cui il modello ne legge sei. */
45
+ const CARD_FIELDS = ['id', 'name', 'number', 'rarity', 'set_code', 'set_name', 'release_date', 'print_region', 'artist_name', 'index_eur'];
46
+ function textResult(text, structured) {
47
+ return { content: [{ type: 'text', text }], structuredContent: structured };
48
+ }
49
+ /**
50
+ * Un errore dell'API torna come `isError`, non come eccezione: il modello deve
51
+ * poterlo leggere e correggersi da solo. `details.valid_fields` e simili sono
52
+ * la parte utile, quindi si riportano invece di essere riassunti.
53
+ */
54
+ function errorResult(error) {
55
+ if (error instanceof ApiError) {
56
+ const detail = error.details === undefined ? '' : `\n${JSON.stringify(error.details)}`;
57
+ return {
58
+ isError: true,
59
+ content: [{ type: 'text', text: `${error.code}: ${error.message}${detail}` }],
60
+ };
61
+ }
62
+ return {
63
+ isError: true,
64
+ content: [{ type: 'text', text: error instanceof Error ? error.message : String(error) }],
65
+ };
66
+ }
67
+ export function createServer() {
68
+ const server = new McpServer({ name: 'pokemontcgapi', version: '0.1.0' }, { capabilities: { tools: {} } });
69
+ // ── 1 · ricerca carte ─────────────────────────────────────────────────────
70
+ server.registerTool('ptcg_search_cards', {
71
+ title: 'Search Pokémon cards',
72
+ description: 'Search the Pokémon TCG card catalogue by name, set, print region, rarity, artist or release window. ' +
73
+ 'Returns one row per printing with its set, rarity, illustrator and euro price index. ' +
74
+ REGION_CAVEAT +
75
+ ' ' +
76
+ LANGUAGE_CAVEAT +
77
+ ' ' +
78
+ GAMEPLAY_FIELDS,
79
+ annotations: READ_ONLY,
80
+ inputSchema: fromJsonSchema({
81
+ type: 'object',
82
+ properties: {
83
+ name: { type: 'string', description: 'Card name or part of it, e.g. "charizard".' },
84
+ set: { type: 'string', description: 'Set code, e.g. "bs", "sv3". Use ptcg_list_sets to find it.' },
85
+ region: { type: 'string', enum: [...REGIONS], description: 'Print region of the set the card belongs to.' },
86
+ rarity: { type: 'string', description: 'Exact printed rarity. Use ptcg_get_reference to list valid values.' },
87
+ artist: { type: 'string', description: 'Illustrator name.' },
88
+ released_from: { type: 'string', description: 'ISO date, inclusive lower bound on the set release date.' },
89
+ released_to: { type: 'string', description: 'ISO date, inclusive upper bound on the set release date.' },
90
+ lang: { type: 'string', enum: [...LOCALES], description: 'Return card names in this locale.' },
91
+ order_by: { type: 'string', description: 'e.g. "-set.releaseDate", "name". Sorting always ends with id.' },
92
+ limit: { type: 'integer', minimum: 1, maximum: MAX_ROWS, description: `Rows, capped at ${MAX_ROWS}.` },
93
+ cursor: { type: 'string', description: 'Opaque cursor from a previous call. Never construct one.' },
94
+ q: {
95
+ type: 'string',
96
+ description: 'Escape hatch: raw Lucene-style query. Field names are camelCase and dotted (set.code, ' +
97
+ 'set.releaseDate, nationalPokedexNumbers) while response keys are snake_case. Only use this ' +
98
+ 'when the named arguments above cannot express the question.',
99
+ },
100
+ },
101
+ additionalProperties: false,
102
+ }),
103
+ }, async (args) => {
104
+ try {
105
+ const clauses = [];
106
+ // Niente wildcard iniziali: l'API le rifiuta con QUERY_UNSUPPORTED, e
107
+ // ha ragione — un pattern che comincia per "*" non ha prefisso su cui
108
+ // cercare. `name:charizard` gia' matcha come sottostringa.
109
+ if (args.name !== undefined && args.name !== '')
110
+ clauses.push(`name:${JSON.stringify(args.name)}`);
111
+ if (args.rarity !== undefined && args.rarity !== '')
112
+ clauses.push(`rarity:${JSON.stringify(args.rarity)}`);
113
+ if (args.artist !== undefined && args.artist !== '')
114
+ clauses.push(`artist:${JSON.stringify(args.artist)}`);
115
+ if (args.released_from !== undefined)
116
+ clauses.push(`set.releaseDate:[${args.released_from} TO *]`);
117
+ if (args.released_to !== undefined)
118
+ clauses.push(`set.releaseDate:[* TO ${args.released_to}]`);
119
+ if (args.q !== undefined && args.q !== '')
120
+ clauses.push(args.q);
121
+ const limit = clampLimit(args.limit);
122
+ // `print_region` non e' un campo della grammatica: sta sul set, non
123
+ // sulla carta. Si scorre finche' non si sono raccolte abbastanza righe,
124
+ // e se la scansione non e' finita lo si DICE.
125
+ const collected = await collect((cursor) => api.get('/v1/cards', {
126
+ q: clauses.length === 0 ? undefined : clauses.join(' '),
127
+ set: args.set,
128
+ lang: args.lang,
129
+ orderBy: args.order_by,
130
+ cursor: cursor ?? args.cursor,
131
+ limit: args.region === undefined ? limit : 100,
132
+ select: CARD_FIELDS,
133
+ }), (card) => (args.region === undefined || card.print_region === args.region ? 'keep' : 'skip'), limit);
134
+ const rows = collected.rows;
135
+ const trailer = collected.nextCursor !== undefined
136
+ ? `
137
+
138
+ More rows available. Call again with cursor="${collected.nextCursor}".`
139
+ : collected.exhausted
140
+ ? ''
141
+ : `
142
+
143
+ Scan stopped after ${collected.scannedPages} pages without reaching the end of the catalogue; there may be more matches.`;
144
+ const text = table(['id', 'name', 'set', 'rarity', 'region', 'released', 'EUR idx'], rows.map((c) => [c.id, c.name, c.set_code, c.rarity, c.print_region, c.release_date, c.index_eur])) + trailer;
145
+ return textResult(text, { cards: rows, complete: collected.exhausted });
146
+ }
147
+ catch (error) {
148
+ return errorResult(error);
149
+ }
150
+ });
151
+ // ── 2 · carte per id ──────────────────────────────────────────────────────
152
+ server.registerTool('ptcg_get_cards', {
153
+ title: 'Get Pokémon cards by id',
154
+ description: 'Fetch one or many cards by id, in a single call. Ids are the printed coordinate — set code, dash, ' +
155
+ 'collector number, e.g. "bs-4" — and the alternate legacy id ("base1-4") resolves on the same route. ' +
156
+ 'Ids that do not exist are omitted from the result; compare requested against found. ' +
157
+ GAMEPLAY_FIELDS,
158
+ annotations: READ_ONLY,
159
+ inputSchema: fromJsonSchema({
160
+ type: 'object',
161
+ properties: {
162
+ ids: {
163
+ type: 'array',
164
+ items: { type: 'string' },
165
+ minItems: 1,
166
+ maxItems: 100,
167
+ description: 'Card ids, 1 to 100.',
168
+ },
169
+ lang: { type: 'string', enum: [...LOCALES] },
170
+ include_prices: { type: 'boolean', description: 'Attach the full price list to each card.' },
171
+ },
172
+ required: ['ids'],
173
+ additionalProperties: false,
174
+ }),
175
+ }, async (args) => {
176
+ try {
177
+ const ids = args.ids.slice(0, 100);
178
+ const body = await api.get('/v1/cards/batch', {
179
+ ids,
180
+ lang: args.lang,
181
+ include: args.include_prices === true ? 'prices' : undefined,
182
+ select: args.include_prices === true ? undefined : CARD_FIELDS,
183
+ });
184
+ const missing = ids.filter((id) => !body.data.some((c) => c.id === id));
185
+ const text = table(['id', 'name', 'set', 'rarity', 'region', 'released', 'EUR idx'], body.data.map((c) => [c.id, c.name, c.set_code, c.rarity, c.print_region, c.release_date, c.index_eur])) +
186
+ (missing.length === 0 ? '' : `\n\nNot found: ${missing.join(', ')}`);
187
+ return textResult(text, { cards: body.data, requested: body.requested, found: body.found, missing });
188
+ }
189
+ catch (error) {
190
+ return errorResult(error);
191
+ }
192
+ });
193
+ // ── 3 · prezzi ────────────────────────────────────────────────────────────
194
+ server.registerTool('ptcg_get_card_prices', {
195
+ title: 'Get prices for a Pokémon card',
196
+ description: 'Every current price observation for one card, each with its source, basis, printing, grade, sample ' +
197
+ 'size and the day it is for. ' +
198
+ PRICE_CAVEAT,
199
+ annotations: READ_ONLY,
200
+ inputSchema: fromJsonSchema({
201
+ type: 'object',
202
+ properties: {
203
+ id: { type: 'string', description: 'Card id, e.g. "bs-4" or "base1-4".' },
204
+ currency: {
205
+ type: 'string',
206
+ enum: ['EUR', 'USD'],
207
+ description: 'Only show rows already denominated in this currency. Nothing is ever converted.',
208
+ },
209
+ },
210
+ required: ['id'],
211
+ additionalProperties: false,
212
+ }),
213
+ }, async (args) => {
214
+ try {
215
+ const card = await api.get(`/v1/cards/${encodeURIComponent(args.id)}`, { include: 'prices' });
216
+ const all = card.prices ?? [];
217
+ const rows = args.currency === undefined ? all : all.filter((p) => p.currency === args.currency);
218
+ // I prezzi non si tagliano: vedi il commento in render.ts.
219
+ const text = `${card.name} (${card.id}) — composite index ${card.index_eur ?? '—'} EUR, last recomputed ${card.last_price_at ?? '—'}\n\n` +
220
+ table(['source', 'variant', 'basis', 'price', 'cur', 'printing', 'grade', 'as_of', 'n'], rows.map((p) => [
221
+ p.source,
222
+ p.variant,
223
+ p.basis,
224
+ p.price,
225
+ p.currency,
226
+ p.printing,
227
+ p.grading === null ? null : `${p.grading.company} ${p.grading.score}`,
228
+ p.as_of,
229
+ p.sample_n,
230
+ ]));
231
+ return textResult(text, {
232
+ card: { id: card.id, name: card.name, index_eur: card.index_eur, last_price_at: card.last_price_at },
233
+ prices: rows,
234
+ });
235
+ }
236
+ catch (error) {
237
+ return errorResult(error);
238
+ }
239
+ });
240
+ // ── 4 · set ───────────────────────────────────────────────────────────────
241
+ server.registerTool('ptcg_list_sets', {
242
+ title: 'List Pokémon TCG sets',
243
+ description: 'Every set with its code, series, print region, release date and printed total. One call answers ' +
244
+ 'questions like "every Japanese set released in 2024". ' +
245
+ REGION_CAVEAT,
246
+ annotations: READ_ONLY,
247
+ inputSchema: fromJsonSchema({
248
+ type: 'object',
249
+ properties: {
250
+ region: { type: 'string', enum: [...REGIONS] },
251
+ series: { type: 'string', description: 'Era or series name, e.g. "Scarlet & Violet".' },
252
+ name: { type: 'string', description: 'Set name or part of it.' },
253
+ released_from: { type: 'string', description: 'ISO date, inclusive lower bound.' },
254
+ released_to: { type: 'string', description: 'ISO date, inclusive upper bound.' },
255
+ order_by: { type: 'string', description: 'e.g. "-release_date" (default) or "release_date".' },
256
+ limit: { type: 'integer', minimum: 1, maximum: MAX_ROWS },
257
+ cursor: { type: 'string' },
258
+ },
259
+ additionalProperties: false,
260
+ }),
261
+ }, async (args) => {
262
+ try {
263
+ const limit = clampLimit(args.limit);
264
+ const orderBy = args.order_by ?? '-release_date';
265
+ // L'ordinamento di default e' per data decrescente: appena una riga e'
266
+ // piu' vecchia del limite inferiore, tutte le successive lo sono, e la
267
+ // scansione puo' fermarsi. E' cio' che rende praticabile "i set
268
+ // giapponesi del 2024" in una sola chiamata.
269
+ const descendingByDate = orderBy === '-release_date';
270
+ const collected = await collect((cursor) => api.get('/v1/sets', {
271
+ region: args.region,
272
+ series: args.series,
273
+ // `q` su /v1/sets e' testo libero, NON la grammatica a campi
274
+ // delle carte: `name:Base` non trova nulla, `Base` si'.
275
+ q: args.name,
276
+ orderBy,
277
+ cursor,
278
+ limit: 100,
279
+ }), (set) => {
280
+ if (set.release_date === null) {
281
+ return args.released_from === undefined && args.released_to === undefined ? 'keep' : 'skip';
282
+ }
283
+ if (args.released_to !== undefined && set.release_date > args.released_to)
284
+ return 'skip';
285
+ if (args.released_from !== undefined && set.release_date < args.released_from) {
286
+ return descendingByDate ? 'stop' : 'skip';
287
+ }
288
+ return 'keep';
289
+ }, limit);
290
+ const rows = collected.rows;
291
+ const trailer = collected.nextCursor !== undefined
292
+ ? `
293
+
294
+ More rows available. Call again with cursor="${collected.nextCursor}".`
295
+ : collected.exhausted
296
+ ? ''
297
+ : `
298
+
299
+ Scan stopped after ${collected.scannedPages} pages; there may be more matching sets.`;
300
+ const text = table(['code', 'name', 'region', 'released', 'printed', 'total'], rows.map((set) => [set.code, set.name, set.region, set.release_date, set.printed_total, set.total])) + trailer;
301
+ return textResult(text, { sets: rows, complete: collected.exhausted });
302
+ }
303
+ catch (error) {
304
+ return errorResult(error);
305
+ }
306
+ });
307
+ // ── 5 · vocabolari ────────────────────────────────────────────────────────
308
+ server.registerTool('ptcg_get_reference', {
309
+ title: 'List the closed vocabularies',
310
+ description: 'The exact strings the catalogue uses for types, supertypes and rarities. Call this before filtering ' +
311
+ 'on a rarity or a type rather than guessing the wording — "Rare Rainbow" and "Rainbow Rare" are not ' +
312
+ 'the same string, and only one of them matches. Note that subtypes is currently an empty list.',
313
+ annotations: READ_ONLY,
314
+ inputSchema: fromJsonSchema({
315
+ type: 'object',
316
+ properties: {
317
+ vocabulary: { type: 'string', enum: ['types', 'supertypes', 'rarities', 'subtypes'] },
318
+ },
319
+ required: ['vocabulary'],
320
+ additionalProperties: false,
321
+ }),
322
+ }, async (args) => {
323
+ try {
324
+ const body = await api.get(`/v1/${args.vocabulary}`);
325
+ const note = body.data.length === 0 ? ' (this vocabulary is not populated in the catalogue today)' : '';
326
+ return textResult(`${args.vocabulary}${note}:\n${body.data.join('\n')}`, {
327
+ vocabulary: args.vocabulary,
328
+ values: body.data,
329
+ });
330
+ }
331
+ catch (error) {
332
+ return errorResult(error);
333
+ }
334
+ });
335
+ // ── 6 · illustratori ──────────────────────────────────────────────────────
336
+ server.registerTool('ptcg_list_artists', {
337
+ title: 'List card illustrators',
338
+ description: 'Illustrators with the number of cards each has drawn, deduplicated across 30 years of printings. ' +
339
+ 'Use the returned name with the artist argument of ptcg_search_cards to get their cards.',
340
+ annotations: READ_ONLY,
341
+ inputSchema: fromJsonSchema({
342
+ type: 'object',
343
+ properties: {
344
+ name: { type: 'string', description: 'Illustrator name or part of it.' },
345
+ limit: { type: 'integer', minimum: 1, maximum: MAX_ROWS },
346
+ cursor: { type: 'string' },
347
+ },
348
+ additionalProperties: false,
349
+ }),
350
+ }, async (args) => {
351
+ try {
352
+ const body = await api.get('/v1/artists', {
353
+ q: args.name,
354
+ limit: clampLimit(args.limit),
355
+ cursor: args.cursor,
356
+ });
357
+ const text = table(['slug', 'name', 'cards'], body.data.map((a) => [a.slug, a.name, a.card_count])) +
358
+ moreLine(body.meta, body.links?.next);
359
+ return textResult(text, { artists: body.data, meta: body.meta ?? null });
360
+ }
361
+ catch (error) {
362
+ return errorResult(error);
363
+ }
364
+ });
365
+ // ── 7 · copertura ─────────────────────────────────────────────────────────
366
+ /**
367
+ * Questo strumento esiste per una ragione sola, ed e' la regola 4.
368
+ *
369
+ * Un modello a cui viene chiesto "questa API ha le carte coreane?" altrimenti
370
+ * risponderebbe deducendolo da un enum o da un nome di prodotto, cioe'
371
+ * inventando. Qui la risposta e' un fatto misurato che arriva da una chiamata,
372
+ * ed e' scritta a lettere.
373
+ */
374
+ server.registerTool('ptcg_get_catalogue_status', {
375
+ title: 'What this catalogue actually covers',
376
+ description: 'Live counts and coverage for the catalogue: sets per print region, card total, which locales carry ' +
377
+ 'card names, and which data is explicitly NOT present. Call this before telling a user what the API ' +
378
+ 'can and cannot answer.',
379
+ annotations: READ_ONLY,
380
+ inputSchema: fromJsonSchema({ type: 'object', properties: {}, additionalProperties: false }),
381
+ }, async () => {
382
+ try {
383
+ const status = await api.get('/v1/status');
384
+ const perRegion = await Promise.all(['WEST', 'JP', 'CN', 'KR'].map(async (region) => {
385
+ const body = await api.get('/v1/sets', { region, limit: 1 });
386
+ return { region, sets: body.meta?.total_count ?? 0 };
387
+ }));
388
+ const coverage = {
389
+ measured_at: new Date().toISOString(),
390
+ sets: status.catalog.sets,
391
+ cards: status.catalog.cards,
392
+ sealed_products: status.catalog.sealed,
393
+ sets_by_print_region: Object.fromEntries(perRegion.map((r) => [r.region, r.sets])),
394
+ card_name_locales: [...LOCALES],
395
+ price_sources: ['TCGPLAYER', 'CARDMARKET', 'CARDTRADER', 'EBAY_SOLD', 'PTCG_INDEX', 'COMMUNITY'],
396
+ coverage_notes: [KOREAN_COVERAGE, GAMEPLAY_FIELDS, LANGUAGE_CAVEAT],
397
+ };
398
+ const text = [
399
+ `Catalogue: ${status.catalog.sets} sets, ${status.catalog.cards} cards, ${status.catalog.sealed} sealed products.`,
400
+ '',
401
+ table(['print region', 'sets'], perRegion.map((r) => [r.region, r.sets])),
402
+ '',
403
+ `Card-name locales: ${LOCALES.join(', ')}`,
404
+ `Price sources: ${coverage.price_sources.join(', ')}`,
405
+ '',
406
+ KOREAN_COVERAGE,
407
+ '',
408
+ GAMEPLAY_FIELDS,
409
+ ].join('\n');
410
+ return textResult(text, coverage);
411
+ }
412
+ catch (error) {
413
+ return errorResult(error);
414
+ }
415
+ });
416
+ // ── 8 · riconoscimento da foto ────────────────────────────────────────────
417
+ /**
418
+ * L'unico strumento che scrive qualcosa (un'immagine) e l'unico che costa 25
419
+ * crediti invece di uno. Entrambe le cose stanno nella descrizione, perche' un
420
+ * modello che non le sa lo mette in un ciclo.
421
+ *
422
+ * Il contratto verso il modello e' scritto per essere difficile da sbagliare:
423
+ * la risposta dice a lettere cosa fare in ognuno dei tre casi, invece di
424
+ * lasciare che sia il modello a dedurre cosa significhi `ambiguous`. Un
425
+ * assistente che consegna la ristampa sbagliata in una collezione fa un danno
426
+ * che l'utente scopre mesi dopo, quando prova a venderla.
427
+ */
428
+ server.registerTool('ptcg_identify_card_from_image', {
429
+ title: 'Identify a card from a photo',
430
+ description: 'Recognise a Pokemon card from a photograph and return ranked candidates. Use this instead of guessing ' +
431
+ 'from what you see in an image: reprints share their artwork, so visual identification alone cannot ' +
432
+ 'name a printing, and this tool says so when it cannot. Costs 25 credits per call against 1 for a ' +
433
+ 'lookup — do not call it in a loop. Pass `set` or `region` when the user has told you either.',
434
+ annotations: {
435
+ readOnlyHint: true,
436
+ destructiveHint: false,
437
+ // NON idempotente: la stessa foto costa 25 crediti ogni volta che la
438
+ // mandi. Dichiararlo idempotente inviterebbe un client a ritentare.
439
+ idempotentHint: false,
440
+ openWorldHint: true,
441
+ },
442
+ inputSchema: fromJsonSchema({
443
+ type: 'object',
444
+ properties: {
445
+ image_base64: {
446
+ type: 'string',
447
+ description: 'The photograph, base64-encoded. A data: URL prefix is accepted and stripped.',
448
+ },
449
+ set: { type: 'string', description: 'Set code to restrict to, e.g. "sv3". Resolves reprint ties.' },
450
+ region: { type: 'string', enum: [...REGIONS], description: 'Print region to restrict to.' },
451
+ top_k: { type: 'integer', minimum: 1, maximum: 10, description: 'Candidates to return. Default 3.' },
452
+ },
453
+ required: ['image_base64'],
454
+ additionalProperties: false,
455
+ }),
456
+ }, async ({ image_base64, set, region, top_k }) => {
457
+ try {
458
+ const cleaned = image_base64.includes(',') && image_base64.startsWith('data:')
459
+ ? image_base64.slice(image_base64.indexOf(',') + 1)
460
+ : image_base64;
461
+ const bytes = Uint8Array.from(Buffer.from(cleaned, 'base64'));
462
+ if (bytes.byteLength === 0) {
463
+ throw new ApiError(400, { code: 'INVALID_PARAMETER', message: 'image_base64 decoded to nothing.' });
464
+ }
465
+ const fields = {};
466
+ if (set !== undefined)
467
+ fields['set'] = set;
468
+ if (region !== undefined)
469
+ fields['region'] = region;
470
+ if (top_k !== undefined)
471
+ fields['top_k'] = String(top_k);
472
+ const body = await api.postImage('/v1/vision/identify', bytes, fields);
473
+ // La riga di istruzione viene PRIMA della tabella, non dopo: un modello
474
+ // che legge dall'alto deve incontrare il vincolo prima dei dati su cui
475
+ // sarebbe tentato di agire.
476
+ const verdict = body.data.decision === 'match'
477
+ ? `MATCH — one candidate is clearly closest. Safe to treat ${body.data.id} as the card.`
478
+ : body.data.decision === 'ambiguous'
479
+ ? 'AMBIGUOUS — two or more printings share this illustration and cannot be told apart from the ' +
480
+ 'image. Do NOT pick one. Present the candidates to the user, or ask which set it came from ' +
481
+ 'and call again with `set`.'
482
+ : body.meta.regions_detected === 0
483
+ ? 'NO MATCH — and no card-shaped region was found in the photo at all. Ask for a photo where ' +
484
+ 'the card fills more of the frame, on a plain background. This is a framing problem, not ' +
485
+ 'evidence that the card is missing from the catalogue.'
486
+ : 'NO MATCH — a card was found in the photo but nothing in the catalogue is close enough. ' +
487
+ 'Do not guess a name from what you see.';
488
+ const rows = body.data.candidates.map((c) => [
489
+ c.id,
490
+ c.name,
491
+ `${c.set.code} ${c.number}`,
492
+ c.set.print_region,
493
+ c.rarity ?? '—',
494
+ c.distance,
495
+ ]);
496
+ const text = [
497
+ verdict,
498
+ '',
499
+ rows.length === 0
500
+ ? 'No candidates within range.'
501
+ : table(['id', 'name', 'set / no.', 'region', 'rarity', 'distance'], rows),
502
+ '',
503
+ 'distance is 0-512, lower is closer; real matches land well under 150. ' +
504
+ `Compared against ${body.meta.cards_indexed} indexed card images.`,
505
+ ].join('\n');
506
+ return textResult(text, body.data);
507
+ }
508
+ catch (error) {
509
+ return errorResult(error);
510
+ }
511
+ });
512
+ return server;
513
+ }
514
+ export { MEASURED_ON };
515
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@pokemontcgapi/mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for the Pokémon TCG API: search cards, sets, illustrators and prices from any MCP client.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "engines": { "node": ">=20" },
8
+ "bin": { "pokemontcgapi-mcp": "dist/index.js" },
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "files": ["dist", "README.md"],
12
+ "mcpName": "com.pokemontcgapi/mcp",
13
+ "keywords": ["mcp", "modelcontextprotocol", "pokemon", "pokemon-tcg", "tcg", "card-prices", "agent", "ai"],
14
+ "repository": { "type": "git", "url": "git+https://github.com/pokemontcgapi/mcp-server.git" },
15
+ "homepage": "https://pokemontcgapi.com/mcp",
16
+ "bugs": { "url": "https://github.com/pokemontcgapi/mcp-server/issues" },
17
+ "scripts": {
18
+ "build": "tsc -p tsconfig.json",
19
+ "typecheck": "tsc -p tsconfig.json --noEmit",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "dependencies": {
23
+ "@modelcontextprotocol/server": "^2.0.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^22.10.7",
27
+ "typescript": "^5.7.3"
28
+ }
29
+ }