potal-mcp-server 1.4.0 → 1.4.2
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/build/index.js +126 -43
- package/package.json +6 -4
package/build/index.js
CHANGED
|
@@ -7,15 +7,15 @@
|
|
|
7
7
|
* for cross-border purchases across 240 countries and territories.
|
|
8
8
|
*
|
|
9
9
|
* Tools (9):
|
|
10
|
-
* - calculate_landed_cost: Calculate total cost for international purchases
|
|
11
|
-
* - classify_product:
|
|
10
|
+
* - calculate_landed_cost: Calculate total cost for international purchases (with 9-field classify support)
|
|
11
|
+
* - classify_product: HS code classification via v3.3 GRI pipeline (9-field input)
|
|
12
12
|
* - check_restrictions: Import restriction & compliance check
|
|
13
|
-
* - screen_shipment: Comprehensive pre-shipment screening (cost + compliance)
|
|
13
|
+
* - screen_shipment: Comprehensive pre-shipment screening (cost + compliance, 9-field classify)
|
|
14
14
|
* - screen_denied_party: Sanctions/denied-party screening (OFAC SDN + CSL, 21K entries)
|
|
15
15
|
* - lookup_fta: Free Trade Agreement lookup (63 FTAs)
|
|
16
16
|
* - list_supported_countries: Get all supported countries with tax info
|
|
17
17
|
* - generate_document: Generate trade documents (CI/PL/C/O)
|
|
18
|
-
* - compare_countries: Compare TLC across multiple destination countries
|
|
18
|
+
* - compare_countries: Compare TLC across multiple destination countries (with 9-field classify)
|
|
19
19
|
*/
|
|
20
20
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
21
21
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -23,11 +23,11 @@ import { z } from "zod";
|
|
|
23
23
|
// ─── Configuration ──────────────────────────────────────────
|
|
24
24
|
const POTAL_API_BASE = "https://www.potal.app/api/v1";
|
|
25
25
|
const API_KEY = process.env.POTAL_API_KEY || "";
|
|
26
|
-
const USER_AGENT = "potal-mcp-server/1.
|
|
26
|
+
const USER_AGENT = "potal-mcp-server/1.4.1";
|
|
27
27
|
// ─── Server Instance ────────────────────────────────────────
|
|
28
28
|
const server = new McpServer({
|
|
29
29
|
name: "potal",
|
|
30
|
-
version: "1.
|
|
30
|
+
version: "1.4.1",
|
|
31
31
|
});
|
|
32
32
|
async function callPotalApi(endpoint, method = "GET", body) {
|
|
33
33
|
const url = `${POTAL_API_BASE}${endpoint}`;
|
|
@@ -110,7 +110,8 @@ server.tool("calculate_landed_cost", "Calculate the total landed cost for a prod
|
|
|
110
110
|
"Returns a detailed breakdown of import duties, VAT/GST, customs fees, and " +
|
|
111
111
|
"the final total price the buyer will pay. Supports 240 countries and territories. " +
|
|
112
112
|
"Includes China CBEC tax, Mexico IEPS, Brazil cascading tax, India IGST, " +
|
|
113
|
-
"and processing fees for 12 countries."
|
|
113
|
+
"and processing fees for 12 countries. " +
|
|
114
|
+
"When providing productName for auto-classification, include material for accurate HS code results.", {
|
|
114
115
|
price: z.number().describe("Product price in USD. Example: 49.99"),
|
|
115
116
|
origin: z
|
|
116
117
|
.string()
|
|
@@ -132,10 +133,22 @@ server.tool("calculate_landed_cost", "Calculate the total landed cost for a prod
|
|
|
132
133
|
.string()
|
|
133
134
|
.optional()
|
|
134
135
|
.describe("Name of the product for automatic HS Code classification. Examples: Cotton T-Shirt, Laptop, Running Shoes"),
|
|
136
|
+
material: z
|
|
137
|
+
.string()
|
|
138
|
+
.optional()
|
|
139
|
+
.describe("Primary material — needed for accurate HS classification. Examples: cotton, leather, steel, polyester, rubber"),
|
|
135
140
|
productCategory: z
|
|
136
141
|
.string()
|
|
137
142
|
.optional()
|
|
138
143
|
.describe("Product category for classification. Examples: electronics, apparel, footwear, accessories, food"),
|
|
144
|
+
processing: z
|
|
145
|
+
.string()
|
|
146
|
+
.optional()
|
|
147
|
+
.describe("Manufacturing/processing method. Examples: woven, knitted, forged, molded"),
|
|
148
|
+
composition: z
|
|
149
|
+
.string()
|
|
150
|
+
.optional()
|
|
151
|
+
.describe("Material composition. Example: '100% cotton', '70% polyester 30% cotton'"),
|
|
139
152
|
hsCode: z
|
|
140
153
|
.string()
|
|
141
154
|
.optional()
|
|
@@ -151,16 +164,28 @@ server.tool("calculate_landed_cost", "Calculate the total landed cost for a prod
|
|
|
151
164
|
],
|
|
152
165
|
};
|
|
153
166
|
}
|
|
154
|
-
const
|
|
167
|
+
const body = {
|
|
155
168
|
price: params.price,
|
|
156
169
|
origin: params.origin,
|
|
157
170
|
destinationCountry: params.destinationCountry,
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
171
|
+
};
|
|
172
|
+
if (params.shippingPrice !== undefined)
|
|
173
|
+
body.shippingPrice = params.shippingPrice;
|
|
174
|
+
if (params.zipcode)
|
|
175
|
+
body.zipcode = params.zipcode;
|
|
176
|
+
if (params.productName)
|
|
177
|
+
body.productName = params.productName;
|
|
178
|
+
if (params.material)
|
|
179
|
+
body.material = params.material;
|
|
180
|
+
if (params.productCategory)
|
|
181
|
+
body.productCategory = params.productCategory;
|
|
182
|
+
if (params.processing)
|
|
183
|
+
body.processing = params.processing;
|
|
184
|
+
if (params.composition)
|
|
185
|
+
body.composition = params.composition;
|
|
186
|
+
if (params.hsCode)
|
|
187
|
+
body.hsCode = params.hsCode;
|
|
188
|
+
const result = await callPotalApi("/calculate", "POST", body);
|
|
164
189
|
if (!result.success) {
|
|
165
190
|
return {
|
|
166
191
|
content: [
|
|
@@ -183,11 +208,19 @@ server.tool("calculate_landed_cost", "Calculate the total landed cost for a prod
|
|
|
183
208
|
};
|
|
184
209
|
});
|
|
185
210
|
// ─── Tool: classify_product ──────────────────────────────────
|
|
186
|
-
server.tool("classify_product", "Classify a product into an HS (Harmonized System) code for customs. " +
|
|
187
|
-
"
|
|
188
|
-
"
|
|
211
|
+
server.tool("classify_product", "Classify a product into an HS (Harmonized System) code for customs using the v3.3 GRI pipeline. " +
|
|
212
|
+
"Supports 9-field input for maximum accuracy. material is REQUIRED for the v3.3 pipeline — " +
|
|
213
|
+
"without it, classification will fail with 400 error. " +
|
|
214
|
+
"Returns HS code, description, confidence, decision path, and GRI rules applied.", {
|
|
189
215
|
productName: z.string().describe("Product name or description. Example: 'cotton t-shirt', 'laptop computer', 'running shoes'"),
|
|
190
|
-
|
|
216
|
+
material: z.string().describe("Primary material — REQUIRED for v3.3 pipeline. Example: 'cotton', 'leather', 'steel', 'polyester', 'rubber', 'wood', 'glass'"),
|
|
217
|
+
productCategory: z.string().optional().describe("Product category for classification. Example: 'apparel', 'electronics', 'footwear', 'kitchenware', 'toys'"),
|
|
218
|
+
originCountry: z.string().optional().describe("Country of origin (ISO 2-letter code). Example: 'CN', 'DE', 'US', 'JP'"),
|
|
219
|
+
description: z.string().optional().describe("Detailed product description for better accuracy. Example: 'Men's woven dress shirt with long sleeves and button front'"),
|
|
220
|
+
processing: z.string().optional().describe("Manufacturing/processing method. Example: 'woven', 'knitted', 'forged', 'molded', 'printed', 'assembled'"),
|
|
221
|
+
composition: z.string().optional().describe("Material composition with percentages. Example: '100% cotton', '70% polyester 30% cotton', '18/8 stainless steel'"),
|
|
222
|
+
weightSpec: z.string().optional().describe("Product weight specification. Example: '500g', '2kg', '150gsm'"),
|
|
223
|
+
price: z.number().optional().describe("Unit price in USD. Used for price-break HS rules (e.g. 'valued over $X'). Example: 49.99"),
|
|
191
224
|
hsCode: z.string().optional().describe("Known HS code to override classification. Example: '6109.10'"),
|
|
192
225
|
}, async (params) => {
|
|
193
226
|
if (!API_KEY) {
|
|
@@ -195,11 +228,27 @@ server.tool("classify_product", "Classify a product into an HS (Harmonized Syste
|
|
|
195
228
|
content: [{ type: "text", text: "❌ POTAL API key not configured." }],
|
|
196
229
|
};
|
|
197
230
|
}
|
|
198
|
-
const
|
|
231
|
+
const body = {
|
|
199
232
|
productName: params.productName,
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
233
|
+
material: params.material,
|
|
234
|
+
};
|
|
235
|
+
if (params.productCategory)
|
|
236
|
+
body.category = params.productCategory;
|
|
237
|
+
if (params.originCountry)
|
|
238
|
+
body.origin_country = params.originCountry;
|
|
239
|
+
if (params.description)
|
|
240
|
+
body.description = params.description;
|
|
241
|
+
if (params.processing)
|
|
242
|
+
body.processing = params.processing;
|
|
243
|
+
if (params.composition)
|
|
244
|
+
body.composition = params.composition;
|
|
245
|
+
if (params.weightSpec)
|
|
246
|
+
body.weight_spec = params.weightSpec;
|
|
247
|
+
if (params.price !== undefined)
|
|
248
|
+
body.price = params.price;
|
|
249
|
+
if (params.hsCode)
|
|
250
|
+
body.hsCode = params.hsCode;
|
|
251
|
+
const result = await callPotalApi("/classify", "POST", body);
|
|
203
252
|
if (!result.success) {
|
|
204
253
|
return {
|
|
205
254
|
content: [{ type: "text", text: `❌ Classification failed: ${result.error}` }],
|
|
@@ -273,11 +322,15 @@ server.tool("check_restrictions", "Check import restrictions for a product in a
|
|
|
273
322
|
// ─── Tool: screen_shipment ──────────────────────────────────
|
|
274
323
|
server.tool("screen_shipment", "Comprehensive shipment screening: calculates landed cost, checks restrictions, " +
|
|
275
324
|
"and identifies trade remedies — all in one call. Use this for a complete " +
|
|
276
|
-
"pre-shipment compliance and cost analysis.", {
|
|
325
|
+
"pre-shipment compliance and cost analysis. Include material for accurate HS classification.", {
|
|
277
326
|
price: z.number().describe("Product price in USD"),
|
|
278
327
|
origin: z.string().length(2).describe("Origin country ISO2"),
|
|
279
328
|
destinationCountry: z.string().length(2).describe("Destination country ISO2"),
|
|
280
329
|
productName: z.string().describe("Product name for classification"),
|
|
330
|
+
material: z.string().optional().describe("Primary material — needed for accurate classification. Example: 'cotton', 'steel', 'leather'"),
|
|
331
|
+
productCategory: z.string().optional().describe("Product category. Example: 'apparel', 'electronics'"),
|
|
332
|
+
processing: z.string().optional().describe("Processing method. Example: 'woven', 'forged'"),
|
|
333
|
+
composition: z.string().optional().describe("Material composition. Example: '100% cotton'"),
|
|
281
334
|
shippingPrice: z.number().optional().describe("Shipping cost in USD"),
|
|
282
335
|
hsCode: z.string().optional().describe("Known HS code"),
|
|
283
336
|
}, async (params) => {
|
|
@@ -286,16 +339,27 @@ server.tool("screen_shipment", "Comprehensive shipment screening: calculates lan
|
|
|
286
339
|
content: [{ type: "text", text: "❌ POTAL API key not configured." }],
|
|
287
340
|
};
|
|
288
341
|
}
|
|
342
|
+
const calcBody = {
|
|
343
|
+
price: params.price,
|
|
344
|
+
origin: params.origin,
|
|
345
|
+
destinationCountry: params.destinationCountry,
|
|
346
|
+
productName: params.productName,
|
|
347
|
+
};
|
|
348
|
+
if (params.material)
|
|
349
|
+
calcBody.material = params.material;
|
|
350
|
+
if (params.productCategory)
|
|
351
|
+
calcBody.productCategory = params.productCategory;
|
|
352
|
+
if (params.processing)
|
|
353
|
+
calcBody.processing = params.processing;
|
|
354
|
+
if (params.composition)
|
|
355
|
+
calcBody.composition = params.composition;
|
|
356
|
+
if (params.shippingPrice !== undefined)
|
|
357
|
+
calcBody.shippingPrice = params.shippingPrice;
|
|
358
|
+
if (params.hsCode)
|
|
359
|
+
calcBody.hsCode = params.hsCode;
|
|
289
360
|
// Run calculate and restrictions in parallel
|
|
290
361
|
const [calcResult, restrictResult] = await Promise.all([
|
|
291
|
-
callPotalApi("/calculate", "POST",
|
|
292
|
-
price: params.price,
|
|
293
|
-
origin: params.origin,
|
|
294
|
-
destinationCountry: params.destinationCountry,
|
|
295
|
-
productName: params.productName,
|
|
296
|
-
shippingPrice: params.shippingPrice,
|
|
297
|
-
hsCode: params.hsCode,
|
|
298
|
-
}),
|
|
362
|
+
callPotalApi("/calculate", "POST", calcBody),
|
|
299
363
|
callPotalApi("/restrictions", "POST", {
|
|
300
364
|
hsCode: params.hsCode || "",
|
|
301
365
|
destinationCountry: params.destinationCountry,
|
|
@@ -414,16 +478,27 @@ server.tool("lookup_fta", "Look up Free Trade Agreements between two countries.
|
|
|
414
478
|
};
|
|
415
479
|
}
|
|
416
480
|
const d = result.data?.data || result.data;
|
|
481
|
+
const ftaInfo = d.fta || d;
|
|
482
|
+
const hasFta = ftaInfo.applicable || ftaInfo.hasFta || ftaInfo.hasFTA || false;
|
|
417
483
|
const lines = [
|
|
418
484
|
"## FTA Lookup Result\n",
|
|
419
485
|
`- **Route**: ${params.origin} → ${params.destination}`,
|
|
420
|
-
`- **FTA Found**: ${
|
|
486
|
+
`- **FTA Found**: ${hasFta ? '✅ Yes' : '❌ No FTA'}`,
|
|
421
487
|
];
|
|
422
|
-
if (
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
488
|
+
if (hasFta) {
|
|
489
|
+
lines.push(`- **Agreement**: ${ftaInfo.name || d.fta?.name || 'N/A'}`);
|
|
490
|
+
lines.push(`- **Code**: ${ftaInfo.code || d.fta?.code || 'N/A'}`);
|
|
491
|
+
if (ftaInfo.preferentialMultiplier !== undefined && ftaInfo.preferentialMultiplier !== null) {
|
|
492
|
+
const pctReduction = ((1 - ftaInfo.preferentialMultiplier) * 100).toFixed(0);
|
|
493
|
+
lines.push(`- **Preferential Rate**: ${ftaInfo.preferentialMultiplier === 0 ? 'Duty-free (0%)' : `${pctReduction}% reduction`}`);
|
|
494
|
+
}
|
|
495
|
+
if (d.rulesOfOrigin) {
|
|
496
|
+
lines.push(`\n### Rules of Origin`);
|
|
497
|
+
lines.push(`- **Certificate Type**: ${d.rulesOfOrigin.certificateType || 'N/A'}`);
|
|
498
|
+
if (d.rulesOfOrigin.rules?.length) {
|
|
499
|
+
for (const rule of d.rulesOfOrigin.rules) {
|
|
500
|
+
lines.push(`- ${rule.criterion || rule.type}: ${rule.description || rule.note || ''}`);
|
|
501
|
+
}
|
|
427
502
|
}
|
|
428
503
|
}
|
|
429
504
|
}
|
|
@@ -490,8 +565,8 @@ server.tool("generate_document", "Generate trade documents for a shipment: Comme
|
|
|
490
565
|
}
|
|
491
566
|
const result = await callPotalApi("/documents", "POST", {
|
|
492
567
|
type: args.documentType,
|
|
493
|
-
exporter: args.exporterName,
|
|
494
|
-
importer: args.importerName,
|
|
568
|
+
exporter: { name: args.exporterName, country: args.originCountry },
|
|
569
|
+
importer: { name: args.importerName, country: args.destinationCountry },
|
|
495
570
|
origin: args.originCountry,
|
|
496
571
|
destination: args.destinationCountry,
|
|
497
572
|
items: [{
|
|
@@ -516,11 +591,13 @@ server.tool("generate_document", "Generate trade documents for a shipment: Comme
|
|
|
516
591
|
// ─── Tool: compare_countries ────────────────────────────────
|
|
517
592
|
server.tool("compare_countries", "Compare Total Landed Cost across multiple destination countries for the same product. " +
|
|
518
593
|
"Useful for finding the cheapest import route or comparing duty rates between countries. " +
|
|
519
|
-
"Returns a side-by-side comparison of costs.", {
|
|
594
|
+
"Returns a side-by-side comparison of costs. Include material for accurate classification.", {
|
|
520
595
|
productName: z.string().describe("Product name/description"),
|
|
521
596
|
price: z.number().describe("Product price in USD"),
|
|
522
597
|
originCountry: z.string().describe("Origin/export country (ISO 2-letter code, e.g. 'CN')"),
|
|
523
598
|
destinationCountries: z.array(z.string()).describe("List of destination countries to compare (ISO 2-letter codes, e.g. ['US', 'GB', 'DE', 'JP'])"),
|
|
599
|
+
material: z.string().optional().describe("Primary material for accurate classification. Example: 'cotton', 'steel'"),
|
|
600
|
+
productCategory: z.string().optional().describe("Product category. Example: 'apparel', 'electronics'"),
|
|
524
601
|
hsCode: z.string().optional().describe("HS code (6+ digits)"),
|
|
525
602
|
shippingPrice: z.number().optional().describe("Shipping cost in USD (default: 0)"),
|
|
526
603
|
}, async (args) => {
|
|
@@ -531,14 +608,20 @@ server.tool("compare_countries", "Compare Total Landed Cost across multiple dest
|
|
|
531
608
|
}
|
|
532
609
|
const results = [];
|
|
533
610
|
for (const country of args.destinationCountries.slice(0, 10)) {
|
|
534
|
-
const
|
|
611
|
+
const body = {
|
|
535
612
|
price: args.price,
|
|
536
613
|
shippingPrice: args.shippingPrice || 0,
|
|
537
614
|
origin: args.originCountry,
|
|
538
615
|
destinationCountry: country,
|
|
539
616
|
productName: args.productName,
|
|
540
|
-
|
|
541
|
-
|
|
617
|
+
};
|
|
618
|
+
if (args.material)
|
|
619
|
+
body.material = args.material;
|
|
620
|
+
if (args.productCategory)
|
|
621
|
+
body.productCategory = args.productCategory;
|
|
622
|
+
if (args.hsCode)
|
|
623
|
+
body.hsCode = args.hsCode;
|
|
624
|
+
const result = await callPotalApi("/calculate", "POST", body);
|
|
542
625
|
if (result.success && result.data) {
|
|
543
626
|
results.push({ country, data: result.data });
|
|
544
627
|
}
|
package/package.json
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "potal-mcp-server",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
4
4
|
"mcpName": "io.github.soulmaten7/potal",
|
|
5
5
|
"description": "Calculate total landed costs for cross-border commerce. 240 countries, 257M+ tariff records, 131K government tariff schedules, 1.36M product-HS mappings, 63 FTAs, 9-field 100% HS Code accuracy (GRI Pipeline), sanctions screening (21K+ entries). MCP server for Claude, Cursor, and any MCP-compatible AI.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
-
"potal-mcp-server": "
|
|
8
|
+
"potal-mcp-server": "build/index.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc && chmod 755 build/index.js",
|
|
12
12
|
"start": "node build/index.js",
|
|
13
13
|
"dev": "tsx src/index.ts"
|
|
14
14
|
},
|
|
15
|
-
"files": [
|
|
15
|
+
"files": [
|
|
16
|
+
"build"
|
|
17
|
+
],
|
|
16
18
|
"keywords": [
|
|
17
19
|
"mcp",
|
|
18
20
|
"claude",
|
|
@@ -33,7 +35,7 @@
|
|
|
33
35
|
"homepage": "https://potal.app",
|
|
34
36
|
"repository": {
|
|
35
37
|
"type": "git",
|
|
36
|
-
"url": "https://github.com/soulmaten7/potal"
|
|
38
|
+
"url": "git+https://github.com/soulmaten7/potal.git"
|
|
37
39
|
},
|
|
38
40
|
"author": "POTAL",
|
|
39
41
|
"license": "MIT",
|