Finnish Foreign Trade (Tulli) MCP
Pricing
from $1.00 / 1,000 search trade tables
Finnish Foreign Trade (Tulli) MCP
Search Finnish Customs (Tulli) foreign-trade tables, inspect their dimensions, and query clean flattened import/export data by commodity, country and period.
Pricing
from $1.00 / 1,000 search trade tables
Rating
0.0
(0)
Developer
Anton Aouat
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
2 days ago
Last modified
Categories
Share
An MCP server that gives an AI agent clean access to Finnish Customs (Tulli) foreign-trade statistics — imports and exports by commodity, country, and period. It decodes Tulli's "Uljas" json-stat trade cubes into flat, labelled rows an agent can read directly.
Data © Finnish Customs, Uljas statistical database, licensed CC BY 4.0.
Tools
| Tool | What it does |
|---|---|
search_trade_tables | List trade tables (CN goods, SITC, NACE industry, BEC), filter by keyword. Returns each table's ifile. |
get_trade_dimensions | A table's dimensions + classification granularities (CN2…CN8, Year/Quarter/Month, Country/Continents). Pass a classification to also get its value codes. |
get_trade_data | Query flattened import/export rows (json-stat decoded), by commodity × country × period × flow × indicator. |
Agents chain them: search table → dimensions (learn codes) → data.
Example
"Finland's monthly total imports over the last year?"
search_trade_tables({ query: "CN" })→ifileforULJAS_CN.get_trade_dimensions({ ifile, classification: "Flow" })→ Flow codes (1=imports, 2=exports), dimensions.- → 12 monthly import totals in EUR.get_trade_data({ ifile, selections: [ { classification: "Classification of Products CN2", code: "00 - 99" }, { classification: "Time period", anchor: "FIRST", count: 12 }, { classification: "Country", code: "AA" }, { classification: "Flow", code: "1" }, { classification: "Indicators", code: "V1" } ] })
Notes on this API (hard-won)
- The Uljas API responses are BOM-prefixed and the selection grammar needs
= * ;sent literally — Node'sfetch/undici mangles it (500s), so the client uses the rawhttpsmodule. All handled internally. - Selection grammar: each dimension takes a single
code, or a relative range viaanchor(FIRST/LAST) +count. ForTime period,FIRST= the most recent periods. - Current-year detail may be incomplete (product-level nulls); aggregates (
00 - 99, all countriesAA) are published first. Limit: 50,000 cells/query (guarded client-side).
Local use (stdio)
$npm install && npm run build
{ "mcpServers": { "tulli": { "command": "node", "args": ["/abs/path/servers/tulli-trade/dist/server.js"] } } }
No API key — Tulli/Uljas is open data.
Development
npm run typecheck # tsc --noEmitnpm test # vitest — unit tests vs recorded real fixtures (offline)npm run smoke # live: boots the server, real MCP handshake + real Uljas callsnpm run build # produces dist/
Windows: run tools directly (node ./node_modules/vitest/vitest.mjs run) or via PowerShell.
Design
src/tulli/client.ts— HTTP only (Stats/Dims/Class/Data via the rawhttpsmodule). Custom minimal URL encoder, BOM strip, throttle + cache, typedTulliApiError.src/tulli/normalize.ts— the moat: flatten json-stat v1 cubes → flat rows; tidy stats/dims.src/tulli/query.ts— build the Data selection grammar + guard the 50k-cell cap.src/tools.ts— the 3 MCP tools + Pay-Per-Event billing hook.src/server.ts(stdio) /src/standby.ts(Apify HTTP) — both call one factory.