vat-validator-mcp 2.0.2 → 2.0.3

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "vat-validator-mcp",
3
3
  "mcpName": "io.github.OjasKord/vat-validator-mcp",
4
- "version": "2.0.2",
4
+ "version": "2.0.3",
5
5
  "description": "VAT number validator for AI agents. EU VIES, UK HMRC, AU ABR — auto-detects jurisdiction. Fraud risk scoring and invoice name cross-check in one call.",
6
6
  "main": "src/server.js",
7
7
  "scripts": {
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "io.github.OjasKord/vat-validator-mcp",
4
4
  "title": "VAT Validator MCP",
5
5
  "description": "Validate EU, UK, AU VAT numbers for AI agents. EU ViDA e-invoicing compliance.",
6
- "version": "2.0.1",
6
+ "version": "2.0.2",
7
7
  "websiteUrl": "https://kordagencies.com",
8
8
  "repository": {
9
9
  "url": "https://github.com/OjasKord/vat-validator-mcp",
@@ -13,7 +13,7 @@
13
13
  {
14
14
  "registryType": "npm",
15
15
  "identifier": "vat-validator-mcp",
16
- "version": "2.0.1",
16
+ "version": "2.0.2",
17
17
  "transport": { "type": "stdio" },
18
18
  "environmentVariables": [
19
19
  { "name": "ANTHROPIC_API_KEY", "description": "Anthropic API key for AI-powered fraud risk analysis", "isRequired": true, "isSecret": true },
package/src/server.js CHANGED
@@ -7,7 +7,7 @@ const Stripe = require('stripe');
7
7
  const stripe = Stripe(process.env.STRIPE_SECRET_KEY);
8
8
 
9
9
  const PERSIST_FILE = '/tmp/vat_stats.json';
10
- const VERSION = '2.0.2';
10
+ const VERSION = '2.0.3';
11
11
 
12
12
  // Persistent device ID for HMRC fraud prevention headers (BATCH_PROCESS_DIRECT)
13
13
  const DEVICE_ID_FILE = path.join(__dirname, '..', 'device-id.txt');
@@ -211,6 +211,15 @@ async function validateVIES(countryCode, vatNumber) {
211
211
  });
212
212
  }
213
213
 
214
+ async function hmrcFetchWithRetry(url, options, maxRetries = 3) {
215
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
216
+ const response = await fetch(url, options);
217
+ if (response.status !== 429) return response;
218
+ if (attempt === maxRetries) return response;
219
+ await new Promise(resolve => setTimeout(resolve, attempt * 1000));
220
+ }
221
+ }
222
+
214
223
  function getFraudPreventionHeaders() {
215
224
  return {
216
225
  'Gov-Client-Connection-Method': 'BATCH_PROCESS_DIRECT',
@@ -223,7 +232,7 @@ function getFraudPreventionHeaders() {
223
232
  'Gov-Client-User-IDs': 'os=railway-service',
224
233
  'Gov-Vendor-License-IDs': 'vat-validator-mcp=not-applicable',
225
234
  'Gov-Vendor-Product-Name': 'VAT%20Validator%20MCP',
226
- 'Gov-Vendor-Version': 'vat-validator-mcp=2.0.2'
235
+ 'Gov-Vendor-Version': 'vat-validator-mcp=2.0.3'
227
236
  };
228
237
  }
229
238
 
@@ -245,32 +254,23 @@ async function getHMRCToken() {
245
254
 
246
255
  const body = `client_secret=${encodeURIComponent(clientSecret)}&client_id=${encodeURIComponent(clientId)}&grant_type=client_credentials&scope=read%3Avat`;
247
256
 
248
- return new Promise((resolve) => {
249
- const req = https.request({
250
- hostname,
251
- path: '/oauth/token',
257
+ try {
258
+ const response = await hmrcFetchWithRetry(`https://${hostname}/oauth/token`, {
252
259
  method: 'POST',
253
- headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': Buffer.byteLength(body), ...getFraudPreventionHeaders() }
254
- }, res => {
255
- let d = ''; res.on('data', c => d += c);
256
- res.on('end', () => {
257
- try {
258
- const json = JSON.parse(d);
259
- if (json.access_token) {
260
- hmrcToken = json.access_token;
261
- hmrcTokenExpiry = now + (json.expires_in || 14400) * 1000;
262
- resolve(hmrcToken);
263
- } else {
264
- resolve(null);
265
- }
266
- } catch(e) { resolve(null); }
267
- });
260
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded', ...getFraudPreventionHeaders() },
261
+ body,
262
+ signal: AbortSignal.timeout(8000)
268
263
  });
269
- req.on('error', () => resolve(null));
270
- req.setTimeout(8000, () => { req.destroy(); resolve(null); });
271
- req.write(body);
272
- req.end();
273
- });
264
+ const json = await response.json();
265
+ if (json.access_token) {
266
+ hmrcToken = json.access_token;
267
+ hmrcTokenExpiry = now + (json.expires_in || 14400) * 1000;
268
+ return hmrcToken;
269
+ }
270
+ return null;
271
+ } catch(e) {
272
+ return null;
273
+ }
274
274
  }
275
275
 
276
276
  async function validateHMRC(vatNumber) {
@@ -281,23 +281,18 @@ async function validateHMRC(vatNumber) {
281
281
  const sandbox = process.env.HMRC_SANDBOX === 'true';
282
282
  const hostname = sandbox ? 'test-api.service.hmrc.gov.uk' : 'api.service.hmrc.gov.uk';
283
283
 
284
- return new Promise((resolve) => {
285
- const req = https.request({
286
- hostname,
287
- path: '/organisations/vat/check-vat-number/lookup/' + clean,
284
+ try {
285
+ const response = await hmrcFetchWithRetry(`https://${hostname}/organisations/vat/check-vat-number/lookup/${clean}`, {
288
286
  method: 'GET',
289
- headers: { 'Accept': 'application/vnd.hmrc.2.0+json', 'Authorization': 'Bearer ' + token, ...getFraudPreventionHeaders() }
290
- }, res => {
291
- let d = ''; res.on('data', c => d += c);
292
- res.on('end', () => {
293
- try { resolve({ source: 'HMRC', status: res.statusCode, data: JSON.parse(d) }); }
294
- catch(e) { resolve({ source: 'HMRC', error: 'Parse error' }); }
295
- });
287
+ headers: { 'Accept': 'application/vnd.hmrc.2.0+json', 'Authorization': 'Bearer ' + token, ...getFraudPreventionHeaders() },
288
+ signal: AbortSignal.timeout(8000)
296
289
  });
297
- req.on('error', e => resolve({ source: 'HMRC', error: e.message }));
298
- req.setTimeout(8000, () => { req.destroy(); resolve({ source: 'HMRC', error: 'Timeout' }); });
299
- req.end();
300
- });
290
+ const data = await response.json();
291
+ return { source: 'HMRC', status: response.status, data };
292
+ } catch(e) {
293
+ if (e.name === 'TimeoutError' || e.name === 'AbortError') return { source: 'HMRC', error: 'Timeout' };
294
+ return { source: 'HMRC', error: e.message };
295
+ }
301
296
  }
302
297
 
303
298
  async function validateABN(abn) {