@cyanheads/eurostat-mcp-server 0.10.0 → 0.11.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/AGENTS.md +17 -5
- package/CLAUDE.md +17 -5
- package/Dockerfile +9 -5
- package/README.md +63 -129
- package/changelog/0.10.x/0.10.1.md +27 -0
- package/changelog/0.11.x/0.11.0.md +33 -0
- package/dist/config/server-config.d.ts +1 -0
- package/dist/config/server-config.d.ts.map +1 -1
- package/dist/config/server-config.js +9 -4
- package/dist/config/server-config.js.map +1 -1
- package/dist/index.js +29 -16
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/tools/clamp-read-limit.d.ts +11 -0
- package/dist/mcp-server/tools/clamp-read-limit.d.ts.map +1 -0
- package/dist/mcp-server/tools/clamp-read-limit.js +14 -0
- package/dist/mcp-server/tools/clamp-read-limit.js.map +1 -0
- package/dist/mcp-server/tools/definitions/eurostat-browse-themes.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-browse-themes.tool.js +6 -5
- package/dist/mcp-server/tools/definitions/eurostat-browse-themes.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-query.tool.d.ts +3 -1
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-query.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-query.tool.js +49 -33
- package/dist/mcp-server/tools/definitions/eurostat-dataframe-query.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-download-dataset.tool.d.ts +100 -0
- package/dist/mcp-server/tools/definitions/eurostat-download-dataset.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-download-dataset.tool.js +24 -13
- package/dist/mcp-server/tools/definitions/eurostat-download-dataset.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-get-dimension-values.tool.d.ts +70 -1
- package/dist/mcp-server/tools/definitions/eurostat-get-dimension-values.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-get-dimension-values.tool.js +35 -54
- package/dist/mcp-server/tools/definitions/eurostat-get-dimension-values.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-query-dataset.tool.d.ts +103 -0
- package/dist/mcp-server/tools/definitions/eurostat-query-dataset.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-query-dataset.tool.js +43 -25
- package/dist/mcp-server/tools/definitions/eurostat-query-dataset.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-search-datasets.tool.d.ts +45 -0
- package/dist/mcp-server/tools/definitions/eurostat-search-datasets.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/eurostat-search-datasets.tool.js +12 -9
- package/dist/mcp-server/tools/definitions/eurostat-search-datasets.tool.js.map +1 -1
- package/dist/mcp-server/tools/value-list-paging.d.ts +1 -29
- package/dist/mcp-server/tools/value-list-paging.d.ts.map +1 -1
- package/dist/mcp-server/tools/value-list-paging.js +1 -47
- package/dist/mcp-server/tools/value-list-paging.js.map +1 -1
- package/dist/services/eurostat-bulk/eurostat-bulk-service.d.ts.map +1 -1
- package/dist/services/eurostat-bulk/eurostat-bulk-service.js +72 -24
- package/dist/services/eurostat-bulk/eurostat-bulk-service.js.map +1 -1
- package/dist/services/eurostat-bulk/types.d.ts +1 -1
- package/dist/services/eurostat-bulk/types.d.ts.map +1 -1
- package/dist/services/eurostat-catalogue/eurostat-catalogue-service.d.ts +1 -0
- package/dist/services/eurostat-catalogue/eurostat-catalogue-service.d.ts.map +1 -1
- package/dist/services/eurostat-catalogue/eurostat-catalogue-service.js +29 -9
- package/dist/services/eurostat-catalogue/eurostat-catalogue-service.js.map +1 -1
- package/dist/services/eurostat-data/eurostat-data-service.d.ts.map +1 -1
- package/dist/services/eurostat-data/eurostat-data-service.js +32 -14
- package/dist/services/eurostat-data/eurostat-data-service.js.map +1 -1
- package/dist/services/eurostat-data/types.d.ts +4 -2
- package/dist/services/eurostat-data/types.d.ts.map +1 -1
- package/dist/services/eurostat-data/types.js +2 -2
- package/dist/services/eurostat-data/types.js.map +1 -1
- package/package.json +11 -10
- package/server.json +31 -17
- package/skills/eurostat-guide/SKILL.md +60 -0
- package/skills/eurostat-guide/references/sql-analysis.md +40 -0
package/AGENTS.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Agent Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** @cyanheads/eurostat-mcp-server
|
|
4
|
-
**Version:** 0.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.
|
|
4
|
+
**Version:** 0.11.0
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.18`
|
|
6
6
|
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
|
-
**MCP SDK:** `@modelcontextprotocol/server` `^2.
|
|
8
|
-
**Zod:** `^4.6.
|
|
7
|
+
**MCP SDK:** `@modelcontextprotocol/server` `^2.4.0`
|
|
8
|
+
**Zod:** `^4.6.7`
|
|
9
9
|
|
|
10
10
|
> **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
|
|
11
11
|
|
|
@@ -266,7 +266,16 @@ src/
|
|
|
266
266
|
|
|
267
267
|
## Skills
|
|
268
268
|
|
|
269
|
-
|
|
269
|
+
Two skill trees live at the project root, written for different readers:
|
|
270
|
+
|
|
271
|
+
| Tree | For | Written by |
|
|
272
|
+
|:-----|:----|:-----------|
|
|
273
|
+
| `framework-skills/` | The agent developing this server — framework skills (`metadata.audience: external`) plus any development-time skills of the server's own | `init` and the `maintenance` sync (framework skills); you (your own) |
|
|
274
|
+
| `skills/` | The agents that install the server and call its tools — tool chains, identifier sources, gates | You, deliberately, with the `add-skill` skill. Absent by default; usually none is needed |
|
|
275
|
+
|
|
276
|
+
Development skills are modular instructions. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry. Their directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, so a server that ships `.claude-plugin/` or `.codex-plugin/` would hand them to every agent that installs it.
|
|
277
|
+
|
|
278
|
+
A published skill in root `skills/` reaches plugin installs and the `.mcpb` bundle as-is, ships in the npm package and the Docker image, and reaches MCP clients when the server opts in with `createApp({ skills: 'skills' })`. `lint:packaging` validates the tree.
|
|
270
279
|
|
|
271
280
|
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `framework-skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
272
281
|
|
|
@@ -280,6 +289,7 @@ Available skills:
|
|
|
280
289
|
| `add-app-tool` | Scaffold an MCP App tool + paired UI resource |
|
|
281
290
|
| `add-resource` | Scaffold a new resource definition |
|
|
282
291
|
| `add-prompt` | Scaffold a new prompt definition |
|
|
292
|
+
| `add-skill` | Author a skill the server publishes in root `skills/` for the agents that use it — when one earns its place, structure, validation, delivery |
|
|
283
293
|
| `add-service` | Scaffold a new service integration |
|
|
284
294
|
| `add-test` | Scaffold test file for a tool, resource, or service |
|
|
285
295
|
| `field-test` | Exercise tools/resources/prompts with real inputs, verify behavior, report issues |
|
|
@@ -374,6 +384,7 @@ Run the `release-and-publish` skill after git wrapup — it runs the verificatio
|
|
|
374
384
|
bun publish --access public
|
|
375
385
|
|
|
376
386
|
docker buildx build --platform linux/amd64,linux/arm64 \
|
|
387
|
+
--build-arg APP_VERSION=<version> \
|
|
377
388
|
-t ghcr.io/cyanheads/eurostat-mcp-server:<version> \
|
|
378
389
|
-t ghcr.io/cyanheads/eurostat-mcp-server:latest \
|
|
379
390
|
--push .
|
|
@@ -410,6 +421,7 @@ import { getMyService } from '@/services/my-domain/my-service.js';
|
|
|
410
421
|
- [ ] If wrapping external API: tests include at least one sparse payload case with omitted upstream fields
|
|
411
422
|
- [ ] Registered in `createApp()` arrays (directly or via barrel exports)
|
|
412
423
|
- [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
|
|
424
|
+
- [ ] `plugin.json` + `mcp.json` (Agent Plugins 1.0.0) populated — unscoped server name, package metadata, and one stdio server running `npx -y <full package name>`. Root `mcp.json` carries only fixed env values; user-supplied variables remain in `.codex-plugin/mcp.json` `env_vars`, forwarded through `.codex-plugin/plugin.json` `"mcpServers": "./.codex-plugin/mcp.json"`.
|
|
413
425
|
- [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` = the unscoped repo name (never the npm scope — `lint:packaging` enforces this); `interface.shortDescription` from `package.json` description
|
|
414
426
|
- [ ] `.codex-plugin/mcp.json` updated — server name key is the unscoped repo name; every user-supplied variable (API key, contact email, instance URL) is listed in `env_vars` so Codex forwards it from the user's environment. Never write `"KEY": ""` into `env` — an empty value replaces the user's exported key and is read as unset
|
|
415
427
|
- [ ] `.claude-plugin/plugin.json` populated — `name`, `version`, `description`, `author`, `repository`, `license`, `keywords` from `package.json`; inline `mcpServers` entry keyed by the unscoped repo name. Every user-supplied variable is declared under `userConfig` (`type`, `title`, `description`; `sensitive: true` for keys and tokens; `required: true` or `default: ""`) and referenced from `env` as `"KEY": "${user_config.<option>}"` — mirror the `user_config` block in `manifest.json`. Never write `"KEY": ""` into `env`
|
package/CLAUDE.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Agent Protocol
|
|
2
2
|
|
|
3
3
|
**Server:** @cyanheads/eurostat-mcp-server
|
|
4
|
-
**Version:** 0.
|
|
5
|
-
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.
|
|
4
|
+
**Version:** 0.11.0
|
|
5
|
+
**Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.18`
|
|
6
6
|
**Engines:** Bun ≥1.4.0, Node ≥24.0.0
|
|
7
|
-
**MCP SDK:** `@modelcontextprotocol/server` `^2.
|
|
8
|
-
**Zod:** `^4.6.
|
|
7
|
+
**MCP SDK:** `@modelcontextprotocol/server` `^2.4.0`
|
|
8
|
+
**Zod:** `^4.6.7`
|
|
9
9
|
|
|
10
10
|
> **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
|
|
11
11
|
|
|
@@ -266,7 +266,16 @@ src/
|
|
|
266
266
|
|
|
267
267
|
## Skills
|
|
268
268
|
|
|
269
|
-
|
|
269
|
+
Two skill trees live at the project root, written for different readers:
|
|
270
|
+
|
|
271
|
+
| Tree | For | Written by |
|
|
272
|
+
|:-----|:----|:-----------|
|
|
273
|
+
| `framework-skills/` | The agent developing this server — framework skills (`metadata.audience: external`) plus any development-time skills of the server's own | `init` and the `maintenance` sync (framework skills); you (your own) |
|
|
274
|
+
| `skills/` | The agents that install the server and call its tools — tool chains, identifier sources, gates | You, deliberately, with the `add-skill` skill. Absent by default; usually none is needed |
|
|
275
|
+
|
|
276
|
+
Development skills are modular instructions. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry. Their directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, so a server that ships `.claude-plugin/` or `.codex-plugin/` would hand them to every agent that installs it.
|
|
277
|
+
|
|
278
|
+
A published skill in root `skills/` reaches plugin installs and the `.mcpb` bundle as-is, ships in the npm package and the Docker image, and reaches MCP clients when the server opts in with `createApp({ skills: 'skills' })`. `lint:packaging` validates the tree.
|
|
270
279
|
|
|
271
280
|
**Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `framework-skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.
|
|
272
281
|
|
|
@@ -280,6 +289,7 @@ Available skills:
|
|
|
280
289
|
| `add-app-tool` | Scaffold an MCP App tool + paired UI resource |
|
|
281
290
|
| `add-resource` | Scaffold a new resource definition |
|
|
282
291
|
| `add-prompt` | Scaffold a new prompt definition |
|
|
292
|
+
| `add-skill` | Author a skill the server publishes in root `skills/` for the agents that use it — when one earns its place, structure, validation, delivery |
|
|
283
293
|
| `add-service` | Scaffold a new service integration |
|
|
284
294
|
| `add-test` | Scaffold test file for a tool, resource, or service |
|
|
285
295
|
| `field-test` | Exercise tools/resources/prompts with real inputs, verify behavior, report issues |
|
|
@@ -374,6 +384,7 @@ Run the `release-and-publish` skill after git wrapup — it runs the verificatio
|
|
|
374
384
|
bun publish --access public
|
|
375
385
|
|
|
376
386
|
docker buildx build --platform linux/amd64,linux/arm64 \
|
|
387
|
+
--build-arg APP_VERSION=<version> \
|
|
377
388
|
-t ghcr.io/cyanheads/eurostat-mcp-server:<version> \
|
|
378
389
|
-t ghcr.io/cyanheads/eurostat-mcp-server:latest \
|
|
379
390
|
--push .
|
|
@@ -410,6 +421,7 @@ import { getMyService } from '@/services/my-domain/my-service.js';
|
|
|
410
421
|
- [ ] If wrapping external API: tests include at least one sparse payload case with omitted upstream fields
|
|
411
422
|
- [ ] Registered in `createApp()` arrays (directly or via barrel exports)
|
|
412
423
|
- [ ] Tests use `createMockContext()` from `@cyanheads/mcp-ts-core/testing`
|
|
424
|
+
- [ ] `plugin.json` + `mcp.json` (Agent Plugins 1.0.0) populated — unscoped server name, package metadata, and one stdio server running `npx -y <full package name>`. Root `mcp.json` carries only fixed env values; user-supplied variables remain in `.codex-plugin/mcp.json` `env_vars`, forwarded through `.codex-plugin/plugin.json` `"mcpServers": "./.codex-plugin/mcp.json"`.
|
|
413
425
|
- [ ] `.codex-plugin/plugin.json` populated — `name`, `version`, `description`, `repository`, `license` from `package.json`; `interface.displayName` = the unscoped repo name (never the npm scope — `lint:packaging` enforces this); `interface.shortDescription` from `package.json` description
|
|
414
426
|
- [ ] `.codex-plugin/mcp.json` updated — server name key is the unscoped repo name; every user-supplied variable (API key, contact email, instance URL) is listed in `env_vars` so Codex forwards it from the user's environment. Never write `"KEY": ""` into `env` — an empty value replaces the user's exported key and is read as unset
|
|
415
427
|
- [ ] `.claude-plugin/plugin.json` populated — `name`, `version`, `description`, `author`, `repository`, `license`, `keywords` from `package.json`; inline `mcpServers` entry keyed by the unscoped repo name. Every user-supplied variable is declared under `userConfig` (`type`, `title`, `description`; `sensitive: true` for keys and tokens; `required: true` or `default: ""`) and referenced from `env` as `"KEY": "${user_config.<option>}"` — mirror the `user_config` block in `manifest.json`. Never write `"KEY": ""` into `env`
|
package/Dockerfile
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# output. A stage that compiles a native addon needs the target-arch toolchain
|
|
15
15
|
# and cannot cross-compile this way — drop the flag there.
|
|
16
16
|
# ==============================================================================
|
|
17
|
-
FROM --platform=$BUILDPLATFORM oven/bun:1.4.
|
|
17
|
+
FROM --platform=$BUILDPLATFORM oven/bun:1.4.4 AS build
|
|
18
18
|
|
|
19
19
|
WORKDIR /usr/src/app
|
|
20
20
|
|
|
@@ -29,6 +29,9 @@ RUN --mount=type=cache,target=/root/.bun/install/cache \
|
|
|
29
29
|
# Copy the rest of the source code
|
|
30
30
|
COPY . .
|
|
31
31
|
|
|
32
|
+
# Published agent skills are read from the application root at runtime.
|
|
33
|
+
RUN mkdir -p skills
|
|
34
|
+
|
|
32
35
|
# Build the application
|
|
33
36
|
RUN bun run build
|
|
34
37
|
|
|
@@ -46,7 +49,7 @@ RUN bun run build
|
|
|
46
49
|
# A clean image rather than `FROM build`: the build stage's node_modules holds
|
|
47
50
|
# devDependencies.
|
|
48
51
|
# ==============================================================================
|
|
49
|
-
FROM --platform=$BUILDPLATFORM oven/bun:1.4.
|
|
52
|
+
FROM --platform=$BUILDPLATFORM oven/bun:1.4.4 AS deps
|
|
50
53
|
|
|
51
54
|
WORKDIR /usr/src/app
|
|
52
55
|
|
|
@@ -80,8 +83,8 @@ RUN case "$TARGETARCH" in \
|
|
|
80
83
|
# The @duckdb/node-api binding that backs DataCanvas is one of this server's own
|
|
81
84
|
# dependencies, so it survives the flag, and `--cpu` picks the target's binding:
|
|
82
85
|
# the canvas is gated at runtime by CANVAS_PROVIDER_TYPE, not at build time, so
|
|
83
|
-
# the image always carries the binding
|
|
84
|
-
#
|
|
86
|
+
# the image always carries the binding. The canvas is on by default; a deployment
|
|
87
|
+
# turns it off with CANVAS_PROVIDER_TYPE=none.
|
|
85
88
|
RUN --mount=type=cache,target=/root/.bun/install/cache \
|
|
86
89
|
bun install --production --omit=peer --frozen-lockfile --ignore-scripts \
|
|
87
90
|
--os="$TARGETOS" --cpu="$(cat .bun-cpu)"
|
|
@@ -118,7 +121,7 @@ RUN rm -rf node_modules/@socketsecurity/bun-security-scanner
|
|
|
118
121
|
# dependencies and build artifacts. Its only Bun invocations are HEALTHCHECK
|
|
119
122
|
# and CMD, which run on the real target at container start.
|
|
120
123
|
# ==============================================================================
|
|
121
|
-
FROM oven/bun:1.4.
|
|
124
|
+
FROM oven/bun:1.4.4-slim AS production
|
|
122
125
|
|
|
123
126
|
WORKDIR /usr/src/app
|
|
124
127
|
|
|
@@ -140,6 +143,7 @@ COPY --from=deps /usr/src/app/node_modules ./node_modules
|
|
|
140
143
|
|
|
141
144
|
# Copy the compiled application code from the build stage
|
|
142
145
|
COPY --from=build /usr/src/app/dist ./dist
|
|
146
|
+
COPY --from=build /usr/src/app/skills ./skills
|
|
143
147
|
|
|
144
148
|
# The 'oven/bun' image already provides a non-root user named 'bun'.
|
|
145
149
|
# We will use this existing user for enhanced security.
|
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<h1>@cyanheads/eurostat-mcp-server</h1>
|
|
3
3
|
<p><b>Search and query the Eurostat catalogue — EU economy, demography, trade, health, and NUTS regional data via MCP. STDIO or Streamable HTTP.</b>
|
|
4
|
-
<div>6 Tools (up to 9 with the dataframe canvas) • 1
|
|
4
|
+
<div>6 Tools (up to 9 with the dataframe canvas) • 2 Resources • 1 Skill</div>
|
|
5
5
|
</p>
|
|
6
6
|
</div>
|
|
7
7
|
|
|
8
8
|
<div align="center">
|
|
9
9
|
|
|
10
|
-
[](./CHANGELOG.md) [](./LICENSE) [](https://github.com/users/cyanheads/packages/container/package/eurostat-mcp-server) [](https://modelcontextprotocol.io/) [](https://www.npmjs.com/package/@cyanheads/eurostat-mcp-server) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
@@ -29,13 +29,11 @@
|
|
|
29
29
|
|
|
30
30
|
## Overview
|
|
31
31
|
|
|
32
|
-
EU statistics from
|
|
33
|
-
|
|
34
|
-
The catalogue spans two Eurostat hosts. Most datasets come from the dissemination API; the `DS-*` collections — detailed trade by CN8, HS, SITC, BEC and CPA (Comext), and PRODCOM industrial production — come from the Comext dissemination host. Every tool routes a `DS-*` code there by its prefix, in any case, so the same calls work on both.
|
|
32
|
+
EU statistics from Eurostat: economy, demography, trade, health, and NUTS regional data, including detailed Comext trade and PRODCOM industrial production. Search or browse the catalogue, inspect a dataset's dimensions, then query a slice or bulk-download the whole thing.
|
|
35
33
|
|
|
36
34
|
### Tools
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
The three `eurostat_dataframe_*` tools need the dataframe canvas, which is on by default and off with `CANVAS_PROVIDER_TYPE=none`; `eurostat_dataframe_drop` also needs `EUROSTAT_DATAFRAME_DROP_ENABLED=true`.
|
|
39
37
|
|
|
40
38
|
| Tool | Description |
|
|
41
39
|
|:---|:---|
|
|
@@ -54,71 +52,77 @@ Three of the nine are listed only when the dataframe canvas is enabled (`CANVAS_
|
|
|
54
52
|
| Resource | Description |
|
|
55
53
|
|:---|:---|
|
|
56
54
|
| `eurostat://dataset/{dataset_code}` | Dataset metadata (dimensions, time range, observation count, last-updated) by URI |
|
|
55
|
+
| `skill://eurostat-guide/SKILL.md` | Statistical comparison gates, identifier provenance, and SQL analysis workflows |
|
|
57
56
|
|
|
58
57
|
The same metadata is available through `eurostat_get_dataset_info` for tool-only clients.
|
|
59
58
|
|
|
59
|
+
### Agent skill
|
|
60
|
+
|
|
61
|
+
[eurostat-guide](skills/eurostat-guide/SKILL.md) covers country and regional comparisons, Comext aggregation, missing-data interpretation, and SQL joins over staged observations. It ships with plugin, npm, Docker, and MCPB installs and is served over MCP at `skill://eurostat-guide/SKILL.md`; supporting SQL examples are included under the skill's `references/` directory.
|
|
62
|
+
|
|
60
63
|
## Capability reference
|
|
61
64
|
|
|
62
65
|
### `eurostat_search_datasets` <sub>tool</sub>
|
|
63
66
|
|
|
64
|
-
- `query`
|
|
65
|
-
- Results carry `code`, `label`, `type` (`dataset` / `table`), period coverage, `obsCount`, `lastUpdated`, and `themePath`, with `totalMatches` across every page; fails as `no_match` (naming
|
|
66
|
-
-
|
|
67
|
+
- `query` words are ANDed case-insensitively across each dataset's label, theme breadcrumb, and code, ignoring spacing beside punctuation (`Rev.2` finds `Rev. 2`); `limit` is a positive integer (default 20), clamped to 100 with a notice. Page by passing `nextCursor` back as `cursor`; cursor calls ignore `limit`
|
|
68
|
+
- Results carry `code`, `label`, `type` (`dataset` / `table`), period coverage, `obsCount`, `lastUpdated`, and `themePath`, with `totalMatches` across every page; fails as `no_match` (naming the words whose removal finds matches), `invalid_cursor`, or a retryable `upstream_fault` when no catalogue is loaded and Eurostat's table of contents is unreachable
|
|
69
|
+
- If the Comext dataflow list cannot be loaded, results carry `comextListMissing` and a `notice`; a catalogue that could not be refreshed carries `catalogueLoadedAt`
|
|
67
70
|
|
|
68
71
|
---
|
|
69
72
|
|
|
70
73
|
### `eurostat_browse_themes` <sub>tool</sub>
|
|
71
74
|
|
|
72
|
-
- Omit `theme_code` for the top-level theme folders; pass a folder code for its immediate children. The Comext collections sit under `ext_go_detail` (detailed trade, in `ext_go`) and `prom` (PRODCOM, in `icts`)
|
|
73
|
-
- Items carry `code`, `label`, `type` (`folder` / `dataset` / `table`), and `hasChildren`, with a `parentPath` breadcrumb and `otherPlacements` for a folder
|
|
74
|
-
-
|
|
75
|
+
- Omit `theme_code` for the top-level theme folders; pass a folder code for its immediate children. Codes resolve exact-first, then case-insensitively. The Comext collections sit under `ext_go_detail` (detailed trade, in `ext_go`) and `prom` (PRODCOM, in `icts`)
|
|
76
|
+
- Items carry `code`, `label`, `type` (`folder` / `dataset` / `table`), and `hasChildren`, with a `parentPath` breadcrumb and `otherPlacements` for a folder filed in several branches; fails as `not_found` for an unknown code, `not_a_folder` for a dataset or table code, or a retryable `upstream_fault` when no catalogue is loaded and Eurostat's table of contents is unreachable
|
|
77
|
+
- If the Comext dataflow list cannot be loaded, `ext_go` and `icts` carry `comextListMissing` and a `notice`; a catalogue that could not be refreshed carries `catalogueLoadedAt`
|
|
75
78
|
|
|
76
79
|
---
|
|
77
80
|
|
|
78
81
|
### `eurostat_get_dataset_info` <sub>tool</sub>
|
|
79
82
|
|
|
80
83
|
- One `dataset_code`; returns every dimension with `valuesCount` and up to 10 `sampleValues`, plus `timeRange`, `obsCount`, `lastUpdated`, and the ESMS `metadataUrl`
|
|
81
|
-
- Fails as `not_found` for an unknown dataset, or `upstream_fault` when Eurostat returns a structure
|
|
84
|
+
- Fails as `not_found` for an unknown dataset, or `upstream_fault` when Eurostat returns a structure the server cannot read (retryable when the endpoint is down)
|
|
82
85
|
|
|
83
86
|
---
|
|
84
87
|
|
|
85
88
|
### `eurostat_get_dimension_values` <sub>tool</sub>
|
|
86
89
|
|
|
87
|
-
- `dataset_code` plus `dimension
|
|
88
|
-
- `
|
|
89
|
-
-
|
|
90
|
-
- Fails as `not_found`, `no_results`, `conflicting_params`, `invalid_cursor`, `upstream_fault` (a structure or content constraint the server cannot read, or, marked retryable, a structure endpoint that answers HTTP 5xx or cannot be reached on every retry), or `canvas_not_found`
|
|
91
|
-
- `canvas_id` stages every value, whatever `filter` and `limit` are, on that canvas as a `code` / `label` table (`canvasId` / `tableName` / `stagedRowCount`) that labels a download's code columns through a join; this tool never starts a canvas of its own
|
|
90
|
+
- `dataset_code` plus `dimension`, resolved exact-first then case-insensitively (`TIME_PERIOD` aliases `time`); returned value codes and labels stay unchanged. For `geo`, `geo_level` picks a NUTS level (`aggregate`, `country` by default, `nuts1`, `nuts2`, `nuts3`). `filter` keeps values whose code and label contain every word given, ignoring case and accents (`palma frankfurt` finds `DE_EDDF_ES_LEPA`), counted in `matchCount`
|
|
91
|
+
- `limit` defaults to 25 and clamps to 1,000 with a notice. Pages contain that many values, fewer at the end, with `totalCount` for the full list; a page with more after it carries `truncated`, `nextCursor` (pass back as `cursor`), and a `notice`. Continuations retain the effective count and ignore a new `limit`; older cursors above 1,000 normalize to 1,000 with disclosure. Fails as `not_found`, `no_results`, `conflicting_params`, `invalid_cursor`, `upstream_fault` (retryable when the endpoint is down), or `canvas_not_found`
|
|
92
|
+
- `canvas_id` stages every value on that canvas as a `code` / `label` table (`canvasId` / `tableName` / `stagedRowCount`) that labels a download's code columns through a join; it never starts a canvas of its own
|
|
92
93
|
|
|
93
94
|
---
|
|
94
95
|
|
|
95
96
|
### `eurostat_query_dataset` <sub>tool</sub>
|
|
96
97
|
|
|
97
|
-
- `dataset_code` plus `filters` (`{dimension_code: [values]}`), `geo_level`, and either `since_period` / `until_period` (`2020`, `2020-Q1`, `2020-01`, down to `YYYY-Wnn` and `YYYY-Dnnn`) or `last_n_periods`; `preview_limit`
|
|
98
|
+
- `dataset_code` plus `filters` (`{dimension_code: [values]}`), `geo_level`, and either `since_period` / `until_period` (`2020`, `2020-Q1`, `2020-01`, down to `YYYY-Wnn` and `YYYY-Dnnn`) or `last_n_periods`; `preview_limit` is a positive integer (default 25), clamped to 500 with a notice; `lang` is `EN`, `FR`, or `DE`
|
|
99
|
+
- `stage: true` stages the whole match on an available canvas, regardless of size; reuse `canvas_id` or omit it to create one. Without it, automatic staging starts above 5,000 observations. `truncated` still describes that threshold; `tableName` identifies a staged result. Canvas-disabled deployments disclose an explicit staging request as unavailable
|
|
100
|
+
- `last_n_periods` counts dataset time positions, which can include periods with no populated cells. A notice names gaps across the full match (up to the newest 24, with an omitted count); increase N or replace it with `until_period` to reach earlier observations
|
|
98
101
|
- Each observation carries code/label pairs per dimension, a nullable `value`, `status` (OBS_FLAG), and `confStatus` (CONF_STATUS); `obsCount`, `missingObsCount`, and `timeRange` cover the whole match, `truncated` is true above 5,000 observations, and `unmatchedValues` names filter values that matched nothing
|
|
99
|
-
- Fails as `not_found`, `no_results` (naming the unmatched values and the periods
|
|
102
|
+
- Fails as `not_found`, `no_results` (naming the unmatched values and the periods with no value), `invalid_dimension`, `invalid_period`, `conflicting_params`, a non-retryable `async_response` for a query too large to serve, `canvas_not_found`, or a retryable `upstream_fault` when the Statistics API is down
|
|
100
103
|
|
|
101
104
|
---
|
|
102
105
|
|
|
103
106
|
### `eurostat_download_dataset` <sub>tool</sub>
|
|
104
107
|
|
|
105
|
-
- `dataset_code` plus the same `filters` map and `since_period` / `until_period` (no `last_n_periods`: the TSV keeps a column per period unless a range drops it); `preview_limit`
|
|
106
|
-
- Returns `rowCount` (echoed as `totalCount`), `missingCount`, `periodRange`, and `bytesRead` for the whole download; fails as `not_found`, `invalid_dimension`, `invalid_period`, `filter_arity`, a non-retryable `async_queued` or `extraction_too_big`, `no_results`, `upstream_fault` (
|
|
107
|
-
- The `EUROSTAT_BULK_MAX_BYTES` budget (default 50 MiB)
|
|
108
|
+
- `dataset_code` plus the same `filters` map and `since_period` / `until_period` (no `last_n_periods`: the TSV keeps a column per period unless a range drops it); `preview_limit` is a positive integer (default 25), clamped to 500 with a notice
|
|
109
|
+
- Returns `rowCount` (echoed as `totalCount`), `missingCount`, `periodRange`, and `bytesRead` for the whole download; fails as `not_found`, `invalid_dimension`, `invalid_period`, `filter_arity`, a non-retryable `async_queued` or `extraction_too_big`, `no_results`, `upstream_fault` (retryable when the SDMX endpoint is down), or `canvas_not_found`
|
|
110
|
+
- The `EUROSTAT_BULK_MAX_BYTES` budget (default 50 MiB decoded) retains complete observations within the permitted prefix and returns `budgetExceeded: true`. A budget too small for a complete header and usable observation fails as `byte_budget_exceeded`, with the configured limit and narrowing guidance. `bytesRead` counts accepted decoded bytes, never more than the budget; incomplete trailing rows are discarded.
|
|
108
111
|
|
|
109
112
|
---
|
|
110
113
|
|
|
111
114
|
### `eurostat_dataframe_describe` <sub>tool</sub>
|
|
112
115
|
|
|
113
116
|
- `canvas_id` from a staging response; lists each table's `name`, `rowCount`, typed columns, and `expiresAt` (sliding `CANVAS_TTL_MS`, default 24h); fails as `canvas_not_found` or `canvas_disabled`
|
|
114
|
-
- Columns
|
|
117
|
+
- Columns depend on the staging tool: `eurostat_query_dataset` writes a code and a `_label` column per dimension, `eurostat_download_dataset` codes only plus `time` (both share the `obs_*` and `conf_status*` measures), and `eurostat_get_dimension_values` writes `code` and `label`
|
|
115
118
|
|
|
116
119
|
---
|
|
117
120
|
|
|
118
121
|
### `eurostat_dataframe_query` <sub>tool</sub>
|
|
119
122
|
|
|
120
123
|
- One read-only `SELECT` per call; chained statements, other verbs, and functions that read files or external data are rejected
|
|
121
|
-
- Returns `columns`, `rows`, `rowCount`, and `truncated
|
|
124
|
+
- Returns `columns`, `rows`, `rowCount`, and required boolean `truncated`. Every row supplied by the canvas is returned intact, up to `CANVAS_DEFAULT_ROW_LIMIT` (default 10,000). Cells render as lossless JSON, including nesting and quoted strings; 64-bit integers arrive as strings. Fails as `missing_table`, `canvas_not_found`, or `canvas_disabled`
|
|
125
|
+
- A cut sets `truncated: true` and adds a notice. Page an unchanged deterministic SELECT with a total `ORDER BY` (tie-breakers required), `LIMIT` and `OFFSET`; advance by actual `rowCount`. Restart an initially unordered result sorted at offset zero. Volatile SQL and changing or expired tables cannot provide stable paging
|
|
122
126
|
|
|
123
127
|
---
|
|
124
128
|
|
|
@@ -141,125 +145,46 @@ Built on [`@cyanheads/mcp-ts-core`](https://github.com/cyanheads/mcp-ts-core): s
|
|
|
141
145
|
Eurostat-specific:
|
|
142
146
|
|
|
143
147
|
- The Statistics API (JSON-stat 2.0) for slices, the SDMX 2.1 TSV bulk endpoint for whole datasets at roughly half the bytes, and the catalogue TOC cached in memory for 12 hours
|
|
144
|
-
- The Comext host's `DS-*` collections
|
|
145
|
-
- NUTS geo-level filtering (`aggregate` / `country` / `nuts1` / `nuts2` / `nuts3`)
|
|
146
|
-
-
|
|
147
|
-
- A
|
|
148
|
-
- Inline rows are capped by `preview_limit` (at most 500). With `CANVAS_PROVIDER_TYPE=duckdb`, a query match above 5,000 observations, or any bulk download, is staged whole as a SQL table (`canvasId` / `tableName` / `stagedRowCount`) for the dataframe tools, and `canvas_id` stages the next result beside earlier ones for joins — including a dimension's code/label list from `eurostat_get_dimension_values`, which labels a bulk table's codes. An empty download fails as `no_results` without creating or touching a canvas. The `.mcpb` bundle strips the native DuckDB binding, so SQL analytics need the npm, Docker, or from-source install
|
|
148
|
+
- The Comext host's `DS-*` collections (detailed trade by CN8, HS, SITC, BEC and CPA, and PRODCOM) through the same tools. Filter every request, `freq` included, since trade flows mix annual and monthly series; aggregates such as `TOTAL`, `EU27_2020`, and `EXT_EU27_2020` double-count when summed with their members. PRODCOM text values arrive in `valueText` / `obs_value_text`
|
|
149
|
+
- NUTS geo-level filtering (`aggregate` / `country` / `nuts1` / `nuts2` / `nuts3`), and OBS_FLAG and CONF_STATUS decoded into separate fields
|
|
150
|
+
- Too-large-to-serve responses come back as non-retryable `async_response`, `extraction_too_big`, or `async_queued` errors that name the dimensions left to filter
|
|
151
|
+
- A query with `stage: true`, a match above 5,000 observations, or any bulk download is staged as a SQL table (`canvasId` / `tableName` / `stagedRowCount`) on the DuckDB dataframe canvas, which is on by default. The `.mcpb` bundle strips the native DuckDB binding and runs with `CANVAS_PROVIDER_TYPE=none`, so SQL analytics need the npm, Docker, or from-source install
|
|
149
152
|
|
|
150
153
|
Agent-friendly output:
|
|
151
154
|
|
|
152
155
|
- Typed error contracts: every declared failure carries a `reason` and a `recovery.hint` naming the next tool to call
|
|
153
156
|
- Next-step hints: `eurostat_search_datasets` and `eurostat_browse_themes` return a `nextStep` pointing at the follow-up call
|
|
154
157
|
- Unknown stays unknown: counts and period bounds Eurostat doesn't report (`obsCount`, `timeRange.start` / `end`, `lastUpdated`) are omitted rather than zeroed
|
|
155
|
-
- Effective-query echo: `appliedFilters` on `eurostat_query_dataset` and `appliedQuery` (with the SDMX `url`) on `eurostat_download_dataset`, plus a `notice` when the
|
|
158
|
+
- Effective-query echo: `appliedFilters` on `eurostat_query_dataset` and `appliedQuery` (with the SDMX `url`) on `eurostat_download_dataset`, plus a `notice` when the preview omits rows
|
|
156
159
|
|
|
157
160
|
## Getting started
|
|
158
161
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
A public instance is available at `https://eurostat.caseyjhand.com/mcp` — no installation required. Point any MCP client at it via Streamable HTTP:
|
|
162
|
+
Connect to the hosted instance:
|
|
162
163
|
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
"mcpServers": {
|
|
166
|
-
"eurostat-mcp-server": {
|
|
167
|
-
"type": "streamable-http",
|
|
168
|
-
"url": "https://eurostat.caseyjhand.com/mcp"
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
164
|
+
```sh
|
|
165
|
+
claude mcp add --transport http eurostat-mcp-server https://eurostat.caseyjhand.com/mcp
|
|
172
166
|
```
|
|
173
167
|
|
|
174
|
-
|
|
168
|
+
Or run it locally:
|
|
175
169
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
```json
|
|
179
|
-
{
|
|
180
|
-
"mcpServers": {
|
|
181
|
-
"eurostat-mcp-server": {
|
|
182
|
-
"type": "stdio",
|
|
183
|
-
"command": "bunx",
|
|
184
|
-
"args": ["@cyanheads/eurostat-mcp-server@latest"],
|
|
185
|
-
"env": {
|
|
186
|
-
"MCP_TRANSPORT_TYPE": "stdio",
|
|
187
|
-
"MCP_LOG_LEVEL": "info"
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
170
|
+
```sh
|
|
171
|
+
claude mcp add eurostat-mcp-server -- npx -y @cyanheads/eurostat-mcp-server
|
|
192
172
|
```
|
|
193
173
|
|
|
194
|
-
|
|
174
|
+
For other clients, add this to the MCP config file:
|
|
195
175
|
|
|
196
176
|
```json
|
|
197
177
|
{
|
|
198
178
|
"mcpServers": {
|
|
199
179
|
"eurostat-mcp-server": {
|
|
200
|
-
"type": "stdio",
|
|
201
180
|
"command": "npx",
|
|
202
|
-
"args": ["-y", "@cyanheads/eurostat-mcp-server
|
|
203
|
-
"env": {
|
|
204
|
-
"MCP_TRANSPORT_TYPE": "stdio",
|
|
205
|
-
"MCP_LOG_LEVEL": "info"
|
|
206
|
-
}
|
|
181
|
+
"args": ["-y", "@cyanheads/eurostat-mcp-server"]
|
|
207
182
|
}
|
|
208
183
|
}
|
|
209
184
|
}
|
|
210
185
|
```
|
|
211
186
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
```json
|
|
215
|
-
{
|
|
216
|
-
"mcpServers": {
|
|
217
|
-
"eurostat-mcp-server": {
|
|
218
|
-
"type": "stdio",
|
|
219
|
-
"command": "docker",
|
|
220
|
-
"args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/eurostat-mcp-server:latest"]
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
For Streamable HTTP, set the transport and start the server:
|
|
227
|
-
|
|
228
|
-
```sh
|
|
229
|
-
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
|
|
230
|
-
# Server listens at http://localhost:3010/mcp
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
### Prerequisites
|
|
234
|
-
|
|
235
|
-
- [Bun v1.4.0](https://bun.sh/) or higher (or Node.js v24+).
|
|
236
|
-
- No API key: Eurostat's dissemination API and its Comext host are public.
|
|
237
|
-
|
|
238
|
-
### Installation
|
|
239
|
-
|
|
240
|
-
1. **Clone the repository:**
|
|
241
|
-
|
|
242
|
-
```sh
|
|
243
|
-
git clone https://github.com/cyanheads/eurostat-mcp-server.git
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
2. **Navigate into the directory:**
|
|
247
|
-
|
|
248
|
-
```sh
|
|
249
|
-
cd eurostat-mcp-server
|
|
250
|
-
```
|
|
251
|
-
|
|
252
|
-
3. **Install dependencies:**
|
|
253
|
-
|
|
254
|
-
```sh
|
|
255
|
-
bun install
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
4. **Configure environment (optional, every setting has a default):**
|
|
259
|
-
|
|
260
|
-
```sh
|
|
261
|
-
cp .env.example .env
|
|
262
|
-
```
|
|
187
|
+
No API key is required. For Docker or a source build, see [Running the server](#running-the-server).
|
|
263
188
|
|
|
264
189
|
## Configuration
|
|
265
190
|
|
|
@@ -267,15 +192,16 @@ cp .env.example .env
|
|
|
267
192
|
|:---|:---|:---|
|
|
268
193
|
| `EUROSTAT_BASE_URL` | Eurostat API base URL. | `https://ec.europa.eu/eurostat/api/dissemination` |
|
|
269
194
|
| `EUROSTAT_COMEXT_BASE_URL` | Comext dissemination host, which serves every `DS-*` dataset code (detailed trade and PRODCOM). | `https://ec.europa.eu/eurostat/api/comext/dissemination` |
|
|
270
|
-
| `EUROSTAT_REQUEST_TIMEOUT_MS` |
|
|
271
|
-
| `EUROSTAT_METADATA_TIMEOUT_MS` |
|
|
272
|
-
| `EUROSTAT_TOC_CACHE_TTL_MS` | Catalogue TOC cache lifetime, in ms
|
|
273
|
-
| `EUROSTAT_BULK_TIMEOUT_MS` |
|
|
274
|
-
| `EUROSTAT_BULK_MAX_BYTES` | Byte budget for one bulk download
|
|
275
|
-
| `CANVAS_PROVIDER_TYPE` | `
|
|
195
|
+
| `EUROSTAT_REQUEST_TIMEOUT_MS` | Total allowance for one request, including retries, backoff and body reads, in ms. | `30000` |
|
|
196
|
+
| `EUROSTAT_METADATA_TIMEOUT_MS` | Total allowance for one dataset's SDMX dataflow structure, including retries, backoff and body reads, in ms. | `120000` (2 min) |
|
|
197
|
+
| `EUROSTAT_TOC_CACHE_TTL_MS` | Catalogue TOC cache lifetime, in ms. | `43200000` (12 h) |
|
|
198
|
+
| `EUROSTAT_BULK_TIMEOUT_MS` | Total allowance for one bulk transfer, including request retries, backoff and streaming, in ms. | `120000` (2 min) |
|
|
199
|
+
| `EUROSTAT_BULK_MAX_BYTES` | Byte budget for one bulk download. | `52428800` (50 MiB) |
|
|
200
|
+
| `CANVAS_PROVIDER_TYPE` | Dataframe canvas backend; `none` disables the canvas and its tools. The `.mcpb` bundle sets `none`. | `duckdb` |
|
|
276
201
|
| `CANVAS_TTL_MS` | Sliding lifetime of a staged canvas, in ms. | `86400000` (24 h) |
|
|
277
|
-
| `CANVAS_DEFAULT_ROW_LIMIT` | Max rows one `eurostat_dataframe_query` returns before reporting `truncated`. | `10000` |
|
|
278
|
-
| `
|
|
202
|
+
| `CANVAS_DEFAULT_ROW_LIMIT` | Max complete rows one `eurostat_dataframe_query` returns before reporting `truncated`. | `10000` |
|
|
203
|
+
| `EUROSTAT_CLAMP_LIMITS` | Clamp positive integer preview/search/dimension limits to 500/100/1,000 with a notice. `false` restores schemas rejecting over-cap inputs; zero, negatives, fractions and nonnumeric inputs are always rejected. Each clamp logs once and increments `eurostat.limit_clamps`, labelled by tool. | `true` |
|
|
204
|
+
| `EUROSTAT_DATAFRAME_DROP_ENABLED` | `true` lists `eurostat_dataframe_drop`; no effect with `CANVAS_PROVIDER_TYPE=none`. | `false` |
|
|
279
205
|
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
|
|
280
206
|
| `MCP_HTTP_PORT` | HTTP server port. | `3010` |
|
|
281
207
|
| `MCP_SESSION_MODE` | HTTP session mode: `stateless`, `stateful`, or `auto`. The server declares `stateless`; a set value overrides it. | `stateless` |
|
|
@@ -292,6 +218,14 @@ See [`.env.example`](./.env.example) for the full list of optional overrides.
|
|
|
292
218
|
|
|
293
219
|
### Local development
|
|
294
220
|
|
|
221
|
+
Requires [Bun v1.4.0](https://bun.sh/) or higher (or Node.js v24+).
|
|
222
|
+
|
|
223
|
+
```sh
|
|
224
|
+
git clone https://github.com/cyanheads/eurostat-mcp-server.git
|
|
225
|
+
cd eurostat-mcp-server && bun install
|
|
226
|
+
cp .env.example .env # optional; every setting has a default
|
|
227
|
+
```
|
|
228
|
+
|
|
295
229
|
- **Build and run the production version**:
|
|
296
230
|
|
|
297
231
|
```sh
|
|
@@ -314,11 +248,10 @@ See [`.env.example`](./.env.example) for the full list of optional overrides.
|
|
|
314
248
|
### Docker
|
|
315
249
|
|
|
316
250
|
```sh
|
|
317
|
-
docker
|
|
318
|
-
docker run --rm -p 3010:3010 eurostat-mcp-server
|
|
251
|
+
docker run --rm -p 3010:3010 ghcr.io/cyanheads/eurostat-mcp-server:latest
|
|
319
252
|
```
|
|
320
253
|
|
|
321
|
-
The
|
|
254
|
+
The image defaults to HTTP transport, stateless session mode, and logs to `/var/log/eurostat-mcp-server`. For stdio, use `docker run -i --rm -e MCP_TRANSPORT_TYPE=stdio ghcr.io/cyanheads/eurostat-mcp-server:latest`. Build locally with `docker build -t eurostat-mcp-server .`; OpenTelemetry peer dependencies are installed by default — build with `--build-arg OTEL_ENABLED=false` to omit them.
|
|
322
255
|
|
|
323
256
|
## Project structure
|
|
324
257
|
|
|
@@ -335,6 +268,7 @@ The Dockerfile defaults to HTTP transport, stateless session mode, and logs to `
|
|
|
335
268
|
| `src/services/canvas-accessor.ts` | Accessor for the optional DataCanvas and the acquire helper canvas-touching tools share. |
|
|
336
269
|
| `src/services/shared-load.ts` | Lets concurrent callers await one shared download, so one caller's cancellation releases only that caller. |
|
|
337
270
|
| `src/config` | Server-specific environment variable parsing and validation with Zod. |
|
|
271
|
+
| `skills/` | Published eurostat-guide and SQL reference, served over MCP and included in installation packages. |
|
|
338
272
|
| `tests/` | Unit and integration tests, mirroring the `src/` structure. |
|
|
339
273
|
|
|
340
274
|
## Development guide
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Bounded Eurostat reads and lossless SQL results."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.10.1 — 2026-10-22
|
|
8
|
+
|
|
9
|
+
## Changed
|
|
10
|
+
|
|
11
|
+
- **Read limits** clamp positive integer previews to 500 and first-page searches to 100 with a notice, log and counter; `EUROSTAT_CLAMP_LIMITS=false` restores rejecting maximum schemas. ([#77](https://github.com/cyanheads/eurostat-mcp-server/issues/77))
|
|
12
|
+
- **Dataframe canvas** defaults to `duckdb`; set `CANVAS_PROVIDER_TYPE=none` for inline-only reads. The portable `.mcpb` bundle sets `none` because it excludes native bindings.
|
|
13
|
+
|
|
14
|
+
## Fixed
|
|
15
|
+
|
|
16
|
+
- **Request deadlines** bound retries, backoff and body consumption by each operation's configured allowance, preserving caller cancellation and separate metadata/bulk budgets. ([#72](https://github.com/cyanheads/eurostat-mcp-server/issues/72))
|
|
17
|
+
- **SQL cells** render as lossless JSON on the text surface, preserving nested values and distinguishing strings from nulls. ([#74](https://github.com/cyanheads/eurostat-mcp-server/issues/74))
|
|
18
|
+
- **SQL response size** applies 64,000 bytes per serialized surface, including enrichment, between complete rows; at least one row survives even if oversized. Cuts disclose `truncated` and deterministic SQL paging that advances by actual `rowCount`. ([#75](https://github.com/cyanheads/eurostat-mcp-server/issues/75))
|
|
19
|
+
- **Bulk byte budget** retains complete observations within the allowed decoded prefix; an unusable prefix fails as `byte_budget_exceeded` with narrowing guidance. `bytesRead` never exceeds the configured budget. ([#76](https://github.com/cyanheads/eurostat-mcp-server/issues/76))
|
|
20
|
+
|
|
21
|
+
## Dependencies
|
|
22
|
+
|
|
23
|
+
- **`zod`** ^4.6.6 → ^4.6.7.
|
|
24
|
+
- **`ignore`** ^7.0.12 → ^7.0.13.
|
|
25
|
+
- **`tsc-alias`** ^1.9.7 → ^1.9.8.
|
|
26
|
+
- **`vitest`** ^5.0.3 → ^5.0.5.
|
|
27
|
+
- **Bun** 1.4.3 → 1.4.4 (`packageManager` and Docker images).
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Complete Eurostat results and guided statistical analysis."
|
|
3
|
+
breaking: true
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.11.0 — 2026-10-26
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **`eurostat_query_dataset`** accepts `stage: true` to retain every matched observation on a canvas at any size, with an unavailable-staging notice when the canvas is disabled ([#80](https://github.com/cyanheads/eurostat-mcp-server/issues/80)).
|
|
12
|
+
|
|
13
|
+
## Changed
|
|
14
|
+
|
|
15
|
+
- Published Eurostat analysis guide as MCP skill resources.
|
|
16
|
+
- **Dimension pages and SQL results** use count limits and complete rows instead of response byte cuts; dimension pages default to 25, cap at 1,000, and retain cursor continuation ([#82](https://github.com/cyanheads/eurostat-mcp-server/issues/82)).
|
|
17
|
+
- **`EUROSTAT_CLAMP_LIMITS=false`** now rejects dimension `limit` above 1,000. Request at most 1,000 and follow `nextCursor`, or enable clamping; default-on clamping remains permissive.
|
|
18
|
+
- **SQL column references** named `current_schema` must now be qualified, such as `d.current_schema`; unqualified references are rejected by the adopted SQL gate.
|
|
19
|
+
- **Query and download previews** default to 25 observations; `last_n_periods` advertises no maximum.
|
|
20
|
+
- **Installation and development** include Agent Plugins manifests, published guide delivery across npm, Docker and MCPB, synchronized framework checks, and shorter connection instructions.
|
|
21
|
+
|
|
22
|
+
## Fixed
|
|
23
|
+
|
|
24
|
+
- **`last_n_periods`** success notices disclose selected time positions with no populated cells across the full match and explain how to reach earlier observations ([#79](https://github.com/cyanheads/eurostat-mcp-server/issues/79)).
|
|
25
|
+
- **Dimension and theme identifiers** resolve exact matches before case-insensitive matches; `TIME_PERIOD` aliases `time`, and dimension misses name the available codes ([#81](https://github.com/cyanheads/eurostat-mcp-server/issues/81)).
|
|
26
|
+
|
|
27
|
+
## Dependencies
|
|
28
|
+
|
|
29
|
+
- `@cyanheads/mcp-ts-core` ^0.13.15 → ^0.13.18.
|
|
30
|
+
- `@duckdb/node-api` ^1.5.6-r.1 → ^2.0.0-r.1.
|
|
31
|
+
- `@biomejs/biome` 2.5.15 → 2.6.3.
|
|
32
|
+
- `ignore` ^7.0.13 → ^7.0.14.
|
|
33
|
+
- `tsc-alias` ^1.9.8 → ^1.9.9.
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
6
|
declare const ServerConfigSchema: z.ZodObject<{
|
|
7
|
+
clampLimits: z.ZodDefault<z.ZodCodec<z.ZodString, z.ZodBoolean>>;
|
|
7
8
|
baseUrl: z.ZodDefault<z.ZodString>;
|
|
8
9
|
comextBaseUrl: z.ZodDefault<z.ZodString>;
|
|
9
10
|
requestTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C,QAAA,MAAM,kBAAkB
|
|
1
|
+
{"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAG3C,QAAA,MAAM,kBAAkB;;;;;;;;;;iBAiEtB,CAAC;AAIH,wBAAgB,eAAe,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAapE"}
|