customs-mcp-server
Customs operations as MCP tools — drop into any AI agent.
14 production-grade customs / trade capabilities (declarations, ship info,
tariff, dual-use screening, AI-powered declaration generation, ...)
exposed as standard Model Context Protocol
tools for Claude Desktop, Claude Code, Cursor, Windsurf, Trae and any
other MCP-compatible AI client.

English · 简体中文 · 日本語
✨ Features
- 14 MCP tools — every customs-skill capability exposed as a typed tool with zod-validated input
- Universal client support — Claude Desktop / Claude Code / Cursor / Windsurf / Trae / Codex / any MCP client
- Production auth —
/session/exchange → 5-min agentToken → action-code whitelist → tenant isolation
- Per-call identity override — server-level env default + optional
_identity field per tool call (multi-user / multi-tenant friendly)
- AI document maker — multipart upload, no-wait mode,
decId polling pattern
- Error translation — backend
NEED_BIND / NEED_TENANT / etc. rendered as actionable English guidance
- Tiered timeouts — 15 s reads / 180 s upload / 660 s dual-use AI query
- Zero runtime deps —
@modelcontextprotocol/sdk + zod + dotenv only; Node 18+ built-in fetch + FormData
- Dual transport — stdio (Claude Desktop) and Streamable HTTP (remote / self-hosted)
🚀 Quick Start
1. Set up the backend identity binding
Ask your customs system admin to insert one row into agent_identity_binding
mapping your chosen (platform, externalUserId) to an existing sys_user_id.
Full SQL in docs/identity-binding.md.
2. Configure your AI client
Pick a ready-made config from examples/:
Or use the generic snippet:
{
"mcpServers": {
"customs": {
"command": "npx",
"args": ["-y", "@dearmrzhang/customs-mcp-server", "--transport", "stdio"],
"env": {
"CUSTOMS_API_BASE_URL": "http://your-backend-host:port",
"CUSTOMS_ACCESS_KEY": "your-access-key",
"CUSTOMS_SECRET_KEY": "your-secret-key",
"CUSTOMS_DEFAULT_PLATFORM": "mcp",
"CUSTOMS_DEFAULT_EXTERNAL_USER_ID": "your-bound-username",
"CUSTOMS_DEFAULT_EXTERNAL_CORP_ID": "mcp-prod"
}
}
}
}
3. Restart your client and ask
"Use customs_query_tariff to look up HS code 8471300000."
That's it. The AI agent picks the tool, the MCP server signs the request,
exchanges a grant, and returns structured tariff data.
🏗 Architecture
┌────────────────────────────────┐
│ AI client (Claude / Cursor / │
│ Windsurf / Trae / ...) │
└───────────────┬────────────────┘
│ MCP protocol (stdio | HTTP)
┌───────────────▼────────────────┐
│ customs-mcp-server │
│ │
│ • 14 tool handlers │
│ • Identity resolver │
│ • Signer + GrantBroker │
│ (5-min agentToken cache) │
│ • Error translator │
└───────────────┬────────────────┘
│ HTTPS + signed headers
┌───────────────▼────────────────┐
│ Customs backend │
│ /open-api/agent/v1/... │
└─────────────────────────────────┘
📚 Documentation
🛠 Environment Variables
CUSTOMS_API_BASE_URL | ✅ | — | Customs backend root URL |
CUSTOMS_ACCESS_KEY | ✅ | — | Static signature access key |
CUSTOMS_SECRET_KEY | ✅ | — | Static signature secret key |
CUSTOMS_DEFAULT_PLATFORM | ✅ | — | Identity platform (e.g. mcp / cursor) |
CUSTOMS_DEFAULT_EXTERNAL_USER_ID | ✅ | — | Bound external user identifier |
CUSTOMS_DEFAULT_EXTERNAL_CORP_ID | ✅ | — | Bound external corp identifier |
CUSTOMS_API_PREFIX | | /open-api/agent | API path prefix |
CUSTOMS_TIMEOUT_MS | | 15000 | Default request timeout (ms) |
CUSTOMS_UPLOAD_TIMEOUT_MS | | 180000 | AI-maker upload timeout (ms) |
CUSTOMS_DUAL_USE_TIMEOUT_MS | | 660000 | Dual-use slow query timeout (ms) |
CUSTOMS_TIMESTAMP_TIMEZONE | | Asia/Shanghai | Timestamp tz (must match backend) |
CUSTOMS_DEFAULT_CHANNEL | | ${PLATFORM} | Channel field for /session/exchange |
MCP_HTTP_HOST | | 0.0.0.0 | HTTP transport bind host |
MCP_HTTP_PORT | | 8787 | HTTP transport port |
MCP_HTTP_PATH | | /mcp | HTTP transport endpoint |
CUSTOMS_DEBUG | | 0 | Set 1 for verbose stderr debug logs |
🧪 Available Tools
14 tools across 7 domains. Full schema and examples in tool-reference.md.
| Declaration | customs_get_declaration_status · customs_query_declaration_list · customs_get_declaration_detail · customs_get_import_export_status · customs_get_full_process_tracking |
| Ship | customs_query_ship_info (with auto I→E fallback) · customs_query_ship_plan |
| Manifest | customs_query_manifest_info · customs_query_ship_manifest_info |
| Tariff | customs_query_tariff |
| Compliance | customs_query_dual_use_item (slow AI query) |
| Orders | customs_create_order_draft (pre-check only) |
| AI Maker | customs_submit_ai_maker · customs_get_ai_maker_status |
⚠️ Upgrading from v0.1.x
v1.0.0 is a breaking release with mandatory new env vars. See
CHANGELOG.md → 1.0.0 for the full list.
TL;DR:
- Add three identity env vars (
CUSTOMS_DEFAULT_PLATFORM /
CUSTOMS_DEFAULT_EXTERNAL_USER_ID / CUSTOMS_DEFAULT_EXTERNAL_CORP_ID)
- Have your admin insert the matching
agent_identity_binding row
- Rename
customs_query_tariff_info → customs_query_tariff
- Switch
customs_get_declaration_detail callers from cusCiqNo to
entryId (or decId when known)
🛡 Security
CUSTOMS_SECRET_KEY and cached agentTokens never leave server memory
- Per-call
_identity overrides should not embed PII — they appear in MCP structured responses
- All write tools route through action-code whitelist on the backend
- Cross-tenant
ai-maker status access is blocked at the backend layer
🤝 Contributing
Issues and PRs welcome at github.com/yak33/customs-mcp-server.
git clone https://github.com/yak33/customs-mcp-server.git
cd customs-mcp-server
pnpm install
pnpm build
pnpm dev:stdio
📄 License
MIT © ZHANGCHAO. See LICENSE.
🙏 Related Projects
- customs-skill — the same 13 customs capabilities as an OpenClaw skill for Feishu/Lark integration
Built with 🦞 by ZHANGCHAO · v1.1.0 · 2026-05-27