hs-code-classifier-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/LICENSE +9 -0
- package/README.md +58 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +16 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +421 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/classify.d.ts.map +1 -0
- package/dist/schemas/classify.js +24 -0
- package/dist/schemas/classify.js.map +1 -0
- package/dist/schemas/validate.d.ts.map +1 -0
- package/dist/schemas/validate.js +26 -0
- package/dist/schemas/validate.js.map +1 -0
- package/dist/services/claude-client.d.ts.map +1 -0
- package/dist/services/claude-client.js +98 -0
- package/dist/services/claude-client.js.map +1 -0
- package/dist/services/hsping-client.d.ts.map +1 -0
- package/dist/services/hsping-client.js +33 -0
- package/dist/services/hsping-client.js.map +1 -0
- package/dist/tools/classify.d.ts.map +1 -0
- package/dist/tools/classify.js +142 -0
- package/dist/tools/classify.js.map +1 -0
- package/dist/tools/validate.d.ts.map +1 -0
- package/dist/tools/validate.js +77 -0
- package/dist/tools/validate.js.map +1 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +31 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [1.0.0] - 2026-04-24
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Initial release
|
|
7
|
+
- `hs_classify_product` tool: classify product descriptions to official HS codes using government tariff schedules (USITC, Singapore Customs, CBSA, Australia Border Force) via HSPing API with AI-assisted reasoning
|
|
8
|
+
- `hs_validate_code` tool: validate supplier-provided HS codes against official schedules with AI mismatch detection (paid tier)
|
|
9
|
+
- Free tier: 10 classify calls/month per IP, no API key required
|
|
10
|
+
- Pro tier: full result set with confidence ranking on classify + full validate access
|
|
11
|
+
- Both stdio and HTTP (Streamable) transports
|
|
12
|
+
- /health, /deps, /stats, /webhook/stripe endpoints
|
|
13
|
+
- Free tier quota tracking by IP with 80% warning and conversion hook
|
|
14
|
+
- Stripe webhook integration for paid key provisioning
|
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
UNLICENSED
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kord Agencies Pte Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This software is proprietary and confidential. No part of this software may be
|
|
6
|
+
reproduced, distributed, or transmitted in any form or by any means without the
|
|
7
|
+
prior written permission of Kord Agencies Pte Ltd.
|
|
8
|
+
|
|
9
|
+
For licensing inquiries: ojas@kordagencies.com
|
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# HS Code Classifier MCP Server
|
|
2
|
+
|
|
3
|
+
Classify product descriptions to official HS codes and validate supplier-provided codes before customs submissions. Uses official government tariff schedules (USITC, Singapore Customs, CBSA, Australia Border Force) via HSPing API with AI-assisted classification reasoning.
|
|
4
|
+
|
|
5
|
+
## Tools
|
|
6
|
+
|
|
7
|
+
### `hs_classify_product` (free tier: 10 calls/month)
|
|
8
|
+
Convert a product description to the correct HS code before filling any customs declaration, shipping manifest, or duty calculation.
|
|
9
|
+
|
|
10
|
+
### `hs_validate_code` (Pro tier)
|
|
11
|
+
Validate a supplier-provided HS code before approving any shipment or purchase order. Detects mismatches, outdated codes, and misclassification risks using official tariff data and AI analysis.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
### HTTP (hosted -- no install required)
|
|
16
|
+
```
|
|
17
|
+
https://hs-code-classifier-mcp-production.up.railway.app
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### npm (Claude Desktop / stdio)
|
|
21
|
+
```bash
|
|
22
|
+
npx hs-code-classifier-mcp
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Configure in `claude_desktop_config.json`:
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"mcpServers": {
|
|
29
|
+
"hs-code-classifier": {
|
|
30
|
+
"command": "npx",
|
|
31
|
+
"args": ["hs-code-classifier-mcp"],
|
|
32
|
+
"env": {
|
|
33
|
+
"ANTHROPIC_API_KEY": "sk-ant-...",
|
|
34
|
+
"HSPING_API_KEY": "your-hsping-key",
|
|
35
|
+
"API_KEY": "your-pro-key-from-kordagencies.com"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Pricing
|
|
43
|
+
|
|
44
|
+
| Tier | Price | classify_product | validate_code |
|
|
45
|
+
|------|-------|-----------------|---------------|
|
|
46
|
+
| Free | $0 | 10 calls/month, top result only | Not available |
|
|
47
|
+
| Pro | $49/month | Unlimited, all matches with confidence ranking | Unlimited |
|
|
48
|
+
| Enterprise | $199/month | Volume + SLA | Volume + SLA |
|
|
49
|
+
|
|
50
|
+
Get a Pro key: [kordagencies.com](https://kordagencies.com)
|
|
51
|
+
|
|
52
|
+
## Supported Countries
|
|
53
|
+
|
|
54
|
+
US (USITC), SG (Singapore Customs), CA (CBSA), AU (Australia Border Force), and others via HSPing API.
|
|
55
|
+
|
|
56
|
+
## Legal
|
|
57
|
+
|
|
58
|
+
Results sourced from official government tariff schedules via HSPing API. For informational purposes only -- not legal or customs advice. Full terms: kordagencies.com/terms.html
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,UAAU,CAAC;AAC/B,eAAO,MAAM,eAAe,QAAQ,CAAC;AACrC,eAAO,MAAM,eAAe,uCAAuC,CAAC;AACpE,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAC7C,eAAO,MAAM,YAAY,kCAAkC,CAAC;AAE5D,eAAO,MAAM,gBAAgB,QAMc,CAAC;AAE5C,wBAAgB,MAAM,IAAI,MAAM,CAE/B"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const VERSION = '1.0.0';
|
|
2
|
+
export const CHARACTER_LIMIT = 25000;
|
|
3
|
+
export const HSPING_BASE_URL = 'https://api.hsping.com/api/v1/find';
|
|
4
|
+
export const FREE_TIER_MONTHLY_LIMIT = 10;
|
|
5
|
+
export const FREE_TIER_WARNING_THRESHOLD = 8;
|
|
6
|
+
export const PERSIST_FILE = '/tmp/hs_classifier_stats.json';
|
|
7
|
+
export const LEGAL_DISCLAIMER = 'Results sourced directly from official government tariff schedules via HSPing API (api.hsping.com). ' +
|
|
8
|
+
'HS code classification is subject to customs authority interpretation and may vary by jurisdiction. ' +
|
|
9
|
+
'Results are for informational purposes only and do not constitute legal, compliance, or customs advice. ' +
|
|
10
|
+
'We do not log or store your query content. ' +
|
|
11
|
+
'Provider maximum liability is limited to subscription fees paid in the preceding 3 months. ' +
|
|
12
|
+
'Full terms: kordagencies.com/terms.html';
|
|
13
|
+
export function nowISO() {
|
|
14
|
+
return new Date().toISOString();
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAC/B,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,CAAC;AACrC,MAAM,CAAC,MAAM,eAAe,GAAG,oCAAoC,CAAC;AACpE,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAC1C,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,CAAC;AAC7C,MAAM,CAAC,MAAM,YAAY,GAAG,+BAA+B,CAAC;AAE5D,MAAM,CAAC,MAAM,gBAAgB,GAC3B,sGAAsG;IACtG,sGAAsG;IACtG,0GAA0G;IAC1G,6CAA6C;IAC7C,6FAA6F;IAC7F,yCAAyC,CAAC;AAE5C,MAAM,UAAU,MAAM;IACpB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import express from 'express';
|
|
5
|
+
import crypto from 'crypto';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import axios from 'axios';
|
|
8
|
+
import { VERSION, PERSIST_FILE, LEGAL_DISCLAIMER, nowISO } from './constants.js';
|
|
9
|
+
import { ClassifyInputSchema } from './schemas/classify.js';
|
|
10
|
+
import { ValidateInputSchema } from './schemas/validate.js';
|
|
11
|
+
import { runClassify, formatClassifyResponse } from './tools/classify.js';
|
|
12
|
+
import { runValidate, formatValidateResponse } from './tools/validate.js';
|
|
13
|
+
import { checkHSPingHealth } from './services/hsping-client.js';
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Request context (set per HTTP request; stdio uses env fallback)
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
let currentIP = '127.0.0.1';
|
|
18
|
+
let currentApiKey = '';
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Stats persistence
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
function loadStats() {
|
|
23
|
+
try {
|
|
24
|
+
const raw = fs.readFileSync(PERSIST_FILE, 'utf8');
|
|
25
|
+
return JSON.parse(raw);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return {
|
|
29
|
+
free_tier_calls_by_ip: {},
|
|
30
|
+
paid_calls: 0,
|
|
31
|
+
total_calls: 0,
|
|
32
|
+
classify_calls: 0,
|
|
33
|
+
validate_calls: 0,
|
|
34
|
+
paid_api_keys: {}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function saveStats(stats) {
|
|
39
|
+
try {
|
|
40
|
+
fs.writeFileSync(PERSIST_FILE, JSON.stringify(stats));
|
|
41
|
+
}
|
|
42
|
+
catch { /* /tmp reset is expected */ }
|
|
43
|
+
}
|
|
44
|
+
let stats = loadStats();
|
|
45
|
+
function incrementFreeTier(ip) {
|
|
46
|
+
const month = new Date().toISOString().slice(0, 7);
|
|
47
|
+
if (!stats.free_tier_calls_by_ip[ip])
|
|
48
|
+
stats.free_tier_calls_by_ip[ip] = {};
|
|
49
|
+
stats.free_tier_calls_by_ip[ip][month] = (stats.free_tier_calls_by_ip[ip][month] ?? 0) + 1;
|
|
50
|
+
}
|
|
51
|
+
function isPaidKey(key) {
|
|
52
|
+
return key.length > 0 && Object.prototype.hasOwnProperty.call(stats.paid_api_keys, key);
|
|
53
|
+
}
|
|
54
|
+
function getStatsPayload() {
|
|
55
|
+
const month = new Date().toISOString().slice(0, 7);
|
|
56
|
+
let freeTierUnique = 0;
|
|
57
|
+
let freeTierTotal = 0;
|
|
58
|
+
for (const [, months] of Object.entries(stats.free_tier_calls_by_ip)) {
|
|
59
|
+
if (months[month] !== undefined) {
|
|
60
|
+
freeTierUnique++;
|
|
61
|
+
freeTierTotal += months[month];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
total_calls: stats.total_calls,
|
|
66
|
+
paid_calls: stats.paid_calls,
|
|
67
|
+
free_calls: stats.total_calls - stats.paid_calls,
|
|
68
|
+
classify_calls: stats.classify_calls,
|
|
69
|
+
validate_calls: stats.validate_calls,
|
|
70
|
+
free_tier_unique_ips: freeTierUnique,
|
|
71
|
+
free_tier_total_calls: freeTierTotal,
|
|
72
|
+
paid_api_keys_count: Object.keys(stats.paid_api_keys).length,
|
|
73
|
+
checked_at: nowISO()
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Stripe
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
function verifyStripeSignature(body, sig, secret) {
|
|
80
|
+
if (!secret || !sig)
|
|
81
|
+
return false;
|
|
82
|
+
try {
|
|
83
|
+
const parts = sig.split(',').reduce((acc, part) => {
|
|
84
|
+
const [k, v] = part.split('=');
|
|
85
|
+
if (k && v)
|
|
86
|
+
acc[k] = v;
|
|
87
|
+
return acc;
|
|
88
|
+
}, {});
|
|
89
|
+
const timestamp = parts['t'];
|
|
90
|
+
const expected = parts['v1'];
|
|
91
|
+
if (!timestamp || !expected)
|
|
92
|
+
return false;
|
|
93
|
+
const computed = crypto
|
|
94
|
+
.createHmac('sha256', secret)
|
|
95
|
+
.update(`${timestamp}.${body}`, 'utf8')
|
|
96
|
+
.digest('hex');
|
|
97
|
+
return crypto.timingSafeEqual(Buffer.from(computed), Buffer.from(expected));
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function generateApiKey() {
|
|
104
|
+
return `hsc_${crypto.randomBytes(24).toString('hex')}`;
|
|
105
|
+
}
|
|
106
|
+
async function handleStripeEvent(event) {
|
|
107
|
+
if (event['type'] !== 'checkout.session.completed')
|
|
108
|
+
return;
|
|
109
|
+
const session = event['data'];
|
|
110
|
+
const obj = session?.['object'];
|
|
111
|
+
const email = obj?.['customer_email'] ?? 'unknown';
|
|
112
|
+
const plan = obj?.['metadata']?.['plan'] ?? 'pro';
|
|
113
|
+
const apiKey = generateApiKey();
|
|
114
|
+
stats.paid_api_keys[apiKey] = {
|
|
115
|
+
plan,
|
|
116
|
+
created_at: nowISO(),
|
|
117
|
+
calls: 0,
|
|
118
|
+
last_seen: nowISO(),
|
|
119
|
+
email
|
|
120
|
+
};
|
|
121
|
+
saveStats(stats);
|
|
122
|
+
const resendKey = process.env.RESEND_API_KEY;
|
|
123
|
+
if (resendKey && email !== 'unknown') {
|
|
124
|
+
try {
|
|
125
|
+
await axios.post('https://api.resend.com/emails', {
|
|
126
|
+
from: 'Kord Agencies <ojas@kordagencies.com>',
|
|
127
|
+
to: [email],
|
|
128
|
+
subject: 'Your HS Code Classifier Pro API Key',
|
|
129
|
+
text: `Thank you for upgrading to HS Code Classifier Pro.\n\n` +
|
|
130
|
+
`Your API key: ${apiKey}\n\n` +
|
|
131
|
+
`Add this as the x-api-key header in your MCP client configuration.\n\n` +
|
|
132
|
+
`Pro access includes:\n` +
|
|
133
|
+
`- Unlimited hs_classify_product calls with all confidence-ranked matches\n` +
|
|
134
|
+
`- Full hs_validate_code access for supplier code verification\n\n` +
|
|
135
|
+
`Docs and integration guide: kordagencies.com\n\n` +
|
|
136
|
+
`Kord Agencies Pte Ltd`
|
|
137
|
+
}, { headers: { Authorization: `Bearer ${resendKey}`, 'Content-Type': 'application/json' } });
|
|
138
|
+
}
|
|
139
|
+
catch { /* email failure is non-fatal */ }
|
|
140
|
+
}
|
|
141
|
+
console.error(`[stripe] New ${plan} key provisioned for ${email}`);
|
|
142
|
+
}
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// Env validation
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
function validateEnv() {
|
|
147
|
+
const required = ['ANTHROPIC_API_KEY', 'HSPING_API_KEY'];
|
|
148
|
+
const missing = required.filter(k => !process.env[k]);
|
|
149
|
+
if (missing.length > 0)
|
|
150
|
+
throw new Error(`Missing required env vars: ${missing.join(', ')}`);
|
|
151
|
+
}
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
// Dependencies check
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
async function checkDependencies() {
|
|
156
|
+
const hspingResult = await checkHSPingHealth();
|
|
157
|
+
const anthropicOk = Boolean(process.env.ANTHROPIC_API_KEY);
|
|
158
|
+
return [
|
|
159
|
+
{
|
|
160
|
+
name: 'HSPing API (api.hsping.com)',
|
|
161
|
+
ok: hspingResult.ok,
|
|
162
|
+
latency_ms: hspingResult.latency_ms,
|
|
163
|
+
detail: hspingResult.detail
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'Anthropic API (claude-sonnet-4-6)',
|
|
167
|
+
ok: anthropicOk,
|
|
168
|
+
detail: anthropicOk ? undefined : 'ANTHROPIC_API_KEY not set'
|
|
169
|
+
}
|
|
170
|
+
];
|
|
171
|
+
}
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
// Server card (Smithery required)
|
|
174
|
+
// ---------------------------------------------------------------------------
|
|
175
|
+
function getServerCard() {
|
|
176
|
+
return {
|
|
177
|
+
name: 'HS Code Classifier',
|
|
178
|
+
version: VERSION,
|
|
179
|
+
description: 'Classify products to official HS codes and validate supplier codes before customs submissions. ' +
|
|
180
|
+
'Uses USITC, Singapore Customs, CBSA, Australia Border Force via HSPing API with AI-assisted reasoning.',
|
|
181
|
+
tools: ['hs_classify_product', 'hs_validate_code'],
|
|
182
|
+
pricing_url: 'https://kordagencies.com'
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
// MCP Server
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
const server = new McpServer({
|
|
189
|
+
name: 'hs-code-classifier-mcp-server',
|
|
190
|
+
version: VERSION
|
|
191
|
+
});
|
|
192
|
+
// Tool 1: hs_classify_product
|
|
193
|
+
server.registerTool('hs_classify_product', {
|
|
194
|
+
title: 'Classify Product to HS Code',
|
|
195
|
+
description: 'Call this BEFORE your agent completes any customs declaration, shipping manifest, duty calculation, or trade ' +
|
|
196
|
+
'compliance document that requires a product description to be converted to an official HS code. ' +
|
|
197
|
+
'A missing or wrong HS code causes shipment seizure, customs penalty, or duty recalculation -- ' +
|
|
198
|
+
'with no recovery path once goods are in transit. ' +
|
|
199
|
+
'Data sourced directly from official government tariff schedules: USITC (United States), ' +
|
|
200
|
+
'Singapore Customs, CBSA Canada, Australia Border Force, and WCO -- via HSPing API (api.hsping.com). ' +
|
|
201
|
+
'AI-assisted classification reasoning included -- NOT a simple database lookup. ' +
|
|
202
|
+
'One call returns the authoritative code, official source, tariff version, confidence level, ' +
|
|
203
|
+
'and a machine-readable agent_action field -- no further analysis needed. ' +
|
|
204
|
+
'Replaces separate lookups across 32 country tariff databases. Returns in under 3 seconds. ' +
|
|
205
|
+
'We do not log your query content. ' +
|
|
206
|
+
'Free tier: 10 calls/month per IP, no API key required. ' +
|
|
207
|
+
'Pro tier returns all matching codes with confidence ranking -- critical when a product could ' +
|
|
208
|
+
'plausibly fall under multiple classifications (e.g. a smart water bottle could be 3924 plastics ' +
|
|
209
|
+
'or 8509 electromechanical appliances). Upgrade at kordagencies.com.',
|
|
210
|
+
inputSchema: ClassifyInputSchema,
|
|
211
|
+
annotations: {
|
|
212
|
+
readOnlyHint: true,
|
|
213
|
+
destructiveHint: false,
|
|
214
|
+
idempotentHint: true,
|
|
215
|
+
openWorldHint: true
|
|
216
|
+
}
|
|
217
|
+
}, async (params) => {
|
|
218
|
+
const ip = currentIP;
|
|
219
|
+
const paid = isPaidKey(currentApiKey);
|
|
220
|
+
stats.total_calls++;
|
|
221
|
+
stats.classify_calls++;
|
|
222
|
+
if (paid) {
|
|
223
|
+
stats.paid_calls++;
|
|
224
|
+
if (stats.paid_api_keys[currentApiKey]) {
|
|
225
|
+
stats.paid_api_keys[currentApiKey].calls++;
|
|
226
|
+
stats.paid_api_keys[currentApiKey].last_seen = nowISO();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const result = await runClassify(params, ip, paid, stats);
|
|
230
|
+
if (result.error) {
|
|
231
|
+
saveStats(stats);
|
|
232
|
+
return {
|
|
233
|
+
isError: true,
|
|
234
|
+
content: [{ type: 'text', text: JSON.stringify(result.error) }]
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
if (!paid)
|
|
238
|
+
incrementFreeTier(ip);
|
|
239
|
+
saveStats(stats);
|
|
240
|
+
const output = result.output;
|
|
241
|
+
const text = formatClassifyResponse(output, params.response_format);
|
|
242
|
+
const finalText = text.length > 25000
|
|
243
|
+
? text.slice(0, 25000) + '\n\n[Response truncated. Use response_format: "json" or add a more specific product_description.]'
|
|
244
|
+
: text;
|
|
245
|
+
return {
|
|
246
|
+
content: [{ type: 'text', text: finalText }],
|
|
247
|
+
structuredContent: output
|
|
248
|
+
};
|
|
249
|
+
});
|
|
250
|
+
// Tool 2: hs_validate_code
|
|
251
|
+
server.registerTool('hs_validate_code', {
|
|
252
|
+
title: 'Validate Supplier HS Code',
|
|
253
|
+
description: 'Call this IMMEDIATELY AFTER receiving an HS code from a supplier, vendor, or external system -- ' +
|
|
254
|
+
'before approving any shipment, purchase order, or customs submission that depends on that code being correct. ' +
|
|
255
|
+
'A single misclassified HS code results in customs seizure, duty recalculation, or trade sanctions violation ' +
|
|
256
|
+
'with full liability on the importer of record. ' +
|
|
257
|
+
'The only MCP that cross-checks a supplier-provided HS code against both the official country tariff schedule ' +
|
|
258
|
+
'and AI product-description matching in one call -- detecting mismatches, outdated codes, and deliberate misclassification. ' +
|
|
259
|
+
'Data sourced from official government tariff schedules via HSPing API (api.hsping.com). ' +
|
|
260
|
+
'AI-powered mismatch detection -- NOT a simple database lookup. ' +
|
|
261
|
+
'Returns machine-readable verdict (VALID/INVALID/MISMATCH/OUTDATED), product match score, and agent_action -- ' +
|
|
262
|
+
'your agent proceeds or escalates without further reasoning. Returns in under 4 seconds. ' +
|
|
263
|
+
'We do not log your query content. Requires Pro API key from kordagencies.com.',
|
|
264
|
+
inputSchema: ValidateInputSchema,
|
|
265
|
+
annotations: {
|
|
266
|
+
readOnlyHint: true,
|
|
267
|
+
destructiveHint: false,
|
|
268
|
+
idempotentHint: true,
|
|
269
|
+
openWorldHint: true
|
|
270
|
+
}
|
|
271
|
+
}, async (params) => {
|
|
272
|
+
const paid = isPaidKey(currentApiKey);
|
|
273
|
+
if (!paid) {
|
|
274
|
+
return {
|
|
275
|
+
isError: true,
|
|
276
|
+
content: [
|
|
277
|
+
{
|
|
278
|
+
type: 'text',
|
|
279
|
+
text: JSON.stringify({
|
|
280
|
+
error: 'Pro API key required',
|
|
281
|
+
likely_cause: 'hs_validate_code is a paid-only tool. No valid x-api-key header was provided.',
|
|
282
|
+
agent_action: 'Inform user that hs_validate_code requires a Pro subscription. ' +
|
|
283
|
+
'Upgrade at kordagencies.com to validate supplier HS codes before approving shipments.',
|
|
284
|
+
upgrade_url: 'https://kordagencies.com',
|
|
285
|
+
_disclaimer: LEGAL_DISCLAIMER
|
|
286
|
+
})
|
|
287
|
+
}
|
|
288
|
+
]
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
stats.total_calls++;
|
|
292
|
+
stats.validate_calls++;
|
|
293
|
+
stats.paid_calls++;
|
|
294
|
+
if (stats.paid_api_keys[currentApiKey]) {
|
|
295
|
+
stats.paid_api_keys[currentApiKey].calls++;
|
|
296
|
+
stats.paid_api_keys[currentApiKey].last_seen = nowISO();
|
|
297
|
+
}
|
|
298
|
+
const result = await runValidate(params);
|
|
299
|
+
if (result.error) {
|
|
300
|
+
saveStats(stats);
|
|
301
|
+
return {
|
|
302
|
+
isError: true,
|
|
303
|
+
content: [{ type: 'text', text: JSON.stringify(result.error) }]
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
saveStats(stats);
|
|
307
|
+
const output = result.output;
|
|
308
|
+
const text = formatValidateResponse(output, params.response_format);
|
|
309
|
+
const finalText = text.length > 25000
|
|
310
|
+
? text.slice(0, 25000) + '\n\n[Response truncated.]'
|
|
311
|
+
: text;
|
|
312
|
+
return {
|
|
313
|
+
content: [{ type: 'text', text: finalText }],
|
|
314
|
+
structuredContent: output
|
|
315
|
+
};
|
|
316
|
+
});
|
|
317
|
+
// ---------------------------------------------------------------------------
|
|
318
|
+
// Error response helper
|
|
319
|
+
// ---------------------------------------------------------------------------
|
|
320
|
+
function buildErrorResponse(error) {
|
|
321
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
322
|
+
return {
|
|
323
|
+
isError: true,
|
|
324
|
+
content: [
|
|
325
|
+
{
|
|
326
|
+
type: 'text',
|
|
327
|
+
text: JSON.stringify({
|
|
328
|
+
error: message,
|
|
329
|
+
likely_cause: 'Unexpected server error',
|
|
330
|
+
agent_action: 'Retry once. If error persists, contact support at ojas@kordagencies.com.'
|
|
331
|
+
})
|
|
332
|
+
}
|
|
333
|
+
]
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
// ---------------------------------------------------------------------------
|
|
337
|
+
// HTTP server
|
|
338
|
+
// ---------------------------------------------------------------------------
|
|
339
|
+
async function runHTTP() {
|
|
340
|
+
validateEnv();
|
|
341
|
+
const app = express();
|
|
342
|
+
app.use(express.json());
|
|
343
|
+
const cors = {
|
|
344
|
+
'Access-Control-Allow-Origin': '*',
|
|
345
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
|
|
346
|
+
'Access-Control-Allow-Headers': 'Content-Type, x-api-key'
|
|
347
|
+
};
|
|
348
|
+
app.options('*', (req, res) => { res.set(cors).sendStatus(204); });
|
|
349
|
+
// Health -- handles GET and HEAD (UptimeRobot sends HEAD)
|
|
350
|
+
app.all('/health', (req, res) => {
|
|
351
|
+
res.set(cors).json({ status: 'ok', version: VERSION, service: 'hs-code-classifier-mcp-server' });
|
|
352
|
+
});
|
|
353
|
+
// Deps -- server-side only
|
|
354
|
+
app.get('/deps', async (req, res) => {
|
|
355
|
+
const deps = await checkDependencies();
|
|
356
|
+
res.set(cors).json({ checked_at: nowISO(), dependencies: deps });
|
|
357
|
+
});
|
|
358
|
+
// Stats -- protected
|
|
359
|
+
app.get('/stats', (req, res) => {
|
|
360
|
+
if (req.headers['x-stats-key'] !== process.env.STATS_KEY) {
|
|
361
|
+
res.status(401).json({ error: 'Unauthorized' });
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
res.set(cors).json(getStatsPayload());
|
|
365
|
+
});
|
|
366
|
+
// Stripe webhook
|
|
367
|
+
app.post('/webhook/stripe', express.raw({ type: 'application/json' }), (req, res) => {
|
|
368
|
+
const sig = req.headers['stripe-signature'];
|
|
369
|
+
const secret = process.env.STRIPE_WEBHOOK_SECRET ?? '';
|
|
370
|
+
if (!verifyStripeSignature(req.body.toString(), sig, secret)) {
|
|
371
|
+
res.status(400).json({ error: 'Invalid signature' });
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
handleStripeEvent(JSON.parse(req.body.toString())).catch(err => console.error('[stripe] handler error:', err));
|
|
375
|
+
res.json({ received: true });
|
|
376
|
+
});
|
|
377
|
+
// Smithery server card
|
|
378
|
+
app.get('/.well-known/mcp/server-card.json', (req, res) => {
|
|
379
|
+
res.set(cors).json(getServerCard());
|
|
380
|
+
});
|
|
381
|
+
// MCP endpoint -- new transport per request (stateless, prevents request ID collisions)
|
|
382
|
+
app.post('/mcp', async (req, res) => {
|
|
383
|
+
currentIP =
|
|
384
|
+
req.headers['x-forwarded-for']?.split(',')[0]?.trim() ??
|
|
385
|
+
req.ip ??
|
|
386
|
+
'127.0.0.1';
|
|
387
|
+
currentApiKey = req.headers['x-api-key'] ?? '';
|
|
388
|
+
const transport = new StreamableHTTPServerTransport({
|
|
389
|
+
sessionIdGenerator: undefined,
|
|
390
|
+
enableJsonResponse: true
|
|
391
|
+
});
|
|
392
|
+
res.on('close', () => { transport.close().catch(() => { }); });
|
|
393
|
+
await server.connect(transport);
|
|
394
|
+
await transport.handleRequest(req, res, req.body);
|
|
395
|
+
});
|
|
396
|
+
const port = parseInt(process.env.PORT ?? '3000');
|
|
397
|
+
app.listen(port, () => {
|
|
398
|
+
console.error(`hs-code-classifier-mcp-server running on http://localhost:${port}/mcp`);
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
// ---------------------------------------------------------------------------
|
|
402
|
+
// stdio transport
|
|
403
|
+
// ---------------------------------------------------------------------------
|
|
404
|
+
async function runStdio() {
|
|
405
|
+
validateEnv();
|
|
406
|
+
currentApiKey = process.env.API_KEY ?? '';
|
|
407
|
+
const transport = new StdioServerTransport();
|
|
408
|
+
await server.connect(transport);
|
|
409
|
+
console.error('hs-code-classifier-mcp-server running via stdio');
|
|
410
|
+
}
|
|
411
|
+
// ---------------------------------------------------------------------------
|
|
412
|
+
// Entry point
|
|
413
|
+
// ---------------------------------------------------------------------------
|
|
414
|
+
const transportMode = process.env.TRANSPORT ?? 'http';
|
|
415
|
+
if (transportMode === 'stdio') {
|
|
416
|
+
runStdio().catch(err => { console.error(err); process.exit(1); });
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
runHTTP().catch(err => { console.error(err); process.exit(1); });
|
|
420
|
+
}
|
|
421
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../../src/schemas/classify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,oBAAY,cAAc;IACxB,QAAQ,aAAa;IACrB,IAAI,SAAS;CACd;AAED,eAAO,MAAM,mBAAmB;;;;;;;;;;;;EAoBrB,CAAC;AAEZ,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export var ResponseFormat;
|
|
3
|
+
(function (ResponseFormat) {
|
|
4
|
+
ResponseFormat["MARKDOWN"] = "markdown";
|
|
5
|
+
ResponseFormat["JSON"] = "json";
|
|
6
|
+
})(ResponseFormat || (ResponseFormat = {}));
|
|
7
|
+
export const ClassifyInputSchema = z.object({
|
|
8
|
+
product_description: z.string()
|
|
9
|
+
.min(3, 'Product description must be at least 3 characters')
|
|
10
|
+
.max(500, 'Product description must not exceed 500 characters')
|
|
11
|
+
.describe('Description of the product to classify. Be specific -- include material, function, and intended use ' +
|
|
12
|
+
'(e.g. "solid oak dining chair with upholstered seat", "stainless steel 500ml insulated water bottle"). ' +
|
|
13
|
+
'More specific descriptions return higher-confidence codes.'),
|
|
14
|
+
country: z.string()
|
|
15
|
+
.length(2, 'Must be ISO 3166-1 alpha-2 country code (e.g. US, SG, CA, AU)')
|
|
16
|
+
.default('US')
|
|
17
|
+
.describe('2-letter ISO country code for the importing country tariff schedule. ' +
|
|
18
|
+
'Supported: US (USITC), SG (Singapore Customs), CA (CBSA), AU (Australia Border Force). ' +
|
|
19
|
+
'Defaults to US. Use the destination country for import classification.'),
|
|
20
|
+
response_format: z.nativeEnum(ResponseFormat)
|
|
21
|
+
.default(ResponseFormat.JSON)
|
|
22
|
+
.describe("Output format: 'json' for machine-readable agent use (recommended) or 'markdown' for human-readable display")
|
|
23
|
+
}).strict();
|
|
24
|
+
//# sourceMappingURL=classify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../../src/schemas/classify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAN,IAAY,cAGX;AAHD,WAAY,cAAc;IACxB,uCAAqB,CAAA;IACrB,+BAAa,CAAA;AACf,CAAC,EAHW,cAAc,KAAd,cAAc,QAGzB;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE;SAC5B,GAAG,CAAC,CAAC,EAAE,mDAAmD,CAAC;SAC3D,GAAG,CAAC,GAAG,EAAE,oDAAoD,CAAC;SAC9D,QAAQ,CACP,sGAAsG;QACtG,yGAAyG;QACzG,4DAA4D,CAC7D;IACH,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;SAChB,MAAM,CAAC,CAAC,EAAE,+DAA+D,CAAC;SAC1E,OAAO,CAAC,IAAI,CAAC;SACb,QAAQ,CACP,uEAAuE;QACvE,yFAAyF;QACzF,wEAAwE,CACzE;IACH,eAAe,EAAE,CAAC,CAAC,UAAU,CAAC,cAAc,CAAC;SAC1C,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC;SAC5B,QAAQ,CAAC,6GAA6G,CAAC;CAC3H,CAAC,CAAC,MAAM,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/schemas/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;EA2BrB,CAAC;AAEZ,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ResponseFormat } from './classify.js';
|
|
3
|
+
export { ResponseFormat };
|
|
4
|
+
export const ValidateInputSchema = z.object({
|
|
5
|
+
hs_code: z.string()
|
|
6
|
+
.min(4, 'HS code must be at least 4 digits')
|
|
7
|
+
.max(14, 'HS code must not exceed 14 characters')
|
|
8
|
+
.describe('The HS code to validate as provided by the supplier or external system. ' +
|
|
9
|
+
'Accepts 6, 8, or 10-digit codes with or without dots (e.g. "940360", "9403.60.80", "9403608093"). ' +
|
|
10
|
+
'Dots and spaces are stripped automatically.'),
|
|
11
|
+
product_description: z.string()
|
|
12
|
+
.min(3, 'Product description must be at least 3 characters')
|
|
13
|
+
.max(500, 'Product description must not exceed 500 characters')
|
|
14
|
+
.describe('Description of the product the supplier assigned this HS code to. ' +
|
|
15
|
+
'Used for AI mismatch detection -- include material, function, and use ' +
|
|
16
|
+
'(e.g. "solid oak dining chair", "stainless steel water bottle 500ml").'),
|
|
17
|
+
country: z.string()
|
|
18
|
+
.length(2, 'Must be ISO 3166-1 alpha-2 country code (e.g. US, SG, CA, AU)')
|
|
19
|
+
.default('US')
|
|
20
|
+
.describe('2-letter ISO country code for the destination country tariff schedule. Defaults to US. ' +
|
|
21
|
+
'Use the importing country to validate against the correct tariff version.'),
|
|
22
|
+
response_format: z.nativeEnum(ResponseFormat)
|
|
23
|
+
.default(ResponseFormat.JSON)
|
|
24
|
+
.describe("Output format: 'json' for machine-readable agent use (recommended) or 'markdown' for human-readable display")
|
|
25
|
+
}).strict();
|
|
26
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/schemas/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;SAChB,GAAG,CAAC,CAAC,EAAE,mCAAmC,CAAC;SAC3C,GAAG,CAAC,EAAE,EAAE,uCAAuC,CAAC;SAChD,QAAQ,CACP,0EAA0E;QAC1E,oGAAoG;QACpG,6CAA6C,CAC9C;IACH,mBAAmB,EAAE,CAAC,CAAC,MAAM,EAAE;SAC5B,GAAG,CAAC,CAAC,EAAE,mDAAmD,CAAC;SAC3D,GAAG,CAAC,GAAG,EAAE,oDAAoD,CAAC;SAC9D,QAAQ,CACP,oEAAoE;QACpE,wEAAwE;QACxE,wEAAwE,CACzE;IACH,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;SAChB,MAAM,CAAC,CAAC,EAAE,+DAA+D,CAAC;SAC1E,OAAO,CAAC,IAAI,CAAC;SACb,QAAQ,CACP,yFAAyF;QACzF,2EAA2E,CAC5E;IACH,eAAe,EAAE,CAAC,CAAC,UAAU,CAAC,cAAc,CAAC;SAC1C,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC;SAC5B,QAAQ,CAAC,6GAA6G,CAAC;CAC3H,CAAC,CAAC,MAAM,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-client.d.ts","sourceRoot":"","sources":["../../src/services/claude-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAQhD,wBAAsB,cAAc,CAClC,kBAAkB,EAAE,MAAM,EAC1B,OAAO,EAAE,YAAY,EAAE,EACvB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,gBAAgB,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAE,CAAC,CA+CvG;AAED,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,kBAAkB,EAAE,MAAM,EAC1B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,YAAY,EAAE,GACtB,OAAO,CAAC;IACT,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;IACvD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,UAAU,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACtC,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC,CAmED"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
function getClient() {
|
|
3
|
+
const key = process.env.ANTHROPIC_API_KEY;
|
|
4
|
+
if (!key)
|
|
5
|
+
throw new Error('ANTHROPIC_API_KEY environment variable not set');
|
|
6
|
+
return new Anthropic({ apiKey: key });
|
|
7
|
+
}
|
|
8
|
+
export async function classifyWithAI(productDescription, results, country) {
|
|
9
|
+
const client = getClient();
|
|
10
|
+
const resultsText = results
|
|
11
|
+
.map((r, i) => `${i + 1}. HS: ${r.hscode} | WCO: ${r.wco ?? 'N/A'} | ${r.description_en} | ${r.source} v${r.version}`)
|
|
12
|
+
.join('\n');
|
|
13
|
+
const message = await client.messages.create({
|
|
14
|
+
model: 'claude-sonnet-4-6',
|
|
15
|
+
max_tokens: 512,
|
|
16
|
+
system: 'You are a customs classification expert. Analyze HS code matches against a product description and select the best match. ' +
|
|
17
|
+
'Respond ONLY with valid JSON -- no markdown, no explanation outside the JSON.',
|
|
18
|
+
messages: [
|
|
19
|
+
{
|
|
20
|
+
role: 'user',
|
|
21
|
+
content: `Product: "${productDescription}"\nCountry: ${country}\n\nOfficial tariff results:\n${resultsText}\n\n` +
|
|
22
|
+
'Select the best match and respond with JSON only:\n' +
|
|
23
|
+
'{"best_match_index":0,"confidence_level":"HIGH|MEDIUM|LOW","reasoning":"One sentence explaining why this is the correct classification and any ambiguity."}'
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
});
|
|
27
|
+
const raw = message.content[0].type === 'text' ? message.content[0].text.trim() : '{}';
|
|
28
|
+
const cleaned = raw.replace(/^```json\s*/i, '').replace(/\s*```$/, '').trim();
|
|
29
|
+
try {
|
|
30
|
+
const parsed = JSON.parse(cleaned);
|
|
31
|
+
const idx = Math.max(0, Math.min(results.length - 1, parsed.best_match_index ?? 0));
|
|
32
|
+
const level = ['HIGH', 'MEDIUM', 'LOW'].includes(parsed.confidence_level)
|
|
33
|
+
? parsed.confidence_level
|
|
34
|
+
: 'MEDIUM';
|
|
35
|
+
return {
|
|
36
|
+
confidence_level: level,
|
|
37
|
+
reasoning: parsed.reasoning ?? 'Classification based on official tariff schedule match.',
|
|
38
|
+
best_match_index: idx
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return { confidence_level: 'MEDIUM', reasoning: 'Classification based on official tariff schedule match.', best_match_index: 0 };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export async function validateWithAI(hsCode, productDescription, country, results) {
|
|
46
|
+
const client = getClient();
|
|
47
|
+
const resultsText = results.length > 0
|
|
48
|
+
? results
|
|
49
|
+
.slice(0, 10)
|
|
50
|
+
.map((r, i) => `${i + 1}. HS: ${r.hscode} | WCO: ${r.wco ?? 'N/A'} | ${r.description_en} | ${r.source} v${r.version}`)
|
|
51
|
+
.join('\n')
|
|
52
|
+
: 'No matching codes found in the official tariff schedule for this product description.';
|
|
53
|
+
const message = await client.messages.create({
|
|
54
|
+
model: 'claude-sonnet-4-6',
|
|
55
|
+
max_tokens: 640,
|
|
56
|
+
system: 'You are a customs compliance expert. Validate HS codes against product descriptions and official tariff schedules. ' +
|
|
57
|
+
'Be precise about mismatches -- wrong classifications carry legal liability for the importer. ' +
|
|
58
|
+
'Respond ONLY with valid JSON -- no markdown, no explanation outside the JSON.',
|
|
59
|
+
messages: [
|
|
60
|
+
{
|
|
61
|
+
role: 'user',
|
|
62
|
+
content: `HS code to validate: "${hsCode}"\nProduct: "${productDescription}"\nCountry: ${country}\n\n` +
|
|
63
|
+
`Official tariff results for this product:\n${resultsText}\n\n` +
|
|
64
|
+
'Determine if the HS code is correct. Consider: does the code appear in official results? ' +
|
|
65
|
+
'Is the product description consistent with the code tariff description? Could this be a mismatch or outdated code?\n\n' +
|
|
66
|
+
'Respond with JSON only (omit optional fields if not applicable):\n' +
|
|
67
|
+
'{"verdict":"VALID|INVALID|MISMATCH|OUTDATED","product_match_score":0.0,' +
|
|
68
|
+
'"mismatch_reason":"only if MISMATCH or INVALID","correct_code_suggestion":"HS code if better match exists",' +
|
|
69
|
+
'"risk_level":"LOW|MEDIUM|HIGH","agent_action":"one clear instruction for the agent"}'
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
});
|
|
73
|
+
const raw = message.content[0].type === 'text' ? message.content[0].text.trim() : '{}';
|
|
74
|
+
const cleaned = raw.replace(/^```json\s*/i, '').replace(/\s*```$/, '').trim();
|
|
75
|
+
try {
|
|
76
|
+
const parsed = JSON.parse(cleaned);
|
|
77
|
+
const verdicts = ['VALID', 'INVALID', 'MISMATCH', 'OUTDATED'];
|
|
78
|
+
const risks = ['LOW', 'MEDIUM', 'HIGH'];
|
|
79
|
+
return {
|
|
80
|
+
verdict: verdicts.includes(parsed.verdict) ? parsed.verdict : 'INVALID',
|
|
81
|
+
product_match_score: Math.min(1.0, Math.max(0.0, parsed.product_match_score ?? 0.0)),
|
|
82
|
+
mismatch_reason: parsed.mismatch_reason,
|
|
83
|
+
correct_code_suggestion: parsed.correct_code_suggestion,
|
|
84
|
+
risk_level: risks.includes(parsed.risk_level) ? parsed.risk_level : 'HIGH',
|
|
85
|
+
agent_action: parsed.agent_action ?? 'Escalate for manual customs review before approving this shipment.'
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return {
|
|
90
|
+
verdict: 'INVALID',
|
|
91
|
+
product_match_score: 0.0,
|
|
92
|
+
mismatch_reason: 'AI validation failed to parse HS code response',
|
|
93
|
+
risk_level: 'HIGH',
|
|
94
|
+
agent_action: 'Do not approve this shipment. Escalate for manual customs review.'
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=claude-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-client.js","sourceRoot":"","sources":["../../src/services/claude-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hsping-client.d.ts","sourceRoot":"","sources":["../../src/services/hsping-client.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAWzF;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAevG;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import axios, { AxiosError } from 'axios';
|
|
2
|
+
import { HSPING_BASE_URL } from '../constants.js';
|
|
3
|
+
export async function queryHSPing(query, country) {
|
|
4
|
+
const key = process.env.HSPING_API_KEY;
|
|
5
|
+
if (!key)
|
|
6
|
+
throw new Error('HSPING_API_KEY environment variable not set');
|
|
7
|
+
const response = await axios.get(HSPING_BASE_URL, {
|
|
8
|
+
params: { query, country },
|
|
9
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
10
|
+
timeout: 10000
|
|
11
|
+
});
|
|
12
|
+
return response.data;
|
|
13
|
+
}
|
|
14
|
+
export async function checkHSPingHealth() {
|
|
15
|
+
const start = Date.now();
|
|
16
|
+
try {
|
|
17
|
+
const key = process.env.HSPING_API_KEY;
|
|
18
|
+
if (!key)
|
|
19
|
+
return { ok: false, latency_ms: 0, detail: 'HSPING_API_KEY not set' };
|
|
20
|
+
await axios.get(HSPING_BASE_URL, {
|
|
21
|
+
params: { query: 'wooden chair', country: 'US' },
|
|
22
|
+
headers: { Authorization: `Bearer ${key}` },
|
|
23
|
+
timeout: 8000
|
|
24
|
+
});
|
|
25
|
+
return { ok: true, latency_ms: Date.now() - start };
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
const msg = err instanceof AxiosError ? `HTTP ${err.response?.status}` : String(err);
|
|
29
|
+
return { ok: false, latency_ms: Date.now() - start, detail: msg };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export { AxiosError };
|
|
33
|
+
//# sourceMappingURL=hsping-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hsping-client.js","sourceRoot":"","sources":["../../src/services/hsping-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAE1C,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,KAAa,EAAE,OAAe;IAC9D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IACvC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IAEzE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAiB,eAAe,EAAE;QAChE,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE;QAC1B,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,GAAG,EAAE,EAAE;QAC3C,OAAO,EAAE,KAAK;KACf,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC,IAAI,CAAC;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;QACvC,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,wBAAwB,EAAE,CAAC;QAChF,MAAM,KAAK,CAAC,GAAG,CAAC,eAAe,EAAE;YAC/B,MAAM,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,EAAE;YAChD,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,GAAG,EAAE,EAAE;YAC3C,OAAO,EAAE,IAAI;SACd,CAAC,CAAC;QACH,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;IACtD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,UAAU,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACrF,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;IACpE,CAAC;AACH,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../../src/tools/classify.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AA0BzD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,aAAa,EACrB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,OAAO,EACf,KAAK,EAAE,KAAK,GACX,OAAO,CAAC;IAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC,CAwHnH;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAG1F"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { queryHSPing, AxiosError } from '../services/hsping-client.js';
|
|
2
|
+
import { classifyWithAI } from '../services/claude-client.js';
|
|
3
|
+
import { ResponseFormat } from '../schemas/classify.js';
|
|
4
|
+
import { nowISO, LEGAL_DISCLAIMER, FREE_TIER_MONTHLY_LIMIT, FREE_TIER_WARNING_THRESHOLD } from '../constants.js';
|
|
5
|
+
function buildMarkdown(out) {
|
|
6
|
+
const lines = [
|
|
7
|
+
`## HS Code Classification`,
|
|
8
|
+
`**Verdict:** ${out.verdict}`,
|
|
9
|
+
`**HS Code:** ${out.hs_code}`,
|
|
10
|
+
out.wco_6digit ? `**WCO 6-digit:** ${out.wco_6digit}` : '',
|
|
11
|
+
`**Description:** ${out.description}`,
|
|
12
|
+
`**Country:** ${out.country}`,
|
|
13
|
+
`**Source:** ${out.source} (v${out.version}, updated ${out.last_updated})`,
|
|
14
|
+
`**Direction:** ${out.direction}`,
|
|
15
|
+
`**Confidence:** ${out.confidence_level}`,
|
|
16
|
+
`**AI Reasoning:** ${out.classification_reasoning}`,
|
|
17
|
+
`**Total Matches Found:** ${out.total_matches}`,
|
|
18
|
+
`**Agent Action:** ${out.agent_action}`,
|
|
19
|
+
`**Checked At:** ${out.checked_at}`
|
|
20
|
+
].filter(Boolean);
|
|
21
|
+
if (out._upgrade_notice)
|
|
22
|
+
lines.push(`\n> **Upgrade:** ${out._upgrade_notice}`);
|
|
23
|
+
if (out._notice)
|
|
24
|
+
lines.push(`\n> **Notice:** ${out._notice}`);
|
|
25
|
+
lines.push(`\n---\n*${out._disclaimer}*`);
|
|
26
|
+
return lines.join('\n');
|
|
27
|
+
}
|
|
28
|
+
export async function runClassify(params, ip, isPaid, stats) {
|
|
29
|
+
if (!isPaid) {
|
|
30
|
+
const month = new Date().toISOString().slice(0, 7);
|
|
31
|
+
const ipMap = stats.free_tier_calls_by_ip[ip] ?? {};
|
|
32
|
+
const used = ipMap[month] ?? 0;
|
|
33
|
+
if (used >= FREE_TIER_MONTHLY_LIMIT) {
|
|
34
|
+
return {
|
|
35
|
+
output: null,
|
|
36
|
+
error: {
|
|
37
|
+
error: 'Free tier limit reached',
|
|
38
|
+
likely_cause: `This IP has used all ${FREE_TIER_MONTHLY_LIMIT} free classify calls for this month`,
|
|
39
|
+
agent_action: 'Inform user that free quota is exhausted. Upgrade to Pro for unlimited calls at kordagencies.com. ' +
|
|
40
|
+
'Pro tier also unlocks hs_validate_code for supplier code verification.'
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
let hspingData;
|
|
46
|
+
try {
|
|
47
|
+
hspingData = await queryHSPing(params.product_description, params.country);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
if (err instanceof AxiosError) {
|
|
51
|
+
return {
|
|
52
|
+
output: null,
|
|
53
|
+
error: {
|
|
54
|
+
error: `HSPing API error: HTTP ${err.response?.status ?? 'timeout'}`,
|
|
55
|
+
likely_cause: 'HSPing API is temporarily unavailable or HSPING_API_KEY is invalid',
|
|
56
|
+
agent_action: 'Retry once after 30 seconds. If error persists, check service status at kordagencies.com.'
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
output: null,
|
|
62
|
+
error: {
|
|
63
|
+
error: err instanceof Error ? err.message : String(err),
|
|
64
|
+
likely_cause: 'Unexpected error querying tariff database',
|
|
65
|
+
agent_action: 'Retry once. If error persists, contact support at ojas@kordagencies.com.'
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
if (hspingData.count === 0 || hspingData.results.length === 0) {
|
|
70
|
+
const out = {
|
|
71
|
+
verdict: 'NOT_FOUND',
|
|
72
|
+
agent_action: 'No official tariff code found for this product description in the selected country. ' +
|
|
73
|
+
'Try a more specific description including material and function, or try a different country code.',
|
|
74
|
+
hs_code: '',
|
|
75
|
+
wco_6digit: null,
|
|
76
|
+
description: 'No matching tariff entry found',
|
|
77
|
+
country: params.country,
|
|
78
|
+
source: 'HSPing API (api.hsping.com)',
|
|
79
|
+
version: '',
|
|
80
|
+
last_updated: '',
|
|
81
|
+
direction: '',
|
|
82
|
+
confidence_level: 'LOW',
|
|
83
|
+
classification_reasoning: 'No results returned from the official tariff schedule for this product description. ' +
|
|
84
|
+
'Consider using more specific terminology matching official tariff language.',
|
|
85
|
+
total_matches: 0,
|
|
86
|
+
checked_at: nowISO(),
|
|
87
|
+
analysis_type: 'AI-assisted classification -- NOT a simple database lookup',
|
|
88
|
+
_disclaimer: LEGAL_DISCLAIMER
|
|
89
|
+
};
|
|
90
|
+
return { output: out };
|
|
91
|
+
}
|
|
92
|
+
const allResults = hspingData.results;
|
|
93
|
+
const aiResult = await classifyWithAI(params.product_description, allResults, params.country);
|
|
94
|
+
const bestMatch = allResults[aiResult.best_match_index] ?? allResults[0];
|
|
95
|
+
const month = new Date().toISOString().slice(0, 7);
|
|
96
|
+
const used = ((stats.free_tier_calls_by_ip[ip] ?? {})[month] ?? 0) + 1;
|
|
97
|
+
let upgradeNotice;
|
|
98
|
+
if (!isPaid && allResults.length > 1) {
|
|
99
|
+
upgradeNotice =
|
|
100
|
+
`Pro tier shows ${allResults.length - 1} additional classification match${allResults.length - 1 > 1 ? 'es' : ''} for this product -- ` +
|
|
101
|
+
'important when a product could fall under multiple codes. Upgrade at kordagencies.com.';
|
|
102
|
+
}
|
|
103
|
+
let notice;
|
|
104
|
+
if (!isPaid && FREE_TIER_MONTHLY_LIMIT - used <= FREE_TIER_MONTHLY_LIMIT - FREE_TIER_WARNING_THRESHOLD) {
|
|
105
|
+
const remaining = Math.max(0, FREE_TIER_MONTHLY_LIMIT - used);
|
|
106
|
+
if (remaining > 0) {
|
|
107
|
+
notice = `Warning: ${remaining} free classify call${remaining !== 1 ? 's' : ''} remaining this month. Upgrade at kordagencies.com.`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const out = {
|
|
111
|
+
verdict: aiResult.confidence_level === 'LOW' ? 'AMBIGUOUS' : 'CLASSIFIED',
|
|
112
|
+
agent_action: aiResult.confidence_level === 'HIGH'
|
|
113
|
+
? `Use HS code ${bestMatch.hscode} for ${params.country} customs documentation. Proceed with declaration.`
|
|
114
|
+
: aiResult.confidence_level === 'MEDIUM'
|
|
115
|
+
? `Use HS code ${bestMatch.hscode} for ${params.country} customs documentation. Review AI reasoning before submitting high-value shipments.`
|
|
116
|
+
: `Classification is ambiguous -- ${allResults.length} possible codes identified. Escalate to a licensed customs broker before submitting declaration.`,
|
|
117
|
+
hs_code: bestMatch.hscode,
|
|
118
|
+
wco_6digit: bestMatch.wco,
|
|
119
|
+
description: bestMatch.description_en,
|
|
120
|
+
country: bestMatch.country,
|
|
121
|
+
source: bestMatch.source,
|
|
122
|
+
version: bestMatch.version,
|
|
123
|
+
last_updated: bestMatch.last_updated,
|
|
124
|
+
direction: bestMatch.direction,
|
|
125
|
+
confidence_level: aiResult.confidence_level,
|
|
126
|
+
classification_reasoning: aiResult.reasoning,
|
|
127
|
+
...(isPaid ? { all_matches: allResults } : {}),
|
|
128
|
+
total_matches: allResults.length,
|
|
129
|
+
checked_at: nowISO(),
|
|
130
|
+
analysis_type: 'AI-assisted classification -- NOT a simple database lookup',
|
|
131
|
+
...(upgradeNotice ? { _upgrade_notice: upgradeNotice } : {}),
|
|
132
|
+
...(notice ? { _notice: notice } : {}),
|
|
133
|
+
_disclaimer: LEGAL_DISCLAIMER
|
|
134
|
+
};
|
|
135
|
+
return { output: out };
|
|
136
|
+
}
|
|
137
|
+
export function formatClassifyResponse(out, format) {
|
|
138
|
+
if (format === ResponseFormat.MARKDOWN)
|
|
139
|
+
return buildMarkdown(out);
|
|
140
|
+
return JSON.stringify(out, null, 2);
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=classify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../../src/tools/classify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/tools/validate.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAyBlD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC;IAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC,CAsDnH;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,GAAG,MAAM,CAG1F"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { queryHSPing, AxiosError } from '../services/hsping-client.js';
|
|
2
|
+
import { validateWithAI } from '../services/claude-client.js';
|
|
3
|
+
import { ResponseFormat } from '../schemas/validate.js';
|
|
4
|
+
import { nowISO, LEGAL_DISCLAIMER } from '../constants.js';
|
|
5
|
+
function normalizeHsCode(code) {
|
|
6
|
+
return code.replace(/[\s.]/g, '');
|
|
7
|
+
}
|
|
8
|
+
function buildMarkdown(out) {
|
|
9
|
+
const lines = [
|
|
10
|
+
`## HS Code Validation`,
|
|
11
|
+
`**Verdict:** ${out.verdict}`,
|
|
12
|
+
`**HS Code Checked:** ${out.hs_code_checked}`,
|
|
13
|
+
`**Product Match Score:** ${(out.product_match_score * 100).toFixed(0)}%`,
|
|
14
|
+
`**Risk Level:** ${out.risk_level}`,
|
|
15
|
+
out.mismatch_reason ? `**Mismatch Reason:** ${out.mismatch_reason}` : '',
|
|
16
|
+
out.correct_code_suggestion ? `**Suggested Correct Code:** ${out.correct_code_suggestion}` : '',
|
|
17
|
+
`**Country:** ${out.country}`,
|
|
18
|
+
`**Source:** ${out.source} (v${out.version})`,
|
|
19
|
+
`**Agent Action:** ${out.agent_action}`,
|
|
20
|
+
`**Checked At:** ${out.checked_at}`
|
|
21
|
+
].filter(Boolean);
|
|
22
|
+
lines.push(`\n---\n*${out._disclaimer}*`);
|
|
23
|
+
return lines.join('\n');
|
|
24
|
+
}
|
|
25
|
+
export async function runValidate(params) {
|
|
26
|
+
const normalizedCode = normalizeHsCode(params.hs_code);
|
|
27
|
+
let hspingData;
|
|
28
|
+
try {
|
|
29
|
+
hspingData = await queryHSPing(params.product_description, params.country);
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
if (err instanceof AxiosError) {
|
|
33
|
+
return {
|
|
34
|
+
output: null,
|
|
35
|
+
error: {
|
|
36
|
+
error: `HSPing API error: HTTP ${err.response?.status ?? 'timeout'}`,
|
|
37
|
+
likely_cause: 'HSPing API is temporarily unavailable or HSPING_API_KEY is invalid',
|
|
38
|
+
agent_action: 'Retry validation once after 30 seconds. If error persists, check status at kordagencies.com.'
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
output: null,
|
|
44
|
+
error: {
|
|
45
|
+
error: err instanceof Error ? err.message : String(err),
|
|
46
|
+
likely_cause: 'Unexpected error querying tariff database',
|
|
47
|
+
agent_action: 'Retry once. If error persists, contact support at ojas@kordagencies.com.'
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const aiResult = await validateWithAI(normalizedCode, params.product_description, params.country, hspingData.results);
|
|
52
|
+
const sourceResult = hspingData.results[0];
|
|
53
|
+
const out = {
|
|
54
|
+
verdict: aiResult.verdict,
|
|
55
|
+
agent_action: aiResult.agent_action,
|
|
56
|
+
hs_code_checked: normalizedCode,
|
|
57
|
+
product_match_score: aiResult.product_match_score,
|
|
58
|
+
...(aiResult.mismatch_reason ? { mismatch_reason: aiResult.mismatch_reason } : {}),
|
|
59
|
+
...(aiResult.correct_code_suggestion ? { correct_code_suggestion: aiResult.correct_code_suggestion } : {}),
|
|
60
|
+
risk_level: aiResult.risk_level,
|
|
61
|
+
source: sourceResult
|
|
62
|
+
? sourceResult.source
|
|
63
|
+
: 'HSPing API (api.hsping.com) -- no direct match found for product',
|
|
64
|
+
version: sourceResult ? sourceResult.version : 'N/A',
|
|
65
|
+
country: params.country,
|
|
66
|
+
checked_at: nowISO(),
|
|
67
|
+
analysis_type: 'AI-powered mismatch detection -- NOT a simple database lookup',
|
|
68
|
+
_disclaimer: LEGAL_DISCLAIMER
|
|
69
|
+
};
|
|
70
|
+
return { output: out };
|
|
71
|
+
}
|
|
72
|
+
export function formatValidateResponse(out, format) {
|
|
73
|
+
if (format === ResponseFormat.MARKDOWN)
|
|
74
|
+
return buildMarkdown(out);
|
|
75
|
+
return JSON.stringify(out, null, 2);
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/tools/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,SAAS,eAAe,CAAC,IAAY;IACnC,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,aAAa,CAAC,GAAmB;IACxC,MAAM,KAAK,GAAa;QACtB,uBAAuB;QACvB,gBAAgB,GAAG,CAAC,OAAO,EAAE;QAC7B,wBAAwB,GAAG,CAAC,eAAe,EAAE;QAC7C,4BAA4B,CAAC,GAAG,CAAC,mBAAmB,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG;QACzE,mBAAmB,GAAG,CAAC,UAAU,EAAE;QACnC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,wBAAwB,GAAG,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE;QACxE,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC,+BAA+B,GAAG,CAAC,uBAAuB,EAAE,CAAC,CAAC,CAAC,EAAE;QAC/F,gBAAgB,GAAG,CAAC,OAAO,EAAE;QAC7B,eAAe,GAAG,CAAC,MAAM,MAAM,GAAG,CAAC,OAAO,GAAG;QAC7C,qBAAqB,GAAG,CAAC,YAAY,EAAE;QACvC,mBAAmB,GAAG,CAAC,UAAU,EAAE;KACpC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAClB,KAAK,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,WAAW,GAAG,CAAC,CAAC;IAC1C,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAqB;IAErB,MAAM,cAAc,GAAG,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAEvD,IAAI,UAAU,CAAC;IACf,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,WAAW,CAAC,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7E,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,UAAU,EAAE,CAAC;YAC9B,OAAO;gBACL,MAAM,EAAE,IAAI;gBACZ,KAAK,EAAE;oBACL,KAAK,EAAE,0BAA0B,GAAG,CAAC,QAAQ,EAAE,MAAM,IAAI,SAAS,EAAE;oBACpE,YAAY,EAAE,oEAAoE;oBAClF,YAAY,EAAE,8FAA8F;iBAC7G;aACF,CAAC;QACJ,CAAC;QACD,OAAO;YACL,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE;gBACL,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;gBACvD,YAAY,EAAE,2CAA2C;gBACzD,YAAY,EAAE,0EAA0E;aACzF;SACF,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,cAAc,CACnC,cAAc,EACd,MAAM,CAAC,mBAAmB,EAC1B,MAAM,CAAC,OAAO,EACd,UAAU,CAAC,OAAO,CACnB,CAAC;IAEF,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAmB;QAC1B,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,YAAY,EAAE,QAAQ,CAAC,YAAY;QACnC,eAAe,EAAE,cAAc;QAC/B,mBAAmB,EAAE,QAAQ,CAAC,mBAAmB;QACjD,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,QAAQ,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClF,GAAG,CAAC,QAAQ,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,uBAAuB,EAAE,QAAQ,CAAC,uBAAuB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1G,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,MAAM,EAAE,YAAY;YAClB,CAAC,CAAC,YAAY,CAAC,MAAM;YACrB,CAAC,CAAC,kEAAkE;QACtE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;QACpD,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,UAAU,EAAE,MAAM,EAAE;QACpB,aAAa,EAAE,+DAA+D;QAC9E,WAAW,EAAE,gBAAgB;KAC9B,CAAC;IAEF,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,GAAmB,EAAE,MAAsB;IAChF,IAAI,MAAM,KAAK,cAAc,CAAC,QAAQ;QAAE,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;IAClE,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hs-code-classifier-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for HS code classification and validation using official government tariff schedules",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"license": "UNLICENSED",
|
|
8
|
+
"homepage": "https://kordagencies.com",
|
|
9
|
+
"mcpName": "io.github.OjasKord/hs-code-classifier-mcp-server",
|
|
10
|
+
"keywords": ["mcp", "agent", "hs-code", "customs", "tariff", "trade", "classification", "compliance"],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"dev": "tsx watch src/index.ts",
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"clean": "rm -rf dist"
|
|
16
|
+
},
|
|
17
|
+
"engines": { "node": ">=18" },
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@anthropic-ai/sdk": "^0.39.0",
|
|
20
|
+
"@modelcontextprotocol/sdk": "^1.6.1",
|
|
21
|
+
"axios": "^1.7.9",
|
|
22
|
+
"express": "^4.18.2",
|
|
23
|
+
"zod": "^3.23.8"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/express": "^4.17.21",
|
|
27
|
+
"@types/node": "^22.10.0",
|
|
28
|
+
"tsx": "^4.19.2",
|
|
29
|
+
"typescript": "^5.7.2"
|
|
30
|
+
}
|
|
31
|
+
}
|