@cyanheads/eurostat-mcp-server 0.8.0 → 0.9.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.
Files changed (34) hide show
  1. package/AGENTS.md +25 -19
  2. package/CLAUDE.md +25 -19
  3. package/Dockerfile +93 -46
  4. package/README.md +34 -35
  5. package/changelog/0.8.x/0.8.1.md +11 -0
  6. package/changelog/0.9.x/0.9.0.md +43 -0
  7. package/dist/config/server-config.d.ts +1 -0
  8. package/dist/config/server-config.d.ts.map +1 -1
  9. package/dist/config/server-config.js +5 -0
  10. package/dist/config/server-config.js.map +1 -1
  11. package/dist/index.js +44 -4
  12. package/dist/index.js.map +1 -1
  13. package/dist/mcp-server/tools/definitions/eurostat-dataframe-describe.tool.js +1 -1
  14. package/dist/mcp-server/tools/definitions/eurostat-dataframe-describe.tool.js.map +1 -1
  15. package/dist/mcp-server/tools/definitions/eurostat-dataframe-drop.tool.d.ts +34 -0
  16. package/dist/mcp-server/tools/definitions/eurostat-dataframe-drop.tool.d.ts.map +1 -0
  17. package/dist/mcp-server/tools/definitions/eurostat-dataframe-drop.tool.js +112 -0
  18. package/dist/mcp-server/tools/definitions/eurostat-dataframe-drop.tool.js.map +1 -0
  19. package/dist/mcp-server/tools/definitions/eurostat-dataframe-query.tool.js +1 -1
  20. package/dist/mcp-server/tools/definitions/eurostat-dataframe-query.tool.js.map +1 -1
  21. package/dist/mcp-server/tools/definitions/eurostat-download-dataset.tool.js +1 -1
  22. package/dist/mcp-server/tools/definitions/eurostat-download-dataset.tool.js.map +1 -1
  23. package/dist/mcp-server/tools/definitions/eurostat-query-dataset.tool.js +2 -2
  24. package/dist/mcp-server/tools/definitions/eurostat-query-dataset.tool.js.map +1 -1
  25. package/dist/mcp-server/tools/narrowing-advice.d.ts +2 -1
  26. package/dist/mcp-server/tools/narrowing-advice.d.ts.map +1 -1
  27. package/dist/mcp-server/tools/narrowing-advice.js +4 -1
  28. package/dist/mcp-server/tools/narrowing-advice.js.map +1 -1
  29. package/dist/services/canvas-accessor.d.ts +10 -6
  30. package/dist/services/canvas-accessor.d.ts.map +1 -1
  31. package/dist/services/canvas-accessor.js +14 -10
  32. package/dist/services/canvas-accessor.js.map +1 -1
  33. package/package.json +10 -9
  34. package/server.json +26 -26
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 (8 with the dataframe canvas) • 1 Resource</div>
4
+ <div>6 Tools (up to 9 with the dataframe canvas) • 1 Resource</div>
5
5
  </p>
6
6
  </div>
7
7
 
8
8
  <div align="center">
9
9
 
10
- [![Version](https://img.shields.io/badge/Version-0.8.0-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/eurostat-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.0.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/eurostat-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/eurostat-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.0-blueviolet.svg?style=flat-square)](https://bun.sh/)
10
+ [![Version](https://img.shields.io/badge/Version-0.9.0-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/eurostat-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.2.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/eurostat-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/eurostat-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.2-blueviolet.svg?style=flat-square)](https://bun.sh/)
11
11
 
12
12
  </div>
13
13
 
@@ -35,7 +35,7 @@ The catalogue spans two Eurostat hosts. Most datasets come from the disseminatio
35
35
 
36
36
  ### Tools
37
37
 
38
- Two of the eight are listed only when the dataframe canvas is enabled (`CANVAS_PROVIDER_TYPE=duckdb`).
38
+ Three of the nine are listed only when the dataframe canvas is enabled (`CANVAS_PROVIDER_TYPE=duckdb`), and `eurostat_dataframe_drop` also needs `EUROSTAT_DATAFRAME_DROP_ENABLED=true`.
39
39
 
40
40
  | Tool | Description |
41
41
  |:---|:---|
@@ -47,6 +47,7 @@ Two of the eight are listed only when the dataframe canvas is enabled (`CANVAS_P
47
47
  | `eurostat_download_dataset` | Download a whole dataset via the SDMX bulk endpoint and stage every observation on the dataframe canvas |
48
48
  | `eurostat_dataframe_describe` | List the tables staged on a dataframe canvas, with row counts and column types |
49
49
  | `eurostat_dataframe_query` | Run a read-only SQL SELECT across staged tables |
50
+ | `eurostat_dataframe_drop` | Remove one staged table from a dataframe canvas, leaving its other tables in place (opt-in) |
50
51
 
51
52
  ### Resources
52
53
 
@@ -61,69 +62,66 @@ The same metadata is available through `eurostat_get_dataset_info` for tool-only
61
62
  ### `eurostat_search_datasets` <sub>tool</sub>
62
63
 
63
64
  - `query` tokens are ANDed case-insensitively across each dataset's label, theme breadcrumb, and code; `limit` 1–100 (default 20), paged by passing `nextCursor` back as `cursor`
64
- - Results carry `code`, `label`, `type` (`dataset` / `table`), period coverage, `obsCount`, `lastUpdated`, and `themePath`; `totalMatches` counts every match
65
- - Searches the dissemination table of contents plus the Comext host's dataflow list, whose `DS-*` entries carry `lastUpdated` but no period coverage or `obsCount`. A collection on neither, such as the legacy PRODCOM `DS-056120`, is not disseminated and cannot be reached
66
- - Fails as `no_match` — for a query naming such a `DS-*` code, saying the collection is not disseminated rather than suggesting a broader search — or `invalid_cursor` when a cursor is reused with a different query or after the catalogue refreshes
65
+ - Results carry `code`, `label`, `type` (`dataset` / `table`), period coverage, `obsCount`, `lastUpdated`, and `themePath`, with `totalMatches` across every page; fails as `no_match` or `invalid_cursor`
67
66
 
68
67
  ---
69
68
 
70
69
  ### `eurostat_browse_themes` <sub>tool</sub>
71
70
 
72
- - Omit `theme_code` for the top-level theme folders; pass a folder code for its immediate children
73
- - The Comext collections sit in two folders the table of contents lacks: `ext_go_detail` (detailed trade) under `ext_go`, and `prom` (PRODCOM) under `icts`
74
- - Items carry `code`, `label`, `type` (`folder` / `dataset` / `table`), and `hasChildren`, with a `parentPath` breadcrumb; `otherPlacements` names other branches that file the same folder code
75
- - Fails as `not_found` for an unknown code, `not_a_folder` for a dataset or table code
71
+ - 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`)
72
+ - Items carry `code`, `label`, `type` (`folder` / `dataset` / `table`), and `hasChildren`, with a `parentPath` breadcrumb and `otherPlacements` for a folder code filed in several branches; fails as `not_found` for an unknown code, `not_a_folder` for a dataset or table code
76
73
 
77
74
  ---
78
75
 
79
76
  ### `eurostat_get_dataset_info` <sub>tool</sub>
80
77
 
81
78
  - One `dataset_code`; returns every dimension with `valuesCount` and up to 10 `sampleValues`, plus `timeRange`, `obsCount`, `lastUpdated`, and the ESMS `metadataUrl`
82
- - The Comext host reports no `timeRange` or `obsCount` for a `DS-*` collection, so both read as unreported. Its structure carries a label for every code, 23 MB for `DS-045409`: parsed metadata is cached per dataset for an hour, within a 64 MiB memory budget, so only the first call pays for it
83
- - Fails as `not_found` for an unknown dataset, or `upstream_fault` when Eurostat returns a structure or content constraint the server cannot read; a failed read is not cached, so a later call downloads it afresh
79
+ - Fails as `not_found` for an unknown dataset, or `upstream_fault` when Eurostat returns a structure or content constraint the server cannot read
84
80
 
85
81
  ---
86
82
 
87
83
  ### `eurostat_get_dimension_values` <sub>tool</sub>
88
84
 
89
- - `dataset_code` plus `dimension`; returns the dataset-available code/label pairs in `values`, with `totalCount`
90
- - At most 2,000 values come back inline, in Eurostat's order. A longer list — the 37,069 CN8 codes of `DS-045409`, a daily `time` dimension, the largest airport-pair lists — is cut there, with `truncated`, `shown`, `cap`, and a `notice` saying where the rest is
91
- - For `geo`, `geo_level` picks a NUTS level (`aggregate`, `country` by default, `nuts1`, `nuts2`, `nuts3`)
92
- - `canvas_id` also stages every value, past the inline cap too, on that canvas as a `code` / `label` table (`canvasId` / `tableName` / `stagedRowCount`), to search with SQL or to label a download's code-only columns through a join; this tool never starts a canvas of its own, and ignores `canvas_id` on a deployment without one
93
- - Fails as `not_found` for an unknown dataset or dimension, `no_results` for a NUTS level with no values, `conflicting_params` for `geo_level` on any other dimension, `upstream_fault` for a structure or content constraint the server cannot read, or `canvas_not_found` for an unknown or expired `canvas_id`
85
+ - `dataset_code` plus `dimension`; for `geo`, `geo_level` picks a NUTS level (`aggregate`, `country` by default, `nuts1`, `nuts2`, `nuts3`). At most 2,000 values come back inline, in Eurostat's order
86
+ - Returns code/label pairs in `values` with `totalCount`, and `truncated` / `shown` / `cap` past the inline cap; fails as `not_found`, `no_results`, `conflicting_params`, `upstream_fault`, or `canvas_not_found`
87
+ - `canvas_id` stages every value, past the inline cap too, 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
94
88
 
95
89
  ---
96
90
 
97
91
  ### `eurostat_query_dataset` <sub>tool</sub>
98
92
 
99
- - `dataset_code` plus `filters` (`{dimension_code: [values]}`, codes in any case), `geo_level`, and either `since_period` / `until_period` (`YYYY`, `YYYY-MM`, `YYYY-MM-DD`, `YYYY-Qn`, `YYYY-Sn`, `YYYY-Tn`, `YYYY-Mnn`, `YYYY-Wnn`, `YYYY-Dnnn`; extra leading zeros after the letter are dropped, so `2020-Q01` is sent as `2020-Q1`, and `YYYY-A1` is sent as `YYYY`) or `last_n_periods`, which counts back from the dataset's latest period rather than the slice's; `preview_limit` 1–500 (default 50) sets the inline row count; `lang` is `EN`, `FR`, or `DE`
100
- - Each observation carries code/label pairs per dimension, a nullable `value`, an OBS_FLAG `status`, and a CONF_STATUS `confStatus`; `obsCount`, `missingObsCount`, and `timeRange` cover the whole match, `truncated` is true above 5,000 observations, and `unmatchedValues` names filter values that matched nothing
101
- - PRODCOM publishes its flag and unit indicators as text in the value itself: `:C` comes back as a null `value` with `confStatus` `C`, and anything else, such as the unit `KG`, verbatim in `valueText` with a null `value`
102
- - Fails as `not_found`, `no_results` (naming the filter values that matched nothing and, up to the newest 24 with a full count, the selected periods that carry no value), `invalid_dimension`, `invalid_period` (a malformed or non-existent period, or a `since_period` that starts after `until_period` ends, rejected before any request), `conflicting_params` (`geo` filter with `geo_level`, or a period range with `last_n_periods`), a non-retryable `async_response` for a query too large to serve inline, or `canvas_not_found` for an unknown or expired `canvas_id`
93
+ - `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` 1–500 (default 50) sets the inline rows; `lang` is `EN`, `FR`, or `DE`
94
+ - 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
95
+ - Fails as `not_found`, `no_results` (naming the unmatched values and the periods that carry no value), `invalid_dimension`, `invalid_period`, `conflicting_params`, a non-retryable `async_response` for a query too large to serve inline, or `canvas_not_found`
103
96
 
104
97
  ---
105
98
 
106
99
  ### `eurostat_download_dataset` <sub>tool</sub>
107
100
 
108
- - `dataset_code` plus the same `filters` map and `since_period` / `until_period` in the same period forms (no `last_n_periods`: the TSV keeps a column per period unless a range drops it); `preview_limit` 1–500 (default 50)
109
- - Returns `rowCount` (echoed as `totalCount`), `missingCount`, `periodRange`, and `bytesRead` for the whole download; the `EUROSTAT_BULK_MAX_BYTES` budget (default 50 MiB) stops a transfer mid-stream and returns what arrived with `budgetExceeded: true`
110
- - On a `DS-*` table, `obs_value_text` holds a value published as text (PRODCOM's `KG`), and a PRODCOM `:C` lands in `conf_status`
111
- - Fails as `not_found`, `invalid_dimension` (including a range wholly outside the dataset's coverage), `invalid_period` (a malformed or non-existent period, or a `since_period` that starts after `until_period` ends, rejected before any request), `filter_arity`, a non-retryable `async_queued` when Eurostat queues the extraction, a non-retryable `extraction_too_big` when Eurostat refuses it as too large (past its 5,000,000-row limit, or an unfiltered download of a large Comext collection) with the dimensions left to filter, `no_results` (including a range inside the dataset's coverage that misses the filtered series), `upstream_fault`, or `canvas_not_found` for an unknown or expired `canvas_id`
101
+ - `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` 1–500 (default 50)
102
+ - 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`, or `canvas_not_found`
103
+ - The `EUROSTAT_BULK_MAX_BYTES` budget (default 50 MiB) stops a transfer mid-stream and returns what arrived with `budgetExceeded: true`
112
104
 
113
105
  ---
114
106
 
115
107
  ### `eurostat_dataframe_describe` <sub>tool</sub>
116
108
 
117
- - `canvas_id` from a staging response; lists each table's `name`, `rowCount`, and typed columns, plus canvas and table `expiresAt` (sliding `CANVAS_TTL_MS`, default 24h); fails as `canvas_not_found` for an unknown or expired ID, or `canvas_disabled` on a deployment without a canvas
118
- - `eurostat_query_dataset` tables carry a code and a `_label` column per dimension; `eurostat_download_dataset` tables carry codes only, plus `time`. Both share `obs_value`, `obs_flag`, `obs_flag_label`, `conf_status`, and `conf_status_label`, so they join on dimension codes and `time`; a `DS-*` table from either adds `obs_value_text`
119
- - `eurostat_get_dimension_values` tables carry `code` and `label`: join one to a download on its dimension column (`d.geo = g.code`) to label it
109
+ - `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`
110
+ - Columns differ by the tool that staged them: `eurostat_query_dataset` writes a code and a `_label` column per dimension, `eurostat_download_dataset` codes only plus `time` — both with the same `obs_*` and `conf_status*` measures, so they join on dimension codes and `time` — and `eurostat_get_dimension_values` writes `code` and `label`
120
111
 
121
112
  ---
122
113
 
123
114
  ### `eurostat_dataframe_query` <sub>tool</sub>
124
115
 
125
116
  - One read-only `SELECT` per call; chained statements, other verbs, and functions that read files or external data are rejected
126
- - Returns `columns`, `rows`, `rowCount`, and `truncated`, which is true past `CANVAS_DEFAULT_ROW_LIMIT` (default 10,000) rows; 64-bit integers, `COUNT(*)` included, arrive as strings. Fails as `missing_table`, `canvas_not_found`, or `canvas_disabled`
117
+ - Returns `columns`, `rows`, `rowCount`, and `truncated`, true past `CANVAS_DEFAULT_ROW_LIMIT` (default 10,000) rows; 64-bit integers arrive as strings. Fails as `missing_table`, `canvas_not_found`, or `canvas_disabled`
118
+
119
+ ---
120
+
121
+ ### `eurostat_dataframe_drop` <sub>tool</sub>
122
+
123
+ - `canvas_id` plus `table_name`, exactly as `eurostat_dataframe_describe` lists it; removes that one table and leaves the canvas and its other tables in place. Listed only when `EUROSTAT_DATAFRAME_DROP_ENABLED=true` is set beside the canvas
124
+ - Returns `canvasId`, `tableName`, `dropped`, and `expiresAt`; `dropped` is `false`, with a `notice`, when nothing by that name was staged, so repeating a drop is safe. Fails as `canvas_not_found`, `identifier_reserved`, or `canvas_disabled`
127
125
 
128
126
  ---
129
127
 
@@ -139,9 +137,8 @@ Built on [`@cyanheads/mcp-ts-core`](https://github.com/cyanheads/mcp-ts-core): s
139
137
  Eurostat-specific:
140
138
 
141
139
  - 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
142
- - The Comext host's `DS-*` collections through the same tools: detailed trade by CN8, HS, SITC, BEC and CPA, and PRODCOM. The trade flows mix annual and monthly series in one dataset, so filter `freq`; `product`, `reporter` and `partner` carry aggregates (`TOTAL`, `EU27_2020`, `EXT_EU27_2020`) that double-count when summed with their members. The Comext host serves no large collection unfiltered
143
- - NUTS geo-level filtering (`aggregate` / `country` / `nuts1` / `nuts2` / `nuts3`) on `eurostat_query_dataset` and `eurostat_get_dimension_values`
144
- - OBS_FLAG (provisional, estimated, and so on) and CONF_STATUS (confidentiality) decoded into separate fields, with the same codes from JSON-stat and the bulk TSV
140
+ - The Comext host's `DS-*` collections through the same tools: detailed trade by CN8, HS, SITC, BEC and CPA, and PRODCOM. They report no period coverage or `obsCount`, and the trade flows mix annual and monthly series in one dataset, so filter `freq`; `product`, `reporter` and `partner` carry aggregates (`TOTAL`, `EU27_2020`, `EXT_EU27_2020`) that double-count when summed with their members. The Comext host serves no large collection unfiltered. PRODCOM values published as text arrive in `valueText` / `obs_value_text` (the unit `KG`), and its `:C` as confidentiality status `C`. A collection on neither host, such as the legacy `DS-056120`, is not disseminated
141
+ - NUTS geo-level filtering (`aggregate` / `country` / `nuts1` / `nuts2` / `nuts3`) on `eurostat_query_dataset` and `eurostat_get_dimension_values`, and OBS_FLAG (provisional, estimated, and so on) and CONF_STATUS (confidentiality) decoded into separate fields, with the same codes from JSON-stat and the bulk TSV
145
142
  - Eurostat's too-large-to-serve responses (HTTP-200 async warnings, HTTP 413, SOAP fault 413, SOAP queue tickets) come back as non-retryable `async_response`, `extraction_too_big`, or `async_queued` errors that name the dataset's own dimensions left to filter, not timeouts
146
143
  - 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
147
144
 
@@ -270,15 +267,17 @@ cp .env.example .env
270
267
  | `EUROSTAT_TOC_CACHE_TTL_MS` | Catalogue TOC cache lifetime, in ms; the first search or browse call past it refreshes the TOC. | `43200000` (12 h) |
271
268
  | `EUROSTAT_BULK_TIMEOUT_MS` | Timeout for one `eurostat_download_dataset` transfer, in ms. | `120000` (2 min) |
272
269
  | `EUROSTAT_BULK_MAX_BYTES` | Byte budget for one bulk download, counted on the decoded TSV and enforced while streaming. | `52428800` (50 MiB) |
273
- | `CANVAS_PROVIDER_TYPE` | `duckdb` enables the dataframe canvas: lists the two dataframe tools and turns on staging. | `none` |
270
+ | `CANVAS_PROVIDER_TYPE` | `duckdb` enables the dataframe canvas: lists `eurostat_dataframe_describe` and `eurostat_dataframe_query` and turns on staging. | `none` |
274
271
  | `CANVAS_TTL_MS` | Sliding lifetime of a staged canvas, in ms. | `86400000` (24 h) |
275
272
  | `CANVAS_DEFAULT_ROW_LIMIT` | Max rows one `eurostat_dataframe_query` returns before reporting `truncated`. | `10000` |
273
+ | `EUROSTAT_DATAFRAME_DROP_ENABLED` | `true` lists `eurostat_dataframe_drop`, which removes one staged table; needs `CANVAS_PROVIDER_TYPE=duckdb`. Off by default because a drop cannot be undone. | `false` |
276
274
  | `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
277
275
  | `MCP_HTTP_PORT` | HTTP server port. | `3010` |
278
276
  | `MCP_SESSION_MODE` | HTTP session mode: `stateless`, `stateful`, or `auto`. The server declares `stateless`; a set value overrides it. | `stateless` |
279
277
  | `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth`. | `none` |
280
278
  | `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, etc.). The Docker image sets `info`. | `debug` |
281
279
  | `LOGS_DIR` | Directory for log files (Node.js only). | `<project-root>/logs` |
280
+ | `LOG_TOOL_FAILURE_PAYLOADS` | Log each failed tool call's arguments and result, redacted by key name and capped at `LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES` (default `16384`). A secret inside a free-form value is not redacted. | `false` |
282
281
  | `STORAGE_PROVIDER_TYPE` | Storage backend: `in-memory`, `filesystem`, `supabase`, `cloudflare-kv/r2/d1`. | `in-memory` |
283
282
  | `OTEL_ENABLED` | Enable [OpenTelemetry](https://github.com/cyanheads/mcp-ts-core/tree/main/docs/telemetry). | `false` |
284
283
 
@@ -320,8 +319,8 @@ The Dockerfile defaults to HTTP transport, stateless session mode, and logs to `
320
319
 
321
320
  | Directory | Purpose |
322
321
  |:---|:---|
323
- | `src/index.ts` | `createApp()` entry point — registers tools and the resource, inits services, gates the dataframe tools on the canvas. |
324
- | `src/mcp-server/tools` | Tool definitions (`*.tool.ts`). Six discovery and data tools, plus two canvas-gated dataframe tools. |
322
+ | `src/index.ts` | `createApp()` entry point — registers tools and the resource, inits services, gates the dataframe tools on the canvas and the drop tool on its own flag. |
323
+ | `src/mcp-server/tools` | Tool definitions (`*.tool.ts`). Six discovery and data tools, plus three canvas-gated dataframe tools, one of them opt-in. |
325
324
  | `src/mcp-server/resources` | Resource definitions. Dataset metadata resource. |
326
325
  | `src/services/eurostat-catalogue` | Catalogue service — fetches and parses the Eurostat TOC, merges the Comext dataflow list into it, with a TTL-bounded in-memory cache. |
327
326
  | `src/services/eurostat-data` | Statistics API service — SDMX metadata parsing with a per-dataset cache, JSON-stat 2.0 decoding, async-response detection, canvas row source. |
@@ -0,0 +1,11 @@
1
+ ---
2
+ summary: "The Docker images and packageManager pin move to Bun 1.4.2."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.8.1 — 2026-09-25
8
+
9
+ ## Changed
10
+
11
+ - **Bun runtime pinned to 1.4.2**: the Docker build and production stages use `oven/bun:1.4.2` and `oven/bun:1.4.2-slim`, and `packageManager` is `bun@1.4.2` (the `engines.bun` floor stays `>=1.4.0`).
@@ -0,0 +1,43 @@
1
+ ---
2
+ summary: "Opt-in eurostat_dataframe_drop removes one staged table from a canvas, the server moves to mcp-ts-core 0.13.11, and an unwritable CANVAS_TEMP_PATH now fails as a configuration error naming its errno."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.9.0 — 2026-10-03
8
+
9
+ ## Added
10
+
11
+ - **`eurostat_dataframe_drop`** removes one table from a dataframe canvas, leaving the canvas and its other tables in place, and returns `canvasId`, `tableName`, `dropped`, and `expiresAt`. `dropped: false`, with a `notice`, means nothing by that name was staged, so repeating a drop is safe.
12
+ - **`EUROSTAT_DATAFRAME_DROP_ENABLED`** (default `false`) gates it, since a drop cannot be undone: the tool is listed only with this set to `true` beside `CANVAS_PROVIDER_TYPE=duckdb`, and is otherwise registered disabled, shown on the landing page with the setting or settings that turn it on. A boot test pins that a `.env` file in the working directory sets the gate as the environment does.
13
+ - **The drop tool's errors carry its own recovery** ([#66](https://github.com/cyanheads/eurostat-mcp-server/issues/66)) — `canvas_not_found` and `identifier_reserved` reach the caller with the tool's declared recovery instead of the canvas service's hints, which are worded for staging (stage the data again, choose another table name), and `identifier_reserved` with the tool's own message.
14
+
15
+ ## Changed
16
+
17
+ - **The server config is parsed in `setup()`**, so a bad `EUROSTAT_*` value fails startup with the framework's configuration banner naming the variable. The first tool call used to parse it, so the server reported ready and then failed every call.
18
+ - **Dockerfile installs in a `deps` stage** on `$BUILDPLATFORM`, cross-installing for the target with `--os`/`--cpu` through `bunfig.toml`'s release-age gate and scanner; `scripts/install-otel.ts` installs the OTel peers and `scripts/prune-musl-packages.ts` drops musl-only packages. `MCP_FORCE_CONSOLE_LOGGING`, which nothing read, is gone.
19
+ - **`server.json` npm entries launch the bin via `npx`** without the `run start:*` arguments, and the `streamable-http` entry fixes `MCP_TRANSPORT_TYPE` to `http`; the bin ignored those arguments, so the HTTP entry started on stdio.
20
+ - **Tool handlers no longer forward `ctx.recoveryFor`**, which the framework now fills (0.13.10); the recovery hints on the wire are unchanged.
21
+ - **Repo and packaging config** — `.gitignore` and `.dockerignore` cover every `.env*` file (the three templates stay tracked) and `.dockerignore` adds the MCP registry token files, CodeQL runs one job per language under `/language:<language>`, `.mcpbignore` excludes `docs/idea.md` and `logs/`, and `package.json` `files` excludes `dist/*.mcpb`.
22
+ - **Docs** — the README documents the drop tool and condenses the per-tool reference, `.env.example` documents `EUROSTAT_DATAFRAME_DROP_ENABLED`, `LOG_TOOL_FAILURE_PAYLOADS`, and the per-signal OTLP endpoints, `CANVAS_TEMP_PATH` is described as a parent directory throughout, and `CLAUDE.md`/`AGENTS.md` follow the 0.13.11 template.
23
+ - **Framework skills and scripts resynced** from mcp-ts-core 0.13.11, adding `scripts/install-otel.ts` and `scripts/prune-musl-packages.ts` and removing the one-shot `scripts/split-changelog.ts`; `lint:packaging` gains the `.mcpb` exclusion, manifest version, Dockerfile build platform, and npm launch shape checks.
24
+ - **mcp-ts-core 0.13.7** — server stack traces (`originalStack`, `causeChain`) no longer reach clients through error `data`. With `OTEL_ENABLED=true`, `OTEL_EXPORTER_OTLP_ENDPOINT` alone exports traces and metrics to `<base>/v1/traces` and `<base>/v1/metrics`, and with no endpoint set nothing is exported (it used to send to `localhost:4318`).
25
+ - **mcp-ts-core 0.13.8** — request context (tenant, operation, `extra` fields) no longer reaches client error `data`, and a tool result that breaks its own output schema fails as `-32603` (was `-32007`). Opt-in OTLP log export (`OTEL_EXPORTER_OTLP_LOGS_ENDPOINT`) and failed-call payload logging (`LOG_TOOL_FAILURE_PAYLOADS`, `LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES`).
26
+ - **mcp-ts-core 0.13.9** — a JSON-stringified object sent for an object field, or an integer sent for a string field, is repaired before validation, and argument rejections name alias rewrites and dropped keys. Canvas scratch files live in a private directory inside `CANVAS_TEMP_PATH`. MCP SDK 2.1.0 refuses a JSON-RPC batch over 100 messages, and stdin EOF on stdio aborts in-flight requests.
27
+ - **mcp-ts-core 0.13.10** — a declared `recovery` hint reaches the wire on any typed error that carries none of its own, service throws included. Error results carry `data.requestId`, and their `content[]` closes with `request <id>`. `invalid_arguments` refusals log at `notice`. The multi-arch Docker build installs dependencies on the build platform, so Bun no longer runs under QEMU.
28
+ - **mcp-ts-core 0.13.11** — `ctx.log` messages mirrored to the client honor `MCP_LOG_LEVEL` and mask sensitive fields, the log level filter compares RFC 5424 severity, and resource-read logs cap the URI at 1,024 characters. The Docker image drops musl-only bindings, `lint:packaging` enforces the `server.json` npm launch shape above, and `@modelcontextprotocol/server` moves to 2.2.0.
29
+
30
+ ## Fixed
31
+
32
+ - **An unwritable `CANVAS_TEMP_PATH` fails as `-32008` `ConfigurationError`** (was `-32000` `ServiceUnavailable`), naming its errno in the message and in `data.errno` beside `data.reason: "canvas_unavailable"`. Since mcp-ts-core 0.13.9 the provider fails with that code itself; the server keeps it and adds the errno and the `CANVAS_PROVIDER_TYPE=none` way out, which the framework's message omits.
33
+ - **A cancelled `eurostat_query_dataset` or `eurostat_download_dataset` call stays a cancellation** when it reaches the too-large refusal's dimension lookup, which rethrows the abort instead of swallowing it into a generic narrowing hint.
34
+
35
+ ## Dependencies
36
+
37
+ - `@cyanheads/mcp-ts-core` ^0.13.6 → ^0.13.11 (`@modelcontextprotocol/server` 2.0.0 → 2.2.0 transitively).
38
+ - `@duckdb/node-api` ^1.5.5-r.5 → ^1.5.6-r.1.
39
+ - Dev: `@biomejs/biome` 2.5.14 → 2.5.15.
40
+ - Dev: `@types/node` 26.6.2 → 26.6.3.
41
+ - Dev: `ignore` ^7.0.9 → ^7.0.11.
42
+ - Dev: `tsc-alias` ^1.9.5 → ^1.9.7.
43
+ - Dev: `vitest` ^5.0.1 → ^5.0.3.
@@ -11,6 +11,7 @@ declare const ServerConfigSchema: z.ZodObject<{
11
11
  tocCacheTtlMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
12
12
  bulkTimeoutMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
13
13
  bulkMaxBytes: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
14
+ dataframeDropEnabled: z.ZodDefault<z.ZodCodec<z.ZodString, z.ZodBoolean>>;
14
15
  }, z.core.$strip>;
15
16
  export declare function getServerConfig(): z.infer<typeof ServerConfigSchema>;
16
17
  export {};
@@ -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;;;;;;;;iBAmDtB,CAAC;AAIH,wBAAgB,eAAe,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAWpE"}
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;;;;;;;;;iBAyDtB,CAAC;AAIH,wBAAgB,eAAe,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAYpE"}
@@ -45,6 +45,10 @@ const ServerConfigSchema = z.object({
45
45
  .positive()
46
46
  .default(52_428_800)
47
47
  .describe('Byte budget for one SDMX bulk download, measured on the decoded TSV rather than on the wire. Eurostat sends the body chunked with no Content-Length, so the budget is enforced while streaming and the transfer is aborted the moment it is reached. Defaults to 50 MiB.'),
48
+ dataframeDropEnabled: z
49
+ .stringbool()
50
+ .default(false)
51
+ .describe('Register eurostat_dataframe_drop, which removes one staged table from a dataframe canvas. Off by default: a drop cannot be undone, and staged tables already expire with their canvas. Takes effect only with CANVAS_PROVIDER_TYPE=duckdb.'),
48
52
  });
49
53
  let _config;
50
54
  export function getServerConfig() {
@@ -56,6 +60,7 @@ export function getServerConfig() {
56
60
  tocCacheTtlMs: 'EUROSTAT_TOC_CACHE_TTL_MS',
57
61
  bulkTimeoutMs: 'EUROSTAT_BULK_TIMEOUT_MS',
58
62
  bulkMaxBytes: 'EUROSTAT_BULK_MAX_BYTES',
63
+ dataframeDropEnabled: 'EUROSTAT_DATAFRAME_DROP_ENABLED',
59
64
  });
60
65
  return _config;
61
66
  }
@@ -1 +1 @@
1
- {"version":3,"file":"server-config.js","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAE/D,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,OAAO,EAAE,CAAC;SACP,MAAM,EAAE;SACR,GAAG,EAAE;SACL,OAAO,CAAC,iDAAiD,CAAC;SAC1D,QAAQ,CAAC,uBAAuB,CAAC;IACpC,aAAa,EAAE,CAAC;SACb,MAAM,EAAE;SACR,GAAG,EAAE;SACL,OAAO,CAAC,wDAAwD,CAAC;SACjE,QAAQ,CACP,mMAAmM,CACpM;IACH,gBAAgB,EAAE,CAAC,CAAC,MAAM;SACvB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,OAAO,CAAC,MAAM,CAAC;SACf,QAAQ,CAAC,sCAAsC,CAAC;IACnD,iBAAiB,EAAE,CAAC,CAAC,MAAM;SACxB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,OAAO,CAAC,OAAO,CAAC;SAChB,QAAQ,CACP,oRAAoR,CACrR;IACH,aAAa,EAAE,CAAC,CAAC,MAAM;SACpB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,OAAO,CAAC,UAAU,CAAC;SACnB,QAAQ,CACP,qLAAqL,CACtL;IACH,aAAa,EAAE,CAAC,CAAC,MAAM;SACpB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,OAAO,CAAC,OAAO,CAAC;SAChB,QAAQ,CACP,mNAAmN,CACpN;IACH,YAAY,EAAE,CAAC,CAAC,MAAM;SACnB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,OAAO,CAAC,UAAU,CAAC;SACnB,QAAQ,CACP,0QAA0Q,CAC3Q;CACJ,CAAC,CAAC;AAEH,IAAI,OAAuD,CAAC;AAE5D,MAAM,UAAU,eAAe;IAC7B,OAAO,KAAK,cAAc,CAAC,kBAAkB,EAAE;QAC7C,OAAO,EAAE,mBAAmB;QAC5B,aAAa,EAAE,0BAA0B;QACzC,gBAAgB,EAAE,6BAA6B;QAC/C,iBAAiB,EAAE,8BAA8B;QACjD,aAAa,EAAE,2BAA2B;QAC1C,aAAa,EAAE,0BAA0B;QACzC,YAAY,EAAE,yBAAyB;KACxC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"server-config.js","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAE/D,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,OAAO,EAAE,CAAC;SACP,MAAM,EAAE;SACR,GAAG,EAAE;SACL,OAAO,CAAC,iDAAiD,CAAC;SAC1D,QAAQ,CAAC,uBAAuB,CAAC;IACpC,aAAa,EAAE,CAAC;SACb,MAAM,EAAE;SACR,GAAG,EAAE;SACL,OAAO,CAAC,wDAAwD,CAAC;SACjE,QAAQ,CACP,mMAAmM,CACpM;IACH,gBAAgB,EAAE,CAAC,CAAC,MAAM;SACvB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,OAAO,CAAC,MAAM,CAAC;SACf,QAAQ,CAAC,sCAAsC,CAAC;IACnD,iBAAiB,EAAE,CAAC,CAAC,MAAM;SACxB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,OAAO,CAAC,OAAO,CAAC;SAChB,QAAQ,CACP,oRAAoR,CACrR;IACH,aAAa,EAAE,CAAC,CAAC,MAAM;SACpB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,OAAO,CAAC,UAAU,CAAC;SACnB,QAAQ,CACP,qLAAqL,CACtL;IACH,aAAa,EAAE,CAAC,CAAC,MAAM;SACpB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,OAAO,CAAC,OAAO,CAAC;SAChB,QAAQ,CACP,mNAAmN,CACpN;IACH,YAAY,EAAE,CAAC,CAAC,MAAM;SACnB,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,OAAO,CAAC,UAAU,CAAC;SACnB,QAAQ,CACP,0QAA0Q,CAC3Q;IACH,oBAAoB,EAAE,CAAC;SACpB,UAAU,EAAE;SACZ,OAAO,CAAC,KAAK,CAAC;SACd,QAAQ,CACP,4OAA4O,CAC7O;CACJ,CAAC,CAAC;AAEH,IAAI,OAAuD,CAAC;AAE5D,MAAM,UAAU,eAAe;IAC7B,OAAO,KAAK,cAAc,CAAC,kBAAkB,EAAE;QAC7C,OAAO,EAAE,mBAAmB;QAC5B,aAAa,EAAE,0BAA0B;QACzC,gBAAgB,EAAE,6BAA6B;QAC/C,iBAAiB,EAAE,8BAA8B;QACjD,aAAa,EAAE,2BAA2B;QAC1C,aAAa,EAAE,0BAA0B;QACzC,YAAY,EAAE,yBAAyB;QACvC,oBAAoB,EAAE,iCAAiC;KACxD,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
package/dist/index.js CHANGED
@@ -5,9 +5,11 @@
5
5
  */
6
6
  import { createApp, disabledTool } from '@cyanheads/mcp-ts-core';
7
7
  import { config } from '@cyanheads/mcp-ts-core/config';
8
+ import { getServerConfig } from './config/server-config.js';
8
9
  import { eurostatDatasetResource } from './mcp-server/resources/definitions/eurostat-dataset.resource.js';
9
10
  import { eurostatBrowseThemes } from './mcp-server/tools/definitions/eurostat-browse-themes.tool.js';
10
11
  import { eurostatDataframeDescribe } from './mcp-server/tools/definitions/eurostat-dataframe-describe.tool.js';
12
+ import { eurostatDataframeDrop } from './mcp-server/tools/definitions/eurostat-dataframe-drop.tool.js';
11
13
  import { eurostatDataframeQuery } from './mcp-server/tools/definitions/eurostat-dataframe-query.tool.js';
12
14
  import { eurostatDownloadDataset } from './mcp-server/tools/definitions/eurostat-download-dataset.tool.js';
13
15
  import { eurostatGetDatasetInfo } from './mcp-server/tools/definitions/eurostat-get-dataset-info.tool.js';
@@ -20,17 +22,51 @@ import { initEurostatCatalogueService } from './services/eurostat-catalogue/euro
20
22
  import { initEurostatDataService } from './services/eurostat-data/eurostat-data-service.js';
21
23
  /**
22
24
  * The dataframe tools address tables on a canvas, so without one they have
23
- * nothing to talk to. `disabledTool` keeps them off `tools/list` — clients
24
- * cannot call them — while leaving them visible on the landing page and the
25
- * server card with the variable that turns them on, so an operator can see the
25
+ * nothing to talk to. `disabledTool` keeps a gated tool off `tools/list` —
26
+ * clients cannot call it — while the landing page still renders it with the
27
+ * reason it is off and the setting that turns it on, so an operator can see the
26
28
  * capability exists rather than wondering why the README documents a tool the
27
- * server does not advertise.
29
+ * server does not advertise. The drop tool carries a second gate: a drop cannot
30
+ * be undone, so it stays off until EUROSTAT_DATAFRAME_DROP_ENABLED opts in, and
31
+ * its hint names every setting still missing.
28
32
  */
29
33
  const canvasEnabled = config.canvas.providerType !== 'none';
30
34
  const CANVAS_OFF = {
31
35
  reason: 'This deployment runs without a dataframe canvas, so there are no tables to address.',
32
36
  hint: 'CANVAS_PROVIDER_TYPE=duckdb',
33
37
  };
38
+ const DROP_OFF_HINT = 'EUROSTAT_DATAFRAME_DROP_ENABLED=true';
39
+ /**
40
+ * Read before `createApp()` because the drop tool's registration depends on it.
41
+ * A value that fails to parse is not reported here: `setup()` reads the server
42
+ * config again inside `createApp()`, which prints the configuration banner and
43
+ * exits 1 — thrown at this point, the same error would escape as an uncaught
44
+ * stack trace.
45
+ */
46
+ function dataframeDropEnabled() {
47
+ try {
48
+ return getServerConfig().dataframeDropEnabled;
49
+ }
50
+ catch {
51
+ return false;
52
+ }
53
+ }
54
+ /** The drop tool, or its disabled card naming every setting it still needs. */
55
+ function dataframeDropTool() {
56
+ const dropEnabled = dataframeDropEnabled();
57
+ if (!canvasEnabled) {
58
+ return disabledTool(eurostatDataframeDrop, {
59
+ reason: CANVAS_OFF.reason,
60
+ hint: dropEnabled ? CANVAS_OFF.hint : `${CANVAS_OFF.hint}\n${DROP_OFF_HINT}`,
61
+ });
62
+ }
63
+ return dropEnabled
64
+ ? eurostatDataframeDrop
65
+ : disabledTool(eurostatDataframeDrop, {
66
+ reason: 'Table cleanup is disabled in this deployment, so a staged table leaves only when its canvas expires.',
67
+ hint: DROP_OFF_HINT,
68
+ });
69
+ }
34
70
  await createApp({
35
71
  name: 'eurostat-mcp-server',
36
72
  title: 'eurostat-mcp-server',
@@ -49,6 +85,7 @@ await createApp({
49
85
  eurostatDownloadDataset,
50
86
  canvasEnabled ? eurostatDataframeDescribe : disabledTool(eurostatDataframeDescribe, CANVAS_OFF),
51
87
  canvasEnabled ? eurostatDataframeQuery : disabledTool(eurostatDataframeQuery, CANVAS_OFF),
88
+ dataframeDropTool(),
52
89
  ],
53
90
  resources: [eurostatDatasetResource],
54
91
  prompts: [],
@@ -57,9 +94,12 @@ Workflow: eurostat_search_datasets or eurostat_browse_themes to find a dataset c
57
94
  Apply dimension filters to keep queries small: an unfiltered query on a large dataset is slow and can return an async response error instead of data.
58
95
  The catalogue covers two hosts. Most datasets come from the Eurostat dissemination table of contents; the DS-* codes — detailed trade by CN8, HS, SITC, BEC and CPA, and PRODCOM — come from the Comext host, and every tool routes a DS-* code there by its prefix. Comext collections are large: filter every request (freq included, since the trade flows mix annual and monthly series), and expect eurostat_get_dimension_values to return only the first 2,000 values of a long list inline. A collection on neither host, such as the legacy DS-056120, is not disseminated.
59
96
  Two paths past eurostat_query_dataset's 5,000-observation inline cap. For a slice of a dataset, keep using eurostat_query_dataset: when the match is larger than the cap and this deployment runs a dataframe canvas, the whole match is also staged as a SQL table named in the response. For a whole dataset, use eurostat_download_dataset, which reads the SDMX bulk endpoint instead — about half the bytes — and stages every observation the same way, bounded by a byte budget it reports back. Read either staged table with eurostat_dataframe_query (eurostat_dataframe_describe lists the table and column names); the two stage different column sets, since the bulk endpoint carries dimension codes with no labels. To label a download's codes, pass its canvasId to eurostat_get_dimension_values as canvas_id: the dimension's values are staged beside it as a code/label table to join on.
97
+ Staged tables expire with their canvas. eurostat_dataframe_drop removes one sooner, leaving the canvas and its other tables in place; it is opt-in, listed only where the deployment also sets EUROSTAT_DATAFRAME_DROP_ENABLED=true.
60
98
  The dataframe tools are not listed when the canvas is off. There, narrowing the filters is what brings a capped match into reach, and eurostat_download_dataset retains only its inline preview.`,
61
99
  landing: { requireAuth: false },
62
100
  setup(core) {
101
+ // Parsed here so a bad value fails startup with the configuration banner, not every call.
102
+ getServerConfig();
63
103
  initEurostatCatalogueService(core.config, core.storage);
64
104
  initEurostatDataService(core.config, core.storage);
65
105
  initEurostatBulkService(core.config, core.storage);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AACvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,iEAAiE,CAAC;AAC1G,OAAO,EAAE,oBAAoB,EAAE,MAAM,+DAA+D,CAAC;AACrG,OAAO,EAAE,yBAAyB,EAAE,MAAM,oEAAoE,CAAC;AAC/G,OAAO,EAAE,sBAAsB,EAAE,MAAM,iEAAiE,CAAC;AACzG,OAAO,EAAE,uBAAuB,EAAE,MAAM,kEAAkE,CAAC;AAC3G,OAAO,EAAE,sBAAsB,EAAE,MAAM,kEAAkE,CAAC;AAC1G,OAAO,EAAE,0BAA0B,EAAE,MAAM,sEAAsE,CAAC;AAClH,OAAO,EAAE,oBAAoB,EAAE,MAAM,+DAA+D,CAAC;AACrG,OAAO,EAAE,sBAAsB,EAAE,MAAM,iEAAiE,CAAC;AACzG,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,mDAAmD,CAAC;AAC5F,OAAO,EAAE,4BAA4B,EAAE,MAAM,6DAA6D,CAAC;AAC3G,OAAO,EAAE,uBAAuB,EAAE,MAAM,mDAAmD,CAAC;AAE5F;;;;;;;GAOG;AACH,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,KAAK,MAAM,CAAC;AAC5D,MAAM,UAAU,GAAG;IACjB,MAAM,EAAE,qFAAqF;IAC7F,IAAI,EAAE,6BAA6B;CAC3B,CAAC;AAEX,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,qBAAqB;IAC3B,KAAK,EAAE,qBAAqB;IAC5B;;;;OAIG;IACH,WAAW,EAAE,WAAW;IACxB,KAAK,EAAE;QACL,sBAAsB;QACtB,oBAAoB;QACpB,sBAAsB;QACtB,0BAA0B;QAC1B,oBAAoB;QACpB,uBAAuB;QACvB,aAAa,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,YAAY,CAAC,yBAAyB,EAAE,UAAU,CAAC;QAC/F,aAAa,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,YAAY,CAAC,sBAAsB,EAAE,UAAU,CAAC;KAC1F;IACD,SAAS,EAAE,CAAC,uBAAuB,CAAC;IACpC,OAAO,EAAE,EAAE;IACX,YAAY,EAAE;;;;;iMAKiL;IAC/L,OAAO,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE;IAC/B,KAAK,CAAC,IAAI;QACR,4BAA4B,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACxD,uBAAuB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACnD,uBAAuB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACnD,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -68,7 +68,7 @@ export const eurostatDataframeDescribe = tool('eurostat_dataframe_describe', {
68
68
  async handler(input, ctx) {
69
69
  const canvas = getCanvas();
70
70
  if (!canvas) {
71
- throw ctx.fail('canvas_disabled', undefined, ctx.recoveryFor('canvas_disabled'));
71
+ throw ctx.fail('canvas_disabled');
72
72
  }
73
73
  const instance = await acquireCanvas(canvas, input.canvas_id, ctx);
74
74
  const tables = await instance.describe();
@@ -1 +1 @@
1
- {"version":3,"file":"eurostat-dataframe-describe.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/eurostat-dataframe-describe.tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"eurostat-dataframe-describe.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/eurostat-dataframe-describe.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * @fileoverview Tool for removing one staged table from a Eurostat DataCanvas.
3
+ * @module mcp-server/tools/definitions/eurostat-dataframe-drop.tool
4
+ */
5
+ import { z } from '@cyanheads/mcp-ts-core';
6
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
+ export declare const eurostatDataframeDrop: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
8
+ canvas_id: z.ZodString;
9
+ table_name: z.ZodString;
10
+ }, z.core.$strip>, z.ZodObject<{
11
+ canvasId: z.ZodString;
12
+ tableName: z.ZodString;
13
+ dropped: z.ZodBoolean;
14
+ expiresAt: z.ZodString;
15
+ }, z.core.$strip>, readonly [{
16
+ readonly reason: "canvas_disabled";
17
+ readonly code: JsonRpcErrorCode.ServiceUnavailable;
18
+ readonly when: "This deployment runs without a dataframe canvas, so there are no staged tables to drop.";
19
+ readonly retryable: false;
20
+ readonly recovery: "Nothing is staged without a dataframe canvas, so there is nothing to drop; eurostat_query_dataset answers inline with narrower dimension filters.";
21
+ }, {
22
+ readonly reason: "canvas_not_found";
23
+ readonly code: JsonRpcErrorCode.NotFound;
24
+ readonly when: "The canvas_id is unknown or its lifetime has elapsed.";
25
+ readonly recovery: "An expired canvas has already released every table it held, so nothing is left to drop; otherwise copy the canvasId exactly as eurostat_query_dataset, eurostat_download_dataset, or eurostat_get_dimension_values returned it.";
26
+ }, {
27
+ readonly reason: "identifier_reserved";
28
+ readonly code: JsonRpcErrorCode.ValidationError;
29
+ readonly when: "The table_name is a bare SQL keyword (e.g. \"select\"), which no staged table can carry.";
30
+ readonly recovery: "Call eurostat_dataframe_describe and copy a table name exactly as it is listed.";
31
+ }], {
32
+ readonly notice: z.ZodOptional<z.ZodString>;
33
+ }>;
34
+ //# sourceMappingURL=eurostat-dataframe-drop.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eurostat-dataframe-drop.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/eurostat-dataframe-drop.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAEjD,OAAO,EAAE,gBAAgB,EAAY,MAAM,+BAA+B,CAAC;AAG3E,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;EAsIhC,CAAC"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * @fileoverview Tool for removing one staged table from a Eurostat DataCanvas.
3
+ * @module mcp-server/tools/definitions/eurostat-dataframe-drop.tool
4
+ */
5
+ import { tool, z } from '@cyanheads/mcp-ts-core';
6
+ import { CANVAS_IDENTIFIER_REGEX, CanvasIdSchema } from '@cyanheads/mcp-ts-core/canvas';
7
+ import { JsonRpcErrorCode, McpError } from '@cyanheads/mcp-ts-core/errors';
8
+ import { acquireCanvas, getCanvas } from '../../../services/canvas-accessor.js';
9
+ export const eurostatDataframeDrop = tool('eurostat_dataframe_drop', {
10
+ title: 'Drop Eurostat Dataframe',
11
+ description: 'Remove one table from a Eurostat dataframe canvas, leaving the canvas and every other table on it in place — to free the memory a large eurostat_download_dataset table holds once its analysis is done, or to discard an intermediate result. Pass the table name exactly as eurostat_dataframe_describe lists it, case included. A drop cannot be undone: re-run the tool that staged the table to stage it again. A name with nothing staged under it — never staged, already dropped, or expired — returns dropped: false rather than an error, so repeating a drop is safe.',
12
+ annotations: {
13
+ readOnlyHint: false,
14
+ destructiveHint: true,
15
+ idempotentHint: true,
16
+ openWorldHint: false,
17
+ },
18
+ input: z.object({
19
+ canvas_id: CanvasIdSchema.describe('Canvas identifier returned as canvasId by eurostat_query_dataset, eurostat_download_dataset, or eurostat_get_dimension_values. Identifies the workspace holding the table to drop.'),
20
+ table_name: z
21
+ .string()
22
+ .regex(CANVAS_IDENTIFIER_REGEX, 'Expected a table name exactly as eurostat_dataframe_describe lists it: letters, digits, and underscores, starting with a letter or underscore, at most 63 characters.')
23
+ .describe('Name of the table to drop, exactly as eurostat_dataframe_describe lists it, case included (e.g. "df_a1b2c3d4"): letters, digits, and underscores.'),
24
+ }),
25
+ output: z.object({
26
+ canvasId: z.string().describe('Canvas identifier the drop ran against.'),
27
+ tableName: z.string().describe('Table name the drop was asked to remove, as sent.'),
28
+ dropped: z
29
+ .boolean()
30
+ .describe('True when a table by that name was staged and is now removed. False when nothing by that name was staged at call time — never staged, already dropped, or expired — so the canvas is unchanged.'),
31
+ expiresAt: z
32
+ .string()
33
+ .describe('ISO 8601 timestamp when the canvas expires. The drop slid it forward; the canvas and its other tables remain.'),
34
+ }),
35
+ enrichment: {
36
+ notice: z
37
+ .string()
38
+ .optional()
39
+ .describe('Guidance when nothing by that name was staged, so nothing was dropped. Omitted when a table was dropped.'),
40
+ },
41
+ errors: [
42
+ {
43
+ reason: 'canvas_disabled',
44
+ code: JsonRpcErrorCode.ServiceUnavailable,
45
+ when: 'This deployment runs without a dataframe canvas, so there are no staged tables to drop.',
46
+ retryable: false,
47
+ recovery: 'Nothing is staged without a dataframe canvas, so there is nothing to drop; eurostat_query_dataset answers inline with narrower dimension filters.',
48
+ },
49
+ {
50
+ reason: 'canvas_not_found',
51
+ code: JsonRpcErrorCode.NotFound,
52
+ when: 'The canvas_id is unknown or its lifetime has elapsed.',
53
+ recovery: 'An expired canvas has already released every table it held, so nothing is left to drop; otherwise copy the canvasId exactly as eurostat_query_dataset, eurostat_download_dataset, or eurostat_get_dimension_values returned it.',
54
+ },
55
+ {
56
+ reason: 'identifier_reserved',
57
+ code: JsonRpcErrorCode.ValidationError,
58
+ when: 'The table_name is a bare SQL keyword (e.g. "select"), which no staged table can carry.',
59
+ recovery: 'Call eurostat_dataframe_describe and copy a table name exactly as it is listed.',
60
+ },
61
+ ],
62
+ async handler(input, ctx) {
63
+ const canvas = getCanvas();
64
+ if (!canvas) {
65
+ throw ctx.fail('canvas_disabled');
66
+ }
67
+ /**
68
+ * The canvas words both rejections for staging — re-run the tool that
69
+ * staged the data, choose another table name — which for a drop restores
70
+ * what was meant to go. Re-thrown through `ctx.fail`, they carry this tool's
71
+ * declared recovery instead.
72
+ */
73
+ const asDropFailure = (err) => {
74
+ if (err instanceof McpError && err.data?.reason === 'canvas_not_found') {
75
+ throw ctx.fail('canvas_not_found', err.message, { canvasId: input.canvas_id });
76
+ }
77
+ if (err instanceof McpError && err.data?.reason === 'identifier_reserved') {
78
+ throw ctx.fail('identifier_reserved', `"${input.table_name}" is a reserved SQL keyword, so no staged table carries that name.`, { tableName: input.table_name });
79
+ }
80
+ throw err;
81
+ };
82
+ const instance = await acquireCanvas(canvas, input.canvas_id, ctx).catch(asDropFailure);
83
+ const dropped = await instance.drop(input.table_name).catch(asDropFailure);
84
+ ctx.log.info('Canvas table drop', {
85
+ canvasId: instance.canvasId,
86
+ tableName: input.table_name,
87
+ dropped,
88
+ });
89
+ if (!dropped) {
90
+ ctx.enrich.notice(`Canvas ${instance.canvasId} holds no table named "${input.table_name}", so nothing was dropped. Names match exactly, case included — eurostat_dataframe_describe lists what is staged.`);
91
+ }
92
+ return {
93
+ canvasId: instance.canvasId,
94
+ tableName: input.table_name,
95
+ dropped,
96
+ expiresAt: instance.expiresAt,
97
+ };
98
+ },
99
+ format: (result) => [
100
+ {
101
+ type: 'text',
102
+ text: [
103
+ `# Drop \`${result.tableName}\` (canvas \`${result.canvasId}\`)`,
104
+ `**Dropped:** ${result.dropped} — ${result.dropped
105
+ ? 'removed; the canvas and its other tables are unchanged'
106
+ : 'nothing by that name was staged'}`,
107
+ `**Canvas expires:** ${result.expiresAt}`,
108
+ ].join('\n'),
109
+ },
110
+ ],
111
+ });
112
+ //# sourceMappingURL=eurostat-dataframe-drop.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eurostat-dataframe-drop.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/eurostat-dataframe-drop.tool.ts"],"names":[],"mappings":"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"}
@@ -57,7 +57,7 @@ export const eurostatDataframeQuery = tool('eurostat_dataframe_query', {
57
57
  async handler(input, ctx) {
58
58
  const canvas = getCanvas();
59
59
  if (!canvas) {
60
- throw ctx.fail('canvas_disabled', undefined, ctx.recoveryFor('canvas_disabled'));
60
+ throw ctx.fail('canvas_disabled');
61
61
  }
62
62
  const instance = await acquireCanvas(canvas, input.canvas_id, ctx);
63
63
  /**