@cyanheads/un-comtrade-mcp-server
Public Hosted Server: https://un-comtrade.caseyjhand.com/mcp
Overview
International merchandise and services trade statistics from UN Comtrade. Resolve country and HS commodity codes, fetch bilateral trade flows and services trade, and compute balances, partner/commodity rankings, and data availability.
Tools
Tool | Description |
| Resolve country and area names to Comtrade M49 numeric codes. |
| Find HS or SITC commodity codes in a selected edition by keyword, description, or code prefix. |
| Find service trade categories in Comtrade's combined EB catalog, tagged with their EBOPS schemes, by keyword or parent code. |
| Fetch bilateral trade flow records — value, quantity, and weight per period/commodity/partner. |
| Compute a country's trade balance (exports minus imports) across one or more periods. |
| Rank trading partners by trade value for a reporter, commodity, and flow direction. |
| Rank commodity categories by trade value for a reporter and flow direction. |
| Check which reporter/period/classification combinations have published data. |
| Fetch international trade-in-services data — EB as reported (EB10, EB10S, or EB02 codes), or UN Comtrade's converted EB10/EB02 series. |
Resources
Resource | Description |
| Complete country/area code list with M49 codes, ISO identifiers, and reporter validity. |
| Top-level HS commodity hierarchy at chapter, heading, or subheading level. |
| Workflow guide for historical classifications, coverage, bilateral/mirror analysis, and unit values. |
Country and HS reference data are also reachable through comtrade_lookup_countries and comtrade_search_commodities. The workflow guide ships with npm, Docker, MCPB, and plugins, and is available over MCP as a skill resource.
Related MCP server: ONS Trade Data API MCP Server
Capability reference
comtrade_lookup_countries tool
Accepts a partial or full country name, or an ISO alpha-2/alpha-3 code;
rolefilters to"reporter","partner"(only entries in UN's partner-area list), or"any"(default)validAsReporterflag on each match — most regional groupings (e.g. World) are partner-only, but some report (e.g. the European Union, 97)include_groups(defaulttrue) toggles regional/economic groupings in the resultsEach match also pulls in entries sharing its ISO-3 identity, ordered exact ISO match, reporter-valid, current before expired, name, then code; reporter and partner-only area codes remain distinct
entryEffectiveDateandentryExpiredDate(YYYY-MM-DD) give each entity's validity, so historical entities (e.g. Fed. Rep. of Germany, ...1990) sort after current onesCountry references load from UN static files at startup — no subscription key required. Rows that fail validation are dropped and logged; startup fails on an empty catalog or when fewer than half a file's rows are valid
comtrade_search_commodities tool
Free-text keyword, partial description, or code-prefix search;
classification(case-insensitive, defaultHS) selects the currentHSreference, an HS editionH0–H6, or SITCSS/S1–S4aggr_leveluses the selected catalog's native levels: HS chapters/headings/subheadings use2/4/6; SITC has levels1–5. Omit for all levels. The HS all-commoditiesTOTALis level 0, which noaggr_levelfilter returnslimit(default 50) is the page size;totalMatches,shown, andtruncateddescribe the page, andoffsetwith the returnednextOffsetcontinues itrecommendedQueryCodeon each result — the code to pass ascmd_codein trade queries, with the sameclassification
comtrade_list_service_categories tool
Lists the combined EB service categories, optionally filtered by keyword or
parent_code; numeric and alphabetic codes are distinct entries.parent_code: "200"lists the EB10 and EB02 top levelsschemeson each category names the EBOPS scheme catalogs that list its code (EB10,EB10S,EB02); it is empty when none does and absent, with a notice, when the scheme catalogs could not be loaded.ebParentcarries the parent the combined EB catalog gives a code when it differs fromparent(TOTALfor EB10's top-level codes, whoseparentis200)limit(default 100) is the page size;totalMatches,shown, andtruncateddescribe the page, andoffsetwith the returnednextOffsetcontinues itReturned
idis theservice_codeforcomtrade_get_services_trade
comtrade_get_trade_flows tool
Accepts a scalar
reporter_codeor an array; omission requires an explicitpartner_code. Reporter arrays serve the first 20 distinct codes and disclose the remainder inunservedReporters. Served codes of an array that return no rows are listed inreportersWithoutRecords(referenceStatus, optionalsuggestedReporterCode; omitted when the response is truncated). A scalar partner-only code with no rows gets a notice naming its reporter-valid counterpart (250 → 251)Requires
flow_code(Mimport /Xexport /RXre-export /RMre-import) andperiodvalues, allYYYYor allYYYYMM.freqis optional: it is inferred from the period length, mixed lengths are rejected, and an explicit value must match. Up to 120 periods are accepted; keyless calls serve the first 8 (12 with a subscription key). Periods past the cap, or whose request is rate limited or runs out of time, are listed inunservedPeriodswith a notice; if every period fails the call returnsrate_limitedordeadline_exceededpartner_code: 0returns the World total; omit it for a per-partner breakdown.classification(case-insensitive, defaultHS) supportsHS,H0–H6, and SITCS1–S4/SS. Explicit HS editions require authenticated data access; keylessHSreturns as-reported editions, identified by each row'sclassificationCodecmd_code[]accepts up to 200 codes and serves the first 20, listing the rest inunservedCmdCodes; omit it or pass["TOTAL"]for cross-commodity totals.truncated: trueplustruncationHintmeans the response hit the 500-record cap;isReportedseparates directly reported rows from UN-estimated onesQuantity and alternate quantity retain unit codes/abbreviations and estimation flags where available. UN Comtrade sends
0for what it does not report, soqty/altQtyof 0 with unit code-1, andnetWgt/grossWgt/cifvalue/fobvalueof 0 beside a positiveprimaryValue, are omitted: an absent field means not reported, not zero. A 0 in a real unit stays a measurement
comtrade_get_trade_balance tool
Returns
balanceUsd(signed),exportsUsd,importsUsd, andcoverageRatio(exports / imports) per period. Optionalcmd_code[]restricts the balance to HS or SITC commoditiespartner_codedefaults to World (0); a country code computes the reporter's bilateral balance and is echoed with the partner name.classificationis case-insensitive with defaultHS;freqis optional and inferred from the period length (mixed lengths are rejected; an explicit value must match)Accepts up to 120 periods and 200 codes. Keyless calls serve the first 4 periods (two requests each; 12 with a subscription key) and 20 codes, listing the rest in
unservedPeriods/unservedCmdCodes; a period is served only when both flows complete, and one whose export or import request is rate limited or runs out of time is listed too. Anoticewarns when either flow hit the 500-record cap and the balance may be incompleteA partner-only reporter code (0 World, 250 Metropolitan France) with no data fails with
partner_only_reporter, naming the reporter-valid counterpart when one exists (250 → 251)mirrorCaveaton every response: the balance reflects this reporter's own values, not the partner's mirror data
comtrade_get_top_partners tool
Takes one
reporter_code,flow_code(M/X), and singleperiod; optional singlecmd_code(omit for total merchandise trade) in the selectedclassification(case-insensitive, defaultHS;H0–H6with a subscription key, SITCS1–S4/SS)Infers annual or monthly frequency from the period; monthly values require a calendar month from
01through12Returns the top
limitpartners (default 10; any larger value is honored, with no return-all threshold), each withrank,primaryValueUsd, andsharePercent;totalPartnersandtotalValueUsdcover every partner retrieved, and shares divide bytotalValueUsd;classificationnames the catalog thecmd_codewas read intruncated: truewhen the fetch hit the 500-record cap. An empty result for a partner-only reporter code ends its notice with the reporter-valid counterpart (250 → 251)
comtrade_get_top_commodities tool
Takes one
reporter_code,flow_code, and singleperiod;aggr_levelis2(HS chapter, default) or4(heading).partner_codedefaults to World (0); set it to scope to one partnerInfers frequency from
period. Fetches matchingTOTALto identify the reporter's HS edition, then queries that edition's category codes in paced chunks, splitting capped chunksReturns the top
limitcategories (default 10; any larger value is honored, with no return-all threshold). Complete coverage permits ranks; shares usedenominatorValueUsdfromTOTAL, whiletotalValueUsdandtotalCommoditiescover every category retrievedcoverageComplete,coverageNotice, andmissingCodesdescribe retrieval; codes not reached within the call's time budget are listed inmissingCodes. Incomplete results retain available categories without ranks or shares;truncatedremains true. ATOTALwith no rows ends the ranking with an empty result andtruncated: false; for a partner-only reporter code its notice names the reporter-valid counterpart. Ranking can require several upstream requests
comtrade_get_data_availability tool
All filters optional —
reporter_code,period,freq(A/M; inferred fromperiod,Awhenperiodis omitted, and an explicit value must match it),type_code(Cgoods /Sservices, defaultC),classification(omitted or blank resolves toHSfor goods,EB10for services; upstream identifiers such asB4andB5are also accepted)Returns per-dataset
totalRecords,firstReleased, andlastReleasedwhere available; omit all filters to browse goods availabilitylimitdefaults to 50 andoffsetto 0. Results are sorted before slicing;totalDatasets,shown, andnextOffsetsupport stable page traversal.truncatedidentifies a partial page. Paging still fetches the matching upstream availability listAn empty result for a partner-only reporter code ends its notice with the reporter-valid counterpart (250 → 251)
Annual data typically publishes 3–12 months after the reference year — call this before querying recent periods
comtrade_get_services_trade tool
Takes one
reporter_code,flow_code(Mimports/debits,Xexports/credits), annualperiodvalues (YYYY), and optionalpartner_code(0for all partners combined). Up to 120 periods are accepted; keyless calls serve the first 8 (12 with a subscription key). Periods past the cap, or whose request is rate limited or runs out of time, are listed inunservedPeriodswith a noticeclassification(case-insensitive, defaultEB; blank meansEB) selects EB as reported, with each reporter-period in its own scheme (EB10, EB10S, or EB02 codes), or UN Comtrade's convertedEB10/EB02series.EB10Shas no converted series and is rejected; its rows are served only underEB. Each row'sclassificationCodeidentifies its classification andisOriginalClassificationsays whether it is as reported or convertedservice_codefilters to one category fromcomtrade_list_service_categories(3is transport;SAis manufacturing services), which lists each code'sschemes. Omit it to get every reported category at mixed levels, with totals (200for EB10 and EB02,Sfor EB10S) beside their components: don't sum the rowsAn empty result's notice names the code's scheme, routes to
comtrade_get_data_availabilitywithclassification: "EB", and suggests the converted series when one applies; for a partner-only reporter it names the reporter-valid counterparttruncated: trueplustruncationHintwhen the response hit the 500-record cap. Country and period coverage is more limited than for goods
comtrade://countries resource
Complete country/area list as
application/json— M49 code, ISO identifiers,validAsReporter,isGroup, andentryEffectiveDate/entryExpiredDate(YYYY-MM-DD) where upstream lists themLoaded from the same startup reference cache
comtrade_lookup_countriessearches
comtrade://hs-classification/{level} resource
levelpath param accepts2(chapters),4(headings), or6(subheadings); any other value throws a validation errorReturns each code's
description,parent, andisLeaf; full leaf enumeration is too large to inject — usecomtrade_search_commoditiesfor keyword search
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
Comtrade-specific:
Country codes, current HS hierarchy, and EB categories load from UN static files at startup; historical HS/SITC editions, quantity units, and the
EB10.json/EB10S.json/EB02.jsonscheme catalogs (category scheme tags and the services empty-result notice) load lazily and cache successful responses. Invalid rows in a reference file are dropped and logged; startup fails on an empty catalog or when fewer than half a file's rows are validAuthenticated (
data/v1/get) and public preview (public/v1/preview) endpoints — tools fall back to the preview endpoint when no subscription key is setParallel sub-request execution in workflow tools (
comtrade_get_trade_balanceruns export and import fetches concurrently)Retry with exponential backoff on transient failures, honoring an upstream
Retry-Afterheader when presentDescription enrichment uses the row's classification edition and preserves upstream labels. Optional reference failures retain trade values and codes with a notice
Agent-friendly output:
truncated: trueplus a recovery hint when a public preview response reaches 500 recordsisReportedflag on trade flow records distinguishes directly-reported values from UN-estimated/aggregated rowsvalidAsReporteron country lookups prevents constructing invalid queries with partner-only area codesmirrorCaveaton trade-balance output surfaces the methodological caveat without parsing error text
Getting started
Public Hosted Instance
A public instance is available at https://un-comtrade.caseyjhand.com/mcp — no installation required. It runs on the keyless public preview endpoint (500 records per upstream request), splitting served periods into paced requests that start at least 4 seconds apart. Each call serves 8 periods for flows and services and 4 for balance (two requests per period); periods past that, or that fail, come back in unservedPeriods. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"un-comtrade-mcp-server": {
"type": "streamable-http",
"url": "https://un-comtrade.caseyjhand.com/mcp"
}
}
}Self-Hosted / Local
Prerequisites: A UN Comtrade subscription key is optional. Without one, tools use the public preview endpoint (500 records per upstream request). A key selects authenticated access; retrieval and request limits depend on UN Comtrade and the subscription.
Add the following to your MCP client configuration file.
{
"mcpServers": {
"un-comtrade-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/un-comtrade-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"COMTRADE_SUBSCRIPTION_KEY": "your-key-here"
}
}
}
}Or with npx (no Bun required):
{
"mcpServers": {
"un-comtrade-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/un-comtrade-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"COMTRADE_SUBSCRIPTION_KEY": "your-key-here"
}
}
}
}Or with Docker:
{
"mcpServers": {
"un-comtrade-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"-e", "COMTRADE_SUBSCRIPTION_KEY=your-key-here",
"ghcr.io/cyanheads/un-comtrade-mcp-server:latest"
]
}
}
}For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 COMTRADE_SUBSCRIPTION_KEY=... bun run start:http
# Server listens at http://localhost:3010/mcpPrerequisites
Bun v1.4.0 or higher (or Node.js v24+). Development and Docker images use Bun 1.4.4.
Optional: a UN Comtrade subscription key for authenticated API access subject to subscription limits. Without one, data tools use the public preview endpoint (500 records per upstream request). Reference/lookup tools (
comtrade_lookup_countries,comtrade_search_commodities,comtrade_list_service_categories) never require a key — they query static UN reference files.
Installation
Clone the repository:
git clone https://github.com/cyanheads/un-comtrade-mcp-server.gitNavigate into the directory:
cd un-comtrade-mcp-serverInstall dependencies:
bun installConfigure environment:
cp .env.example .env
# edit .env and set COMTRADE_SUBSCRIPTION_KEY if you have oneConfiguration
All configuration is validated at startup via Zod schemas in src/config/server-config.ts.
Variable | Description | Default |
| Azure API Management subscription key from comtradedeveloper.un.org. Without it, tools use the public preview endpoint (500-record cap). | — |
| Override the Comtrade API base URL. |
|
| Transport: |
|
| HTTP server port. |
|
| HTTP endpoint path. |
|
| Public origin override for reverse-proxy deployments. | — |
| HTTP session mode: |
|
| Auth mode: |
|
| Log level ( |
|
| Directory for log files (Node.js only). |
|
| Log failed tool arguments and results, redacted by key name, capped at |
|
| Storage backend: |
|
| Enable OpenTelemetry instrumentation. |
|
| OTLP base URL for traces ( | — |
| Opt-in OTLP log endpoint, used as-is. | — |
See .env.example for the full list of optional overrides.
Running the server
Local development
Build and run:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:httpRun checks and tests:
bun run devcheck # Lint, format, typecheck, security bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
docker build -t un-comtrade-mcp-server .
docker run --rm -e COMTRADE_SUBSCRIPTION_KEY=your-key -p 3010:3010 un-comtrade-mcp-serverThe Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/un-comtrade-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
Directory | Purpose |
|
|
| Environment variable parsing and validation with Zod. |
| Tool definitions ( |
| Resource definitions ( |
|
|
|
|
|
|
| Published workflow guide for agents calling the server. |
| Unit and integration tests mirroring |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
Handlers throw, framework catches — no
try/catchin tool logicUse
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storageRegister new tools and resources in
src/index.tsWrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
Use the matching classification's reference cache for commodity descriptions; lazy reference failures must preserve useful trade records and propagate caller cancellation
Data license
Use UN Comtrade data in accordance with the UN Comtrade license agreement, including its redistribution terms.
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run testLicense
Apache-2.0 — see LICENSE for details.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Comtrade MCP — UN Comtrade API for international bilateral trade data
Trade Intel MCP — Compound tools that chain Comtrade, Census, Treasury,
Macroeconomic, trade, FX and commodity data for 50 countries over one MCP endpoint.
Census Trade MCP — US Census Bureau International Trade data
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceProvides access to UN Comtrade international bilateral trade data via an MCP server, enabling AI agents to query trade statistics through natural language.130 npmMIT
- FlicenseNot gradedqualityDmaintenanceMCP server that provides access to UK trade data from the ONS Trade Data API, enabling queries for trade statistics, metadata, and raw data.-
- AlicenseAqualityDmaintenanceAn MCP server for the U.S. Census Bureau's International Trade Data API, enabling queries of monthly export and import statistics by HS, NAICS, end-use, and other classifications, with tools for trade balance, top partners, and country code lookup.7Creative Commons Zero v1.0 Universal
- AlicenseAqualityBmaintenanceEnables querying Brazilian foreign trade data through MCP tools for trade flows, freight costs, code resolution, and dataset caveats, with known data defects corrected in responses.4MIT