vatnode-mcp 0.1.1 → 0.2.1

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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +25 -0
  3. package/dist/index.js +26 -825
  4. package/package.json +19 -16
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 vatnode
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 CHANGED
@@ -1,6 +1,7 @@
1
1
  # vatnode-mcp
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/vatnode-mcp)](https://www.npmjs.com/package/vatnode-mcp)
4
+ [![Tests](https://github.com/vatnode/vatnode-mcp/actions/workflows/test.yml/badge.svg)](https://github.com/vatnode/vatnode-mcp/actions/workflows/test.yml)
4
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
6
 
6
7
  Official [Model Context Protocol](https://modelcontextprotocol.io) server for **[vatnode](https://vatnode.dev)** — VAT validation and EU tax data for AI agents.
@@ -119,6 +120,30 @@ Any MCP-compatible client can connect — point it at `npx -y vatnode-mcp` and (
119
120
 
120
121
  ---
121
122
 
123
+ ## Contributing
124
+
125
+ Bug reports and PRs welcome. Open an issue first for non-trivial changes so we can align on direction.
126
+
127
+ ```bash
128
+ git clone https://github.com/vatnode/vatnode-mcp.git
129
+ cd vatnode-mcp
130
+ npm install
131
+ npm test
132
+ ```
133
+
134
+ ## Releasing
135
+
136
+ Releases are published to npm via [Trusted Publishing](https://docs.npmjs.com/trusted-publishers) — no NPM_TOKEN secret, every release signed with [npm provenance](https://docs.npmjs.com/generating-provenance-statements).
137
+
138
+ ```bash
139
+ # bump version in package.json (and VERSION in src/index.ts), commit, then:
140
+ git tag v0.2.1
141
+ git push --tags
142
+ ```
143
+
144
+ CI on `.github/workflows/release.yml` picks up the tag and publishes.
145
+
122
146
  ## License
123
147
 
124
148
  MIT
149
+
package/dist/index.js CHANGED
@@ -3,818 +3,15 @@
3
3
  // src/index.ts
4
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
- import { z as z2 } from "zod";
7
-
8
- // ../../packages/eu-vat-rates/dist/index.js
9
- var eu_vat_rates_default = {
10
- version: "2026-05-19",
11
- source: "European Commission TEDB",
12
- rates: {
13
- AD: {
14
- country: "Andorra",
15
- currency: "EUR",
16
- eu_member: false,
17
- vat_name: "Impost General Indirecte",
18
- vat_abbr: "IGI",
19
- standard: 4.5,
20
- reduced: [
21
- 1,
22
- 2.5
23
- ],
24
- super_reduced: null,
25
- parking: null,
26
- format: "1 letter + 6 digits + 1 letter",
27
- pattern: "^[A-Z]\\d{6}[A-Z]$"
28
- },
29
- AL: {
30
- country: "Albania",
31
- currency: "ALL",
32
- eu_member: false,
33
- vat_name: "Tatimi mbi vler\xEBn e shtuar",
34
- vat_abbr: "TVSH",
35
- standard: 20,
36
- reduced: [
37
- 6,
38
- 10
39
- ],
40
- super_reduced: null,
41
- parking: null,
42
- format: "1 letter + 8 digits + 1 letter",
43
- pattern: "^[A-Z]\\d{8}[A-Z]$"
44
- },
45
- AT: {
46
- country: "Austria",
47
- currency: "EUR",
48
- eu_member: true,
49
- vat_name: "Umsatzsteuer",
50
- vat_abbr: "USt",
51
- standard: 20,
52
- reduced: [
53
- 10,
54
- 13,
55
- 19
56
- ],
57
- super_reduced: null,
58
- parking: null,
59
- format: "ATU + 8 digits",
60
- pattern: "^ATU\\d{8}$"
61
- },
62
- BA: {
63
- country: "Bosnia and Herzegovina",
64
- currency: "BAM",
65
- eu_member: false,
66
- vat_name: "Porez na dodanu vrijednost",
67
- vat_abbr: "PDV",
68
- standard: 17,
69
- reduced: [],
70
- super_reduced: null,
71
- parking: null,
72
- format: "12 digits",
73
- pattern: "^\\d{12}$"
74
- },
75
- BE: {
76
- country: "Belgium",
77
- currency: "EUR",
78
- eu_member: true,
79
- vat_name: "Belasting over de toegevoegde waarde",
80
- vat_abbr: "BTW",
81
- standard: 21,
82
- reduced: [
83
- 6,
84
- 12
85
- ],
86
- super_reduced: null,
87
- parking: 12,
88
- format: "BE + 0/1 + 9 digits",
89
- pattern: "^BE[01]\\d{9}$"
90
- },
91
- BG: {
92
- country: "Bulgaria",
93
- currency: "EUR",
94
- eu_member: true,
95
- vat_name: "\u0414\u0430\u043D\u044A\u043A \u0432\u044A\u0440\u0445\u0443 \u0434\u043E\u0431\u0430\u0432\u0435\u043D\u0430\u0442\u0430 \u0441\u0442\u043E\u0439\u043D\u043E\u0441\u0442",
96
- vat_abbr: "\u0414\u0414\u0421",
97
- standard: 20,
98
- reduced: [
99
- 9
100
- ],
101
- super_reduced: null,
102
- parking: null,
103
- format: "BG + 9\u201310 digits",
104
- pattern: "^BG\\d{9,10}$"
105
- },
106
- CH: {
107
- country: "Switzerland",
108
- currency: "CHF",
109
- eu_member: false,
110
- vat_name: "Mehrwertsteuer",
111
- vat_abbr: "MWST",
112
- standard: 8.1,
113
- reduced: [
114
- 2.6,
115
- 3.8
116
- ],
117
- super_reduced: null,
118
- parking: null,
119
- format: "CHE-NNN.NNN.NNN (+ MWST/TVA/IVA)",
120
- pattern: "^CHE-?\\d{3}\\.?\\d{3}\\.?\\d{3}[ ]?(MWST|TVA|IVA)?$"
121
- },
122
- CY: {
123
- country: "Cyprus",
124
- currency: "EUR",
125
- eu_member: true,
126
- vat_name: "\u03A6\u03CC\u03C1\u03BF\u03C2 \u03A0\u03C1\u03BF\u03C3\u03C4\u03B9\u03B8\u03AD\u03BC\u03B5\u03BD\u03B7\u03C2 \u0391\u03BE\u03AF\u03B1\u03C2",
127
- vat_abbr: "\u03A6\u03A0\u0391",
128
- standard: 19,
129
- reduced: [
130
- 5,
131
- 9
132
- ],
133
- super_reduced: 3,
134
- parking: null,
135
- format: "CY + 8 digits + 1 letter",
136
- pattern: "^CY\\d{8}[A-Z]$"
137
- },
138
- CZ: {
139
- country: "Czech Republic",
140
- currency: "CZK",
141
- eu_member: true,
142
- vat_name: "Da\u0148 z p\u0159idan\xE9 hodnoty",
143
- vat_abbr: "DPH",
144
- standard: 21,
145
- reduced: [
146
- 12
147
- ],
148
- super_reduced: null,
149
- parking: null,
150
- format: "CZ + 8\u201310 digits",
151
- pattern: "^CZ\\d{8,10}$"
152
- },
153
- DE: {
154
- country: "Germany",
155
- currency: "EUR",
156
- eu_member: true,
157
- vat_name: "Mehrwertsteuer",
158
- vat_abbr: "MwSt",
159
- standard: 19,
160
- reduced: [
161
- 7
162
- ],
163
- super_reduced: null,
164
- parking: null,
165
- format: "DE + 9 digits",
166
- pattern: "^DE\\d{9}$"
167
- },
168
- DK: {
169
- country: "Denmark",
170
- currency: "DKK",
171
- eu_member: true,
172
- vat_name: "Moms",
173
- vat_abbr: "moms",
174
- standard: 25,
175
- reduced: [],
176
- super_reduced: null,
177
- parking: null,
178
- format: "DK + 8 digits",
179
- pattern: "^DK\\d{8}$"
180
- },
181
- EE: {
182
- country: "Estonia",
183
- currency: "EUR",
184
- eu_member: true,
185
- vat_name: "K\xE4ibemaks",
186
- vat_abbr: "km",
187
- standard: 24,
188
- reduced: [
189
- 9,
190
- 13
191
- ],
192
- super_reduced: null,
193
- parking: null,
194
- format: "EE + 9 digits",
195
- pattern: "^EE\\d{9}$"
196
- },
197
- ES: {
198
- country: "Spain",
199
- currency: "EUR",
200
- eu_member: true,
201
- vat_name: "Impuesto sobre el Valor A\xF1adido",
202
- vat_abbr: "IVA",
203
- standard: 21,
204
- reduced: [
205
- 10
206
- ],
207
- super_reduced: 4,
208
- parking: null,
209
- format: "ES + letter/digit + 7 digits + letter/digit",
210
- pattern: "^ES[A-Z0-9]\\d{7}[A-Z0-9]$"
211
- },
212
- FI: {
213
- country: "Finland",
214
- currency: "EUR",
215
- eu_member: true,
216
- vat_name: "Arvonlis\xE4vero",
217
- vat_abbr: "ALV",
218
- standard: 25.5,
219
- reduced: [
220
- 10,
221
- 13.5
222
- ],
223
- super_reduced: null,
224
- parking: null,
225
- format: "FI + 8 digits",
226
- pattern: "^FI\\d{8}$"
227
- },
228
- FR: {
229
- country: "France",
230
- currency: "EUR",
231
- eu_member: true,
232
- vat_name: "Taxe sur la valeur ajout\xE9e",
233
- vat_abbr: "TVA",
234
- standard: 20,
235
- reduced: [
236
- 0.9,
237
- 1.05,
238
- 5.5,
239
- 8.5,
240
- 10,
241
- 13
242
- ],
243
- super_reduced: 2.1,
244
- parking: null,
245
- format: "FR + 2 alphanumeric + 9 digits",
246
- pattern: "^FR[A-HJ-NP-Z0-9]{2}\\d{9}$"
247
- },
248
- GB: {
249
- country: "United Kingdom",
250
- currency: "GBP",
251
- eu_member: false,
252
- vat_name: "Value Added Tax",
253
- vat_abbr: "VAT",
254
- standard: 20,
255
- reduced: [
256
- 5
257
- ],
258
- super_reduced: null,
259
- parking: null,
260
- format: "GB + 9 digits, 12 digits, or GD/HA + 3 digits",
261
- pattern: "^GB(\\d{9}|\\d{12}|GD\\d{3}|HA\\d{3})$"
262
- },
263
- GE: {
264
- country: "Georgia",
265
- currency: "GEL",
266
- eu_member: false,
267
- vat_name: "\u10D3\u10D0\u10DB\u10D0\u10E2\u10D4\u10D1\u10E3\u10DA\u10D8 \u10E6\u10D8\u10E0\u10D4\u10D1\u10E3\u10DA\u10D4\u10D1\u10D8\u10E1 \u10D2\u10D0\u10D3\u10D0\u10E1\u10D0\u10EE\u10D0\u10D3\u10D8",
268
- vat_abbr: "\u10D3\u10E6\u10D2",
269
- standard: 18,
270
- reduced: [],
271
- super_reduced: null,
272
- parking: null,
273
- format: "9 digits",
274
- pattern: "^\\d{9}$"
275
- },
276
- GR: {
277
- country: "Greece",
278
- currency: "EUR",
279
- eu_member: true,
280
- vat_name: "\u03A6\u03CC\u03C1\u03BF\u03C2 \u03A0\u03C1\u03BF\u03C3\u03C4\u03B9\u03B8\u03AD\u03BC\u03B5\u03BD\u03B7\u03C2 \u0391\u03BE\u03AF\u03B1\u03C2",
281
- vat_abbr: "\u03A6\u03A0\u0391",
282
- standard: 24,
283
- reduced: [
284
- 6,
285
- 13,
286
- 17
287
- ],
288
- super_reduced: 4,
289
- parking: 13,
290
- format: "EL + 9 digits",
291
- pattern: "^EL\\d{9}$"
292
- },
293
- HR: {
294
- country: "Croatia",
295
- currency: "EUR",
296
- eu_member: true,
297
- vat_name: "Porez na dodanu vrijednost",
298
- vat_abbr: "PDV",
299
- standard: 25,
300
- reduced: [
301
- 5,
302
- 13
303
- ],
304
- super_reduced: null,
305
- parking: null,
306
- format: "HR + 11 digits",
307
- pattern: "^HR\\d{11}$"
308
- },
309
- HU: {
310
- country: "Hungary",
311
- currency: "HUF",
312
- eu_member: true,
313
- vat_name: "\xC1ltal\xE1nos forgalmi ad\xF3",
314
- vat_abbr: "\xC1FA",
315
- standard: 27,
316
- reduced: [
317
- 5,
318
- 18
319
- ],
320
- super_reduced: null,
321
- parking: null,
322
- format: "HU + 8 digits",
323
- pattern: "^HU\\d{8}$"
324
- },
325
- IE: {
326
- country: "Ireland",
327
- currency: "EUR",
328
- eu_member: true,
329
- vat_name: "Value Added Tax",
330
- vat_abbr: "VAT",
331
- standard: 23,
332
- reduced: [
333
- 9,
334
- 13.5
335
- ],
336
- super_reduced: null,
337
- parking: null,
338
- format: "IE + 7 digits + 1\u20132 letters",
339
- pattern: "^IE\\d{7}[A-W][A-IW]?$|^IE\\d[A-Z+*]\\d{5}[A-W]$"
340
- },
341
- IS: {
342
- country: "Iceland",
343
- currency: "ISK",
344
- eu_member: false,
345
- vat_name: "Vir\xF0isaukaskattur",
346
- vat_abbr: "VSK",
347
- standard: 24,
348
- reduced: [
349
- 11
350
- ],
351
- super_reduced: null,
352
- parking: null,
353
- format: "5\u20136 digits",
354
- pattern: "^\\d{5,6}$"
355
- },
356
- IT: {
357
- country: "Italy",
358
- currency: "EUR",
359
- eu_member: true,
360
- vat_name: "Imposta sul valore aggiunto",
361
- vat_abbr: "IVA",
362
- standard: 22,
363
- reduced: [
364
- 5,
365
- 10
366
- ],
367
- super_reduced: 4,
368
- parking: null,
369
- format: "IT + 11 digits",
370
- pattern: "^IT\\d{11}$"
371
- },
372
- LI: {
373
- country: "Liechtenstein",
374
- currency: "CHF",
375
- eu_member: false,
376
- vat_name: "Mehrwertsteuer",
377
- vat_abbr: "MWST",
378
- standard: 8.1,
379
- reduced: [
380
- 2.6,
381
- 3.8
382
- ],
383
- super_reduced: null,
384
- parking: null,
385
- format: "5 digits",
386
- pattern: "^\\d{5}$"
387
- },
388
- LT: {
389
- country: "Lithuania",
390
- currency: "EUR",
391
- eu_member: true,
392
- vat_name: "Prid\u0117tin\u0117s vert\u0117s mokestis",
393
- vat_abbr: "PVM",
394
- standard: 21,
395
- reduced: [
396
- 5,
397
- 12
398
- ],
399
- super_reduced: null,
400
- parking: null,
401
- format: "LT + 9 or 12 digits",
402
- pattern: "^LT(\\d{9}|\\d{12})$"
403
- },
404
- LU: {
405
- country: "Luxembourg",
406
- currency: "EUR",
407
- eu_member: true,
408
- vat_name: "Taxe sur la valeur ajout\xE9e",
409
- vat_abbr: "TVA",
410
- standard: 17,
411
- reduced: [
412
- 8,
413
- 14
414
- ],
415
- super_reduced: 3,
416
- parking: 14,
417
- format: "LU + 8 digits",
418
- pattern: "^LU\\d{8}$"
419
- },
420
- LV: {
421
- country: "Latvia",
422
- currency: "EUR",
423
- eu_member: true,
424
- vat_name: "Pievienot\u0101s v\u0113rt\u012Bbas nodoklis",
425
- vat_abbr: "PVN",
426
- standard: 21,
427
- reduced: [
428
- 5,
429
- 12
430
- ],
431
- super_reduced: null,
432
- parking: null,
433
- format: "LV + 11 digits",
434
- pattern: "^LV\\d{11}$"
435
- },
436
- MC: {
437
- country: "Monaco",
438
- currency: "EUR",
439
- eu_member: false,
440
- vat_name: "Taxe sur la valeur ajout\xE9e",
441
- vat_abbr: "TVA",
442
- standard: 20,
443
- reduced: [
444
- 5.5,
445
- 10
446
- ],
447
- super_reduced: 2.1,
448
- parking: null,
449
- format: "FR + 2 alphanumeric + 9 digits",
450
- pattern: "^FR[A-HJ-NP-Z0-9]{2}\\d{9}$"
451
- },
452
- MD: {
453
- country: "Moldova",
454
- currency: "MDL",
455
- eu_member: false,
456
- vat_name: "Taxa pe valoarea ad\u0103ugat\u0103",
457
- vat_abbr: "TVA",
458
- standard: 20,
459
- reduced: [
460
- 8
461
- ],
462
- super_reduced: null,
463
- parking: null,
464
- format: "7 digits",
465
- pattern: "^\\d{7}$"
466
- },
467
- ME: {
468
- country: "Montenegro",
469
- currency: "EUR",
470
- eu_member: false,
471
- vat_name: "Porez na dodatu vrijednost",
472
- vat_abbr: "PDV",
473
- standard: 21,
474
- reduced: [
475
- 7,
476
- 15
477
- ],
478
- super_reduced: null,
479
- parking: null,
480
- format: "8 digits",
481
- pattern: "^\\d{8}$"
482
- },
483
- MK: {
484
- country: "North Macedonia",
485
- currency: "MKD",
486
- eu_member: false,
487
- vat_name: "\u0414\u0430\u043D\u043E\u043A \u043D\u0430 \u0434\u043E\u0434\u0430\u0434\u0435\u043D\u0430 \u0432\u0440\u0435\u0434\u043D\u043E\u0441\u0442",
488
- vat_abbr: "\u0414\u0414\u0412",
489
- standard: 18,
490
- reduced: [
491
- 5,
492
- 10
493
- ],
494
- super_reduced: null,
495
- parking: null,
496
- format: "MK + 13 digits",
497
- pattern: "^MK\\d{13}$"
498
- },
499
- MT: {
500
- country: "Malta",
501
- currency: "EUR",
502
- eu_member: true,
503
- vat_name: "Taxxa tal-Valur Mi\u017Cjud",
504
- vat_abbr: "VAT",
505
- standard: 18,
506
- reduced: [
507
- 5,
508
- 7
509
- ],
510
- super_reduced: null,
511
- parking: 12,
512
- format: "MT + 8 digits",
513
- pattern: "^MT\\d{8}$"
514
- },
515
- NL: {
516
- country: "Netherlands",
517
- currency: "EUR",
518
- eu_member: true,
519
- vat_name: "Belasting over de toegevoegde waarde",
520
- vat_abbr: "btw",
521
- standard: 21,
522
- reduced: [
523
- 9
524
- ],
525
- super_reduced: null,
526
- parking: null,
527
- format: "NL + 9 digits + B + 2 digits",
528
- pattern: "^NL\\d{9}B\\d{2}$"
529
- },
530
- NO: {
531
- country: "Norway",
532
- currency: "NOK",
533
- eu_member: false,
534
- vat_name: "Merverdiavgift",
535
- vat_abbr: "MVA",
536
- standard: 25,
537
- reduced: [
538
- 12,
539
- 15
540
- ],
541
- super_reduced: null,
542
- parking: null,
543
- format: "9 digits + MVA",
544
- pattern: "^\\d{9}MVA$"
545
- },
546
- PL: {
547
- country: "Poland",
548
- currency: "PLN",
549
- eu_member: true,
550
- vat_name: "Podatek od towar\xF3w i us\u0142ug",
551
- vat_abbr: "VAT",
552
- standard: 23,
553
- reduced: [
554
- 5,
555
- 8
556
- ],
557
- super_reduced: 8,
558
- parking: null,
559
- format: "PL + 10 digits",
560
- pattern: "^PL\\d{10}$"
561
- },
562
- PT: {
563
- country: "Portugal",
564
- currency: "EUR",
565
- eu_member: true,
566
- vat_name: "Imposto sobre o Valor Acrescentado",
567
- vat_abbr: "IVA",
568
- standard: 23,
569
- reduced: [
570
- 6,
571
- 13,
572
- 16,
573
- 22
574
- ],
575
- super_reduced: 6,
576
- parking: 13,
577
- format: "PT + 9 digits",
578
- pattern: "^PT\\d{9}$"
579
- },
580
- RO: {
581
- country: "Romania",
582
- currency: "RON",
583
- eu_member: true,
584
- vat_name: "Taxa pe valoarea ad\u0103ugat\u0103",
585
- vat_abbr: "TVA",
586
- standard: 21,
587
- reduced: [
588
- 11
589
- ],
590
- super_reduced: null,
591
- parking: null,
592
- format: "RO + 2\u201310 digits",
593
- pattern: "^RO\\d{2,10}$"
594
- },
595
- RS: {
596
- country: "Serbia",
597
- currency: "RSD",
598
- eu_member: false,
599
- vat_name: "Porez na dodatu vrednost",
600
- vat_abbr: "PDV",
601
- standard: 20,
602
- reduced: [
603
- 10
604
- ],
605
- super_reduced: null,
606
- parking: null,
607
- format: "9 digits",
608
- pattern: "^\\d{9}$"
609
- },
610
- SE: {
611
- country: "Sweden",
612
- currency: "SEK",
613
- eu_member: true,
614
- vat_name: "Merv\xE4rdesskatt",
615
- vat_abbr: "moms",
616
- standard: 25,
617
- reduced: [
618
- 6,
619
- 12
620
- ],
621
- super_reduced: null,
622
- parking: null,
623
- format: "SE + 10 digits + 01",
624
- pattern: "^SE\\d{10}01$"
625
- },
626
- SI: {
627
- country: "Slovenia",
628
- currency: "EUR",
629
- eu_member: true,
630
- vat_name: "Davek na dodano vrednost",
631
- vat_abbr: "DDV",
632
- standard: 22,
633
- reduced: [
634
- 5,
635
- 9.5
636
- ],
637
- super_reduced: null,
638
- parking: null,
639
- format: "SI + 8 digits",
640
- pattern: "^SI\\d{8}$"
641
- },
642
- SK: {
643
- country: "Slovakia",
644
- currency: "EUR",
645
- eu_member: true,
646
- vat_name: "Da\u0148 z pridanej hodnoty",
647
- vat_abbr: "DPH",
648
- standard: 23,
649
- reduced: [
650
- 5,
651
- 19
652
- ],
653
- super_reduced: null,
654
- parking: null,
655
- format: "SK + 10 digits",
656
- pattern: "^SK\\d{10}$"
657
- },
658
- TR: {
659
- country: "Turkey",
660
- currency: "TRY",
661
- eu_member: false,
662
- vat_name: "Katma De\u011Fer Vergisi",
663
- vat_abbr: "KDV",
664
- standard: 20,
665
- reduced: [
666
- 1,
667
- 10
668
- ],
669
- super_reduced: null,
670
- parking: null,
671
- format: "10 digits",
672
- pattern: "^\\d{10}$"
673
- },
674
- UA: {
675
- country: "Ukraine",
676
- currency: "UAH",
677
- eu_member: false,
678
- vat_name: "\u041F\u043E\u0434\u0430\u0442\u043E\u043A \u043D\u0430 \u0434\u043E\u0434\u0430\u043D\u0443 \u0432\u0430\u0440\u0442\u0456\u0441\u0442\u044C",
679
- vat_abbr: "\u041F\u0414\u0412",
680
- standard: 20,
681
- reduced: [
682
- 7,
683
- 14
684
- ],
685
- super_reduced: null,
686
- parking: null,
687
- format: "9 digits",
688
- pattern: "^\\d{9}$"
689
- },
690
- XI: {
691
- country: "Northern Ireland",
692
- currency: "GBP",
693
- eu_member: false,
694
- vat_name: "Value Added Tax",
695
- vat_abbr: "VAT",
696
- standard: 20,
697
- reduced: [
698
- 5
699
- ],
700
- super_reduced: null,
701
- parking: null,
702
- format: "XI + 9 digits, 12 digits, or GD/HA + 3 digits",
703
- pattern: "^XI(\\d{9}|\\d{12}|(GD|HA)\\d{3})$"
704
- },
705
- XK: {
706
- country: "Kosovo",
707
- currency: "EUR",
708
- eu_member: false,
709
- vat_name: "Tatimi mbi Vler\xEBn e Shtuar",
710
- vat_abbr: "TVSH",
711
- standard: 18,
712
- reduced: [
713
- 8
714
- ],
715
- super_reduced: null,
716
- parking: null,
717
- format: "9 digits",
718
- pattern: "^\\d{9}$"
719
- }
720
- }
721
- };
722
- var dataset = eu_vat_rates_default;
723
- function getRate(code) {
724
- return dataset.rates[code];
725
- }
726
- function getAllRates() {
727
- return dataset.rates;
728
- }
729
- var dataVersion = dataset.version;
730
-
731
- // ../../packages/shared/src/validators/index.ts
732
6
  import { z } from "zod";
733
- var EU_COUNTRY_CODES = [
734
- "AT",
735
- "BE",
736
- "BG",
737
- "HR",
738
- "CY",
739
- "CZ",
740
- "DK",
741
- "EE",
742
- "FI",
743
- "FR",
744
- "DE",
745
- "GR",
746
- "HU",
747
- "IE",
748
- "IT",
749
- "LV",
750
- "LT",
751
- "LU",
752
- "MT",
753
- "NL",
754
- "PL",
755
- "PT",
756
- "RO",
757
- "SK",
758
- "SI",
759
- "ES",
760
- "SE",
761
- "XI"
762
- ];
763
- var _rates = getAllRates();
764
- var VAT_PATTERNS = {};
765
- for (const code of EU_COUNTRY_CODES) {
766
- const entry = _rates[code];
767
- if (entry?.pattern) {
768
- VAT_PATTERNS[code] = new RegExp(entry.pattern);
769
- }
770
- }
771
- function normalizeVatId(vatId) {
772
- const upper = vatId.trim().toUpperCase().replace(/[\s\-.]/g, "");
773
- return upper.startsWith("GR") ? "EL" + upper.slice(2) : upper;
774
- }
775
- function extractCountryCode(vatId) {
776
- const normalized = normalizeVatId(vatId);
777
- if (normalized.startsWith("EL")) return "GR";
778
- const code = normalized.slice(0, 2);
779
- return EU_COUNTRY_CODES.includes(code) ? code : null;
780
- }
781
- function validateVatIdFormat(vatId) {
782
- const normalized = normalizeVatId(vatId);
783
- if (normalized.length < 4) {
784
- return { valid: false, error: "VAT ID too short" };
785
- }
786
- const countryCode = extractCountryCode(normalized);
787
- if (!countryCode) {
788
- return { valid: false, error: "Invalid country code" };
789
- }
790
- const pattern = VAT_PATTERNS[countryCode];
791
- if (pattern && !pattern.test(normalized)) {
792
- return { valid: false, error: `Invalid VAT ID format for ${countryCode}` };
793
- }
794
- return { valid: true };
795
- }
796
- var vatIdSchema = z.string().min(4).max(20).transform(normalizeVatId);
797
- var countryCodeSchema = z.enum(EU_COUNTRY_CODES);
798
- var createSubscriptionSchema = z.object({
799
- vatId: vatIdSchema,
800
- webhookId: z.string().uuid().optional(),
801
- emailEnabled: z.boolean().default(false)
802
- });
803
- var updateSubscriptionSchema = z.object({
804
- status: z.enum(["active", "paused"]).optional(),
805
- webhookId: z.string().uuid().nullable().optional(),
806
- emailEnabled: z.boolean().optional()
807
- });
808
- var createWebhookSchema = z.object({
809
- url: z.string().url()
810
- });
811
- var paginationSchema = z.object({
812
- page: z.coerce.number().int().positive().default(1),
813
- limit: z.coerce.number().int().min(1).max(100).default(20)
814
- });
815
-
816
- // src/index.ts
817
- var VERSION = "0.1.0";
7
+ import {
8
+ getAllRates,
9
+ getRate,
10
+ validateFormat,
11
+ isEUMember,
12
+ dataVersion
13
+ } from "eu-vat-rates-data";
14
+ var VERSION = "0.2.1";
818
15
  var API_BASE = process.env.VATNODE_API_URL ?? "https://api.vatnode.dev";
819
16
  var API_KEY = process.env.VATNODE_API_KEY;
820
17
  var USER_AGENT = `vatnode-mcp/${VERSION} (+https://vatnode.dev)`;
@@ -834,6 +31,9 @@ Hint: ${hint}` : message
834
31
  }
835
32
  ]
836
33
  });
34
+ function normalizeVatId(vatId) {
35
+ return vatId.toUpperCase().replace(/[\s-]/g, "");
36
+ }
837
37
  function buildRate(countryCode) {
838
38
  const r = getRate(countryCode);
839
39
  if (!r) return null;
@@ -852,11 +52,13 @@ function buildRate(countryCode) {
852
52
  updatedAt: dataVersion
853
53
  };
854
54
  }
55
+ var isVIESEligible = (code) => isEUMember(code) || code === "XI";
56
+ var EU_COUNTRY_CODES = Object.keys(getAllRates()).filter(isVIESEligible).sort();
855
57
  server.registerTool(
856
58
  "list_eu_vat_rates",
857
59
  {
858
60
  title: "List EU VAT rates",
859
- description: 'Returns current VAT rates for all 27 EU member states (plus XI for Northern Ireland). Use when the user asks for an overview, a comparison across countries, or "all EU VAT rates". For a single country prefer get_country_vat_rates. Data is sourced from the EU Commission TEDB and updated daily. Free, no API key required.',
61
+ description: 'Returns current VAT rates for all EU member states (plus XI for Northern Ireland). Use when the user asks for an overview, a comparison across countries, or "all EU VAT rates". For a single country prefer get_country_vat_rates. Data is sourced from the EU Commission TEDB and updated daily. Free, no API key required.',
860
62
  inputSchema: {}
861
63
  },
862
64
  async () => {
@@ -870,7 +72,7 @@ server.registerTool(
870
72
  title: "Get VAT rates for a country",
871
73
  description: 'Returns the standard, reduced, super-reduced and parking VAT rates for a single European country, plus the VAT number format and regex. Accepts ISO 3166-1 alpha-2 codes (DE, FR, IT, \u2026); also covers non-EU European jurisdictions where available (NO, CH, GB, UA, TR, \u2026). Use when the user asks "what is the VAT rate in X" or needs the VAT number format for a country. Free, no API key required.',
872
74
  inputSchema: {
873
- countryCode: z2.string().length(2).describe('ISO 3166-1 alpha-2 country code, e.g. "DE", "FR", "IT".')
75
+ countryCode: z.string().length(2).describe('ISO 3166-1 alpha-2 country code, e.g. "DE", "FR", "IT".')
874
76
  }
875
77
  },
876
78
  async ({ countryCode }) => {
@@ -885,21 +87,21 @@ server.registerTool(
885
87
  title: "Check VAT number format",
886
88
  description: "Performs an offline syntactic check of a VAT number against the country-specific regex pattern. Does NOT verify the VAT with VIES \u2014 use validate_vat_number for that. Use when the user wants a quick sanity check on the shape of a VAT ID without burning a quota call. Free, no API key required.",
887
89
  inputSchema: {
888
- vatId: z2.string().min(3).describe(
90
+ vatId: z.string().min(3).describe(
889
91
  'A VAT number, with or without spaces/dashes, with or without country prefix (e.g. "DE123456789", "IE 6388047 V").'
890
92
  )
891
93
  }
892
94
  },
893
95
  async ({ vatId }) => {
894
96
  const normalized = normalizeVatId(vatId);
895
- const country = extractCountryCode(normalized);
896
- const result = validateVatIdFormat(normalized);
97
+ const country = normalized.slice(0, 2);
98
+ const valid = validateFormat(normalized);
897
99
  return ok({
898
100
  input: vatId,
899
101
  normalized,
900
- countryCode: country,
901
- validFormat: result.valid,
902
- error: result.error ?? null
102
+ countryCode: getRate(country) ? country : null,
103
+ validFormat: valid,
104
+ error: valid ? null : `${normalized} does not match the expected VAT number format for ${country}.`
903
105
  });
904
106
  }
905
107
  );
@@ -912,12 +114,11 @@ server.registerTool(
912
114
  },
913
115
  async () => {
914
116
  const all = getAllRates();
915
- const euSet = new Set(EU_COUNTRY_CODES);
916
117
  const countries = Object.entries(all).map(([code, r]) => ({
917
118
  countryCode: code,
918
119
  countryName: r.country,
919
- isEUMember: euSet.has(code),
920
- viesValidationSupported: euSet.has(code)
120
+ isEUMember: isVIESEligible(code),
121
+ viesValidationSupported: isVIESEligible(code)
921
122
  })).sort((a, b) => a.countryCode.localeCompare(b.countryCode));
922
123
  return ok({
923
124
  countries,
@@ -960,13 +161,13 @@ server.registerTool(
960
161
  title: "Validate EU VAT number",
961
162
  description: "Verifies an EU VAT number against the official VIES service and returns validity, company name, address, registration date and other metadata. When the requester (your own VAT) is configured on the vatnode account, also returns a VIES consultation number \u2014 audit-grade proof of validation. Use whenever the user wants to confirm a VAT is real, look up the company behind a VAT, or needs evidence for accounting/compliance. Requires a vatnode API key (free tier available). Only EU-27 + XI (Northern Ireland) are supported by VIES.",
962
163
  inputSchema: {
963
- vatId: z2.string().min(3).describe(
164
+ vatId: z.string().min(3).describe(
964
165
  'EU VAT number, with country prefix. Spaces and dashes are stripped. Examples: "DE123456789", "IE6388047V", "FR12345678901".'
965
166
  ),
966
- requesterCountryCode: z2.string().length(2).optional().describe(
167
+ requesterCountryCode: z.string().length(2).optional().describe(
967
168
  "Optional: 2-letter country code of the party doing the check. Together with requesterVatNumber, asks VIES to issue an audit consultation number."
968
169
  ),
969
- requesterVatNumber: z2.string().min(1).optional().describe(
170
+ requesterVatNumber: z.string().min(1).optional().describe(
970
171
  "Optional: VAT number of the party doing the check. Pair with requesterCountryCode to get a consultation number."
971
172
  )
972
173
  }
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "vatnode-mcp",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
+ "mcpName": "io.github.vatnode/vatnode-mcp",
4
5
  "description": "Official Model Context Protocol (MCP) server for vatnode — EU VAT validation, rates, and format checks for AI agents (Claude, Cursor, ChatGPT).",
5
6
  "keywords": [
6
7
  "mcp",
@@ -15,11 +16,14 @@
15
16
  ],
16
17
  "license": "MIT",
17
18
  "author": "vatnode",
18
- "homepage": "https://vatnode.dev",
19
+ "homepage": "https://vatnode.dev/docs/mcp",
19
20
  "repository": {
20
21
  "type": "git",
21
- "url": "https://github.com/vatnode/vatnode.git",
22
- "directory": "apps/mcp"
22
+ "url": "git+https://github.com/vatnode/vatnode-mcp.git"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/vatnode/vatnode-mcp/issues",
26
+ "email": "support@vatnode.dev"
23
27
  },
24
28
  "type": "module",
25
29
  "bin": {
@@ -29,24 +33,23 @@
29
33
  "dist",
30
34
  "README.md"
31
35
  ],
36
+ "scripts": {
37
+ "dev": "tsx watch src/index.ts",
38
+ "build": "tsup",
39
+ "start": "node dist/index.js",
40
+ "typecheck": "tsc --noEmit",
41
+ "test": "npm run build && node test/smoke.mjs",
42
+ "prepublishOnly": "npm run build"
43
+ },
32
44
  "dependencies": {
33
45
  "@modelcontextprotocol/sdk": "^1.0.4",
46
+ "eu-vat-rates-data": "^2026.5.20",
34
47
  "zod": "^3.24.0"
35
48
  },
36
49
  "devDependencies": {
37
50
  "@types/node": "^22.10.0",
38
51
  "tsup": "^8.3.0",
39
52
  "tsx": "^4.19.0",
40
- "typescript": "^5.7.0",
41
- "@vatnode/shared": "0.1.0",
42
- "eu-vat-rates-data": "2026.3.27"
43
- },
44
- "scripts": {
45
- "dev": "tsx watch src/index.ts",
46
- "build": "tsup",
47
- "start": "node dist/index.js",
48
- "lint": "eslint src/",
49
- "typecheck": "tsc --noEmit",
50
- "test": "pnpm build && node test/smoke.mjs"
53
+ "typescript": "^5.7.0"
51
54
  }
52
- }
55
+ }