io.github.cyanheads/eurostat-mcp-server

Official registry Search and web

Search and query the Eurostat catalogue — EU economy, demography, trade, and NUTS regional data.

What can you do with io.github.cyanheads/eurostat-mcp-server?

  • 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
  • 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 `EX...
  • NUTS geo-level filtering (aggregate / country / nuts1 / nuts2 / nuts3), and OBS_FLAG and CONF_STATUS decoded into separate fields
  • 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
  • A query match above 5,000 observations, or any bulk download, is staged whole 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 w...
  • Typed error contracts: every declared failure carries a reason and a recovery.hint naming the next tool to call
  • Next-step hints: eurostat_search_datasets and eurostat_browse_themes return a nextStep pointing at the follow-up call
  • Unknown stays unknown: counts and period bounds Eurostat doesn't report (obsCount, timeRange.start / end, lastUpdated) are omitted rather than zeroed

Install

Add this to your client's MCP configuration (Claude Desktop: claude_desktop_config.json; Cursor: .cursor/mcp.json; VS Code and others use the same "mcpServers" shape).

Remote (streamable-http)
Claude Code
claude mcp add --transport http eurostat-mcp-server 'https://eurostat.caseyjhand.com/mcp'
JSON configuration (Claude Desktop, Cursor, VS Code)
{
    "mcpServers": {
        "eurostat-mcp-server": {
            "type": "http",
            "url": "https://eurostat.caseyjhand.com/mcp"
        }
    }
}
NPM package @cyanheads/eurostat-mcp-server
Claude Code
claude mcp add eurostat-mcp-server -e 'EUROSTAT_CLAMP_LIMITS=true' -e 'MCP_LOG_LEVEL=info' -e 'EUROSTAT_BASE_URL=https://ec.europa.eu/eurostat/api/dissemination' -e 'EUROSTAT_COMEXT_BASE_URL=https://ec.europa.eu/eurostat/api/comext/dissemination' -e 'EUROSTAT_REQUEST_TIMEOUT_MS=30000' -e 'EUROSTAT_METADATA_TIMEOUT_MS=120000' -e 'EUROSTAT_TOC_CACHE_TTL_MS=43200000' -e 'EUROSTAT_BULK_TIMEOUT_MS=120000' -e 'EUROSTAT_BULK_MAX_BYTES=52428800' -e 'CANVAS_PROVIDER_TYPE=duckdb' -e 'CANVAS_TEMP_PATH=<CANVAS_TEMP_PATH>' -e 'CANVAS_TTL_MS=86400000' -e 'CANVAS_DEFAULT_ROW_LIMIT=10000' -e 'EUROSTAT_DATAFRAME_DROP_ENABLED=false' -- npx -y @cyanheads/eurostat-mcp-server@0.11.1
JSON configuration (Claude Desktop, Cursor, VS Code)
{
    "mcpServers": {
        "eurostat-mcp-server": {
            "command": "npx",
            "args": [
                "-y",
                "@cyanheads/eurostat-mcp-server@0.11.1"
            ],
            "env": {
                "EUROSTAT_CLAMP_LIMITS": "true",
                "MCP_LOG_LEVEL": "info",
                "EUROSTAT_BASE_URL": "https://ec.europa.eu/eurostat/api/dissemination",
                "EUROSTAT_COMEXT_BASE_URL": "https://ec.europa.eu/eurostat/api/comext/dissemination",
                "EUROSTAT_REQUEST_TIMEOUT_MS": "30000",
                "EUROSTAT_METADATA_TIMEOUT_MS": "120000",
                "EUROSTAT_TOC_CACHE_TTL_MS": "43200000",
                "EUROSTAT_BULK_TIMEOUT_MS": "120000",
                "EUROSTAT_BULK_MAX_BYTES": "52428800",
                "CANVAS_PROVIDER_TYPE": "duckdb",
                "CANVAS_TEMP_PATH": "<CANVAS_TEMP_PATH>",
                "CANVAS_TTL_MS": "86400000",
                "CANVAS_DEFAULT_ROW_LIMIT": "10000",
                "EUROSTAT_DATAFRAME_DROP_ENABLED": "false"
            }
        }
    }
}

Environment variables

  • EUROSTAT_CLAMP_LIMITS Clamp positive integer preview/search/dimension limits to 500/100/1000 with a notice. False restores schemas rejecting over-cap inputs.
  • MCP_LOG_LEVEL Sets the minimum log level for output (e.g., 'debug', 'info', 'warn').
  • EUROSTAT_BASE_URL Eurostat API base URL. Override when using a mirror or proxy.
  • EUROSTAT_COMEXT_BASE_URL Base URL of the Comext dissemination host, which serves every DS-* dataset code (detailed trade and PRODCOM). Override when using a mirror or proxy.
  • EUROSTAT_REQUEST_TIMEOUT_MS Total allowance for one Eurostat request across retries, backoff and body consumption, in milliseconds.
  • EUROSTAT_METADATA_TIMEOUT_MS Total allowance for one dataset's SDMX dataflow structure across retries, backoff and body consumption, in milliseconds. Defaults to 120000; separate from EUROSTAT_REQUEST_TIMEOUT_MS for large structures.
  • EUROSTAT_TOC_CACHE_TTL_MS How long a fetched catalogue TOC stays usable before the next catalogue call refreshes it, in milliseconds. Defaults to 12 hours.
  • EUROSTAT_BULK_TIMEOUT_MS Total allowance for one eurostat_download_dataset transfer across request retries, backoff and streaming body consumption, in milliseconds. Defaults to 120000.
  • EUROSTAT_BULK_MAX_BYTES Byte budget for one eurostat_download_dataset transfer, counted 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 when it is reached. Defaults to 50 MiB.
  • CANVAS_PROVIDER_TYPE Dataframe canvas backend. The default, 'duckdb', lists eurostat_dataframe_describe and eurostat_dataframe_query, stages query matches with stage=true or above 5,000 observations, and retains bulk downloads beyond their previews. Set to 'none' to disable the canvas and those tools. The DuckDB binding ships with the server.
  • CANVAS_TEMP_PATH Parent directory for DuckDB canvas spill files: a private 'mcp-canvas-XXXXXX' directory is created inside it, so it must be writable by the server process. Defaults to the OS temp directory.
  • CANVAS_TTL_MS Sliding lifetime of a staged dataframe canvas in milliseconds; every call against it extends the window. Defaults to 24 hours.
  • CANVAS_DEFAULT_ROW_LIMIT Maximum complete rows a single eurostat_dataframe_query returns before reporting truncated.
  • EUROSTAT_DATAFRAME_DROP_ENABLED Set to 'true' to list eurostat_dataframe_drop, which removes one staged table from a dataframe canvas and leaves its other tables in place. Off by default because a drop cannot be undone; staged tables expire with their canvas either way. Has no effect with CANVAS_PROVIDER_TYPE=none.
NPM package @cyanheads/eurostat-mcp-server
Claude Code
claude mcp add eurostat-mcp-server -e 'MCP_TRANSPORT_TYPE=<MCP_TRANSPORT_TYPE>' -e 'EUROSTAT_CLAMP_LIMITS=true' -e 'MCP_HTTP_HOST=127.0.0.1' -e 'MCP_HTTP_PORT=3010' -e 'MCP_HTTP_ENDPOINT_PATH=/mcp' -e 'MCP_PUBLIC_URL=<MCP_PUBLIC_URL>' -e 'MCP_AUTH_MODE=none' -e 'MCP_LOG_LEVEL=info' -e 'EUROSTAT_BASE_URL=https://ec.europa.eu/eurostat/api/dissemination' -e 'EUROSTAT_COMEXT_BASE_URL=https://ec.europa.eu/eurostat/api/comext/dissemination' -e 'EUROSTAT_REQUEST_TIMEOUT_MS=30000' -e 'EUROSTAT_METADATA_TIMEOUT_MS=120000' -e 'EUROSTAT_TOC_CACHE_TTL_MS=43200000' -e 'EUROSTAT_BULK_TIMEOUT_MS=120000' -e 'EUROSTAT_BULK_MAX_BYTES=52428800' -e 'CANVAS_PROVIDER_TYPE=duckdb' -e 'CANVAS_TEMP_PATH=<CANVAS_TEMP_PATH>' -e 'CANVAS_TTL_MS=86400000' -e 'CANVAS_DEFAULT_ROW_LIMIT=10000' -e 'EUROSTAT_DATAFRAME_DROP_ENABLED=false' -- npx -y @cyanheads/eurostat-mcp-server@0.11.1
JSON configuration (Claude Desktop, Cursor, VS Code)
{
    "mcpServers": {
        "eurostat-mcp-server": {
            "command": "npx",
            "args": [
                "-y",
                "@cyanheads/eurostat-mcp-server@0.11.1"
            ],
            "env": {
                "MCP_TRANSPORT_TYPE": "<MCP_TRANSPORT_TYPE>",
                "EUROSTAT_CLAMP_LIMITS": "true",
                "MCP_HTTP_HOST": "127.0.0.1",
                "MCP_HTTP_PORT": "3010",
                "MCP_HTTP_ENDPOINT_PATH": "/mcp",
                "MCP_PUBLIC_URL": "<MCP_PUBLIC_URL>",
                "MCP_AUTH_MODE": "none",
                "MCP_LOG_LEVEL": "info",
                "EUROSTAT_BASE_URL": "https://ec.europa.eu/eurostat/api/dissemination",
                "EUROSTAT_COMEXT_BASE_URL": "https://ec.europa.eu/eurostat/api/comext/dissemination",
                "EUROSTAT_REQUEST_TIMEOUT_MS": "30000",
                "EUROSTAT_METADATA_TIMEOUT_MS": "120000",
                "EUROSTAT_TOC_CACHE_TTL_MS": "43200000",
                "EUROSTAT_BULK_TIMEOUT_MS": "120000",
                "EUROSTAT_BULK_MAX_BYTES": "52428800",
                "CANVAS_PROVIDER_TYPE": "duckdb",
                "CANVAS_TEMP_PATH": "<CANVAS_TEMP_PATH>",
                "CANVAS_TTL_MS": "86400000",
                "CANVAS_DEFAULT_ROW_LIMIT": "10000",
                "EUROSTAT_DATAFRAME_DROP_ENABLED": "false"
            }
        }
    }
}

Environment variables

  • MCP_TRANSPORT_TYPE Selects the HTTP transport.
  • EUROSTAT_CLAMP_LIMITS Clamp positive integer preview/search/dimension limits to 500/100/1000 with a notice. False restores schemas rejecting over-cap inputs.
  • MCP_HTTP_HOST The hostname for the HTTP server.
  • MCP_HTTP_PORT The port to run the HTTP server on.
  • MCP_HTTP_ENDPOINT_PATH The endpoint path for the MCP server.
  • MCP_PUBLIC_URL Public origin override for deployments behind a TLS-terminating reverse proxy (e.g. https://mcp.example.com).
  • MCP_AUTH_MODE Authentication mode to use: 'none', 'jwt', or 'oauth'.
  • MCP_LOG_LEVEL Sets the minimum log level for output (e.g., 'debug', 'info', 'warn').
  • EUROSTAT_BASE_URL Eurostat API base URL. Override when using a mirror or proxy.
  • EUROSTAT_COMEXT_BASE_URL Base URL of the Comext dissemination host, which serves every DS-* dataset code (detailed trade and PRODCOM). Override when using a mirror or proxy.
  • EUROSTAT_REQUEST_TIMEOUT_MS Total allowance for one Eurostat request across retries, backoff and body consumption, in milliseconds.
  • EUROSTAT_METADATA_TIMEOUT_MS Total allowance for one dataset's SDMX dataflow structure across retries, backoff and body consumption, in milliseconds. Defaults to 120000; separate from EUROSTAT_REQUEST_TIMEOUT_MS for large structures.
  • EUROSTAT_TOC_CACHE_TTL_MS How long a fetched catalogue TOC stays usable before the next catalogue call refreshes it, in milliseconds. Defaults to 12 hours.
  • EUROSTAT_BULK_TIMEOUT_MS Total allowance for one eurostat_download_dataset transfer across request retries, backoff and streaming body consumption, in milliseconds. Defaults to 120000.
  • EUROSTAT_BULK_MAX_BYTES Byte budget for one eurostat_download_dataset transfer, counted 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 when it is reached. Defaults to 50 MiB.
  • CANVAS_PROVIDER_TYPE Dataframe canvas backend. The default, 'duckdb', lists eurostat_dataframe_describe and eurostat_dataframe_query, stages query matches with stage=true or above 5,000 observations, and retains bulk downloads beyond their previews. Set to 'none' to disable the canvas and those tools. The DuckDB binding ships with the server.
  • CANVAS_TEMP_PATH Parent directory for DuckDB canvas spill files: a private 'mcp-canvas-XXXXXX' directory is created inside it, so it must be writable by the server process. Defaults to the OS temp directory.
  • CANVAS_TTL_MS Sliding lifetime of a staged dataframe canvas in milliseconds; every call against it extends the window. Defaults to 24 hours.
  • CANVAS_DEFAULT_ROW_LIMIT Maximum complete rows a single eurostat_dataframe_query returns before reporting truncated.
  • EUROSTAT_DATAFRAME_DROP_ENABLED Set to 'true' to list eurostat_dataframe_drop, which removes one staged table from a dataframe canvas and leaves its other tables in place. Off by default because a drop cannot be undone; staged tables expire with their canvas either way. Has no effect with CANVAS_PROVIDER_TYPE=none.

Documentation

From the project's README on GitHub, refreshed weekly. Links open on GitHub.

Contents
  1. @cyanheads/eurostat-mcp-server
  2. Overview
  3. Capability reference
  4. Features
  5. Getting started
  6. Configuration
  7. Running the server
  8. Project structure
  9. Development guide
  10. Contributing
  11. License

@cyanheads/eurostat-mcp-server

Search and query the Eurostat catalogue — EU economy, demography, trade, health, and NUTS regional data via MCP. STDIO or Streamable HTTP.

6 Tools (up to 9 with the dataframe canvas) • 1 Resource

Public Hosted Server: https://eurostat.caseyjhand.com/mcp


Overview

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.

Tools

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.

ToolDescription
eurostat_search_datasetsSearch the Eurostat catalogue by keyword — returns codes, descriptions, period coverage, and theme breadcrumbs
eurostat_browse_themesNavigate the Eurostat theme hierarchy — list root themes or drill into subthemes and datasets
eurostat_get_dataset_infoFetch dataset metadata: dimensions with sample values, time range, observation count, and last-update date
eurostat_get_dimension_valuesList all valid codes for one dataset dimension, paged, with a code/label word filter and NUTS hierarchy filtering for geo
eurostat_query_datasetFetch a bounded preview of decoded observations with dimension filters, NUTS geo-level, and time-range controls
eurostat_download_datasetDownload a whole dataset via the SDMX bulk endpoint and stage every observation on the dataframe canvas
eurostat_dataframe_describeList the tables staged on a dataframe canvas, with row counts and column types
eurostat_dataframe_queryRun a read-only SQL SELECT across staged tables
eurostat_dataframe_dropRemove one staged table from a dataframe canvas, leaving its other tables in place (opt-in)
Resources
ResourceDescription
eurostat://dataset/{dataset_code}Dataset metadata (dimensions, time range, observation count, last-updated) by URI

The same metadata is available through eurostat_get_dataset_info for tool-only clients.

Capability reference

eurostat_search_datasets tool
  • 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
  • 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
  • If the Comext dataflow list cannot be loaded, results carry comextListMissing and a notice; a catalogue that could not be refreshed carries catalogueLoadedAt

eurostat_browse_themes tool
  • 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)
  • 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
  • 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

eurostat_get_dataset_info tool
  • One dataset_code; returns every dimension with valuesCount and up to 10 sampleValues, plus timeRange, obsCount, lastUpdated, and the ESMS metadataUrl
  • 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)

eurostat_get_dimension_values tool
  • dataset_code plus dimension; 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
  • Long lists come back in pages of up to 64,000 bytes (or an optional limit), with totalCount for every value; a page with more after it carries truncated, nextCursor (pass back as cursor), and a notice. Fails as not_found, no_results, conflicting_params, invalid_cursor, upstream_fault (retryable when the endpoint is down), or canvas_not_found
  • 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

eurostat_query_dataset tool
  • 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 50), clamped to 500 with a notice; lang is EN, FR, or DE
  • 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
  • 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

eurostat_download_dataset tool
  • 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 50), clamped to 500 with a notice
  • 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
  • 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.

eurostat_dataframe_describe tool
  • 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
  • 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

eurostat_dataframe_query tool
  • One read-only SELECT per call; chained statements, other verbs, and functions that read files or external data are rejected
  • Returns columns, rows, rowCount, and required boolean truncated. Whole rows fit within CANVAS_DEFAULT_ROW_LIMIT (default 10,000) and 64,000 bytes independently for JSON-serialized structuredContent and content[], including enrichment. At least one complete row is retained even if oversized. 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
  • 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

eurostat_dataframe_drop tool
  • 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
  • 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

eurostat://dataset/{dataset_code} resource
  • Same payload as eurostat_get_dataset_info, returned as application/json; a failed read carries the same reason, and retryable on an outage
  • dataset_code comes from eurostat_search_datasets or eurostat_browse_themes

Features

Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.

Eurostat-specific:

  • 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
  • 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
  • NUTS geo-level filtering (aggregate / country / nuts1 / nuts2 / nuts3), and OBS_FLAG and CONF_STATUS decoded into separate fields
  • 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
  • A query match above 5,000 observations, or any bulk download, is staged whole 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

Agent-friendly output:

  • Typed error contracts: every declared failure carries a reason and a recovery.hint naming the next tool to call
  • Next-step hints: eurostat_search_datasets and eurostat_browse_themes return a nextStep pointing at the follow-up call
  • Unknown stays unknown: counts and period bounds Eurostat doesn't report (obsCount, timeRange.start / end, lastUpdated) are omitted rather than zeroed
  • 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

Getting started

Public Hosted Instance

A public instance is available at https://eurostat.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:

{
  "mcpServers": {
    "eurostat-mcp-server": {
      "type": "streamable-http",
      "url": "https://eurostat.caseyjhand.com/mcp"
    }
  }
}
Self-Hosted / Local

Add the following to your MCP client configuration file.

{
  "mcpServers": {
    "eurostat-mcp-server": {
      "type": "stdio",
      "command": "bunx",
      "args": ["@cyanheads/eurostat-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with npx (no Bun required):

{
  "mcpServers": {
    "eurostat-mcp-server": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@cyanheads/eurostat-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

Or with Docker:

{
  "mcpServers": {
    "eurostat-mcp-server": {
      "type": "stdio",
      "command": "docker",
      "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/eurostat-mcp-server:latest"]
    }
  }
}

For Streamable HTTP, set the transport and start the server:

MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
Prerequisites
  • Bun v1.4.0 or higher (or Node.js v24+).
  • No API key: Eurostat's dissemination API and its Comext host are public.
Installation
  1. Clone the repository:
git clone https://github.com/cyanheads/eurostat-mcp-server.git
  1. Navigate into the directory:
cd eurostat-mcp-server
  1. Install dependencies:
bun install
  1. Configure environment (optional, every setting has a default):
cp .env.example .env

Configuration

VariableDescriptionDefault
EUROSTAT_BASE_URLEurostat API base URL.https://ec.europa.eu/eurostat/api/dissemination
EUROSTAT_COMEXT_BASE_URLComext dissemination host, which serves every DS-* dataset code (detailed trade and PRODCOM).https://ec.europa.eu/eurostat/api/comext/dissemination
EUROSTAT_REQUEST_TIMEOUT_MSTotal allowance for one request, including retries, backoff and body reads, in ms.30000
EUROSTAT_METADATA_TIMEOUT_MSTotal allowance for one dataset's SDMX dataflow structure, including retries, backoff and body reads, in ms.120000 (2 min)
EUROSTAT_TOC_CACHE_TTL_MSCatalogue TOC cache lifetime, in ms.43200000 (12 h)
EUROSTAT_BULK_TIMEOUT_MSTotal allowance for one bulk transfer, including request retries, backoff and streaming, in ms.120000 (2 min)
EUROSTAT_BULK_MAX_BYTESByte budget for one bulk download.52428800 (50 MiB)
CANVAS_PROVIDER_TYPEDataframe canvas backend; none disables the canvas and its tools. The .mcpb bundle sets none.duckdb
CANVAS_TTL_MSSliding lifetime of a staged canvas, in ms.86400000 (24 h)
CANVAS_DEFAULT_ROW_LIMITMax rows one eurostat_dataframe_query returns before reporting truncated; a separate 64,000-byte budget applies to each serialized response surface, retaining at least one complete row.10000
EUROSTAT_CLAMP_LIMITSClamp positive integer preview/search limits to 500/100 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
EUROSTAT_DATAFRAME_DROP_ENABLEDtrue lists eurostat_dataframe_drop; no effect with CANVAS_PROVIDER_TYPE=none.false
MCP_TRANSPORT_TYPETransport: stdio or http.stdio
MCP_HTTP_PORTHTTP server port.3010
MCP_SESSION_MODEHTTP session mode: stateless, stateful, or auto. The server declares stateless; a set value overrides it.stateless
MCP_AUTH_MODEAuthentication: none, jwt, or oauth.none
MCP_LOG_LEVELLog level (debug, info, warning, error, etc.). The Docker image sets info.debug
LOGS_DIRDirectory for log files (Node.js only).<project-root>/logs
LOG_TOOL_FAILURE_PAYLOADSLog 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
STORAGE_PROVIDER_TYPEStorage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1.in-memory
OTEL_ENABLEDEnable OpenTelemetry.false

See .env.example for the full list of optional overrides.

Running the server

Local development
  • Build and run the production version:
    # One-time build
    bun run rebuild
    
    # Run the built server
    bun run start:http
    # or
    bun run start:stdio
  • Run checks and tests:
    bun run devcheck  # Lints, formats, type-checks, and more
    bun run test      # Runs the test suite
    bun run lint:mcp  # Validates MCP definitions against spec
Docker
docker build -t eurostat-mcp-server .
docker run --rm -p 3010:3010 eurostat-mcp-server

The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/eurostat-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.

Project structure

DirectoryPurpose
src/index.tscreateApp() entry point — registers tools and the resource, inits services, gates the dataframe tools on the canvas and the drop tool on its own flag.
src/mcp-server/toolsTool definitions (*.tool.ts). Six discovery and data tools, plus three canvas-gated dataframe tools, one of them opt-in.
src/mcp-server/resourcesResource definitions. Dataset metadata resource.
src/services/eurostat-catalogueCatalogue service — fetches and parses the Eurostat TOC, merges the Comext dataflow list into it, with a TTL-bounded in-memory cache.
src/services/eurostat-dataStatistics API service — SDMX metadata parsing with a per-dataset cache, JSON-stat 2.0 decoding, async-response detection, canvas row source.
src/services/eurostat-bulkSDMX 2.1 TSV bulk service — streaming download, byte budget, SOAP fault mapping, canvas row source.
src/services/eurostat-codelists.tsOBS_FLAG and CONF_STATUS codelists, the measure column names both stagers write, and the text-value decoder.
src/services/eurostat-hosts.tsWhich host serves a dataset code: DS-* to the Comext host, everything else to the main one.
src/services/canvas-accessor.tsAccessor for the optional DataCanvas and the acquire helper canvas-touching tools share.
src/services/shared-load.tsLets concurrent callers await one shared download, so one caller's cancellation releases only that caller.
src/configServer-specific environment variable parsing and validation with Zod.
tests/Unit and integration tests, mirroring the src/ structure.

Development guide

See CLAUDE.md for development guidelines and architectural rules. The short version:

  • Handlers throw, framework catches — no try/catch in tool logic
  • Use ctx.log for logging, ctx.state for storage
  • Register new tools and resources in the createApp() arrays in src/index.ts
  • Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields

Contributing

Issues are welcome. Run checks and tests before submitting:

bun run devcheck
bun run test

License

This project is licensed under the Apache 2.0 License. See the LICENSE file for details.

Frequently asked questions

What is io.github.cyanheads/eurostat-mcp-server?

Search and query the Eurostat catalogue — EU economy, demography, trade, and NUTS regional data.

How do I install io.github.cyanheads/eurostat-mcp-server?

It runs as a remote server. In Claude Code: claude mcp add --transport http eurostat-mcp-server 'https://eurostat.caseyjhand.com/mcp'. For other clients, paste the JSON configuration from this page into their MCP settings.

Does io.github.cyanheads/eurostat-mcp-server need an API key?

Its registry entry lists no required key or secret. Some features may still need an account with the service it connects to.

Is io.github.cyanheads/eurostat-mcp-server open source?

Yes. Its code is on GitHub under the Apache-2.0 license.

Who maintains io.github.cyanheads/eurostat-mcp-server?

It is published by cyanheads. AI.tn lists it but does not maintain or endorse it.

From the official MCP Registry. Listing is not an endorsement: review a server's code and permissions before giving it access to your data.