mcpField
πŸ“Š
πŸ“Š Statistics Canada API MCP Server

πŸ“Š Statistics Canada API MCP Server

Remote
mcp-statcan.onrender.comDeveloper Tools7Updated 2d ago

About πŸ“Š Statistics Canada API MCP Server

A Statistics Canada MCP server and standalone CLI for searching tables and retrieving Canadian statistical data through WDS and SDMX. It supports hosted or local use, with optional SQLite tools in the full local setup.

Key features

  1. 01Search Statistics Canada tables
  2. 02Inspect table metadata and dimensions
  3. 03Fetch filtered SDMX observations
  4. 04Run read-only SQL queries on fetched data
  5. 05Download CSV or JSON with the CLI

Use cases

  1. 01Find tables related to a research topic
  2. 02Retrieve selected time-series observations
  3. 03Compare or rank data using server-side SQL
  4. 04Download table data for terminal analysis

MCP support

The server exposes WDS discovery and SDMX data tools to MCP clients, plus prompts for common lookup and download workflows. SQLite tools are available in the full local setup; the standalone statcan CLI can also download data without an LLM.

Configuration

Remote servers need no local install. Pick your client, then copy the config or install in one click.

Endpointhttps://mcp-statcan.com/mcp
Copy install command
Claude CodeTerminal
Cursor.cursor/mcp.json
VS Code.vscode/mcp.json
Claude Desktopclaude_desktop_config.json
Windsurf~/.codeium/windsurf/mcp_config.json

Run commands in a terminal; paste JSON into the client’s MCP config and restart. Servers that need sign-in will prompt on first connect.

Packages
pypistatcan-mcp-server0.17.0

Tools

25

This tool takes no parameters.

Details

From the project README

Statistics Canada MCP Server

Statistics Canada MCP Server

Python 3.10+ License: MIT MCP GitHub SafeSkill 93/100

MCP server and CLI for Statistics Canada's Web Data Service (WDS) and SDMX REST API. Gives any MCP client β€” Claude, Cursor, VS Code Copilot, Gemini β€” structured access to Canadian statistical data. Includes a standalone statcan CLI for direct downloads without an LLM.

Hosted on Render β€” no install required for most users. See Quick Start.

⚠️ LLMs may fabricate data. Always verify important figures against official Statistics Canada sources.


Table of Contents


Quick Start

Pick the option that fits you. You don't need to install anything for Option 1.

Option 1 β€” Use the hosted server (recommended)

Connect directly to the public server on Render. No uv, no terminal, no local setup.

Claude Desktop / Claude.ai

  1. Open Settings β†’ Connectors β†’ Add Custom Connector
  2. Name: mcp-statcan
  3. URL: https://mcp-statcan.com/mcp
  4. Save and restart

Claude Code

claude mcp add statcan --transport http https://mcp-statcan.com/mcp --scope global

The hosted server provides all WDS + SDMX tools. Database tools (SQLite) require local setup (Option 3) β€” they are intentionally excluded from the shared server.


Option 2 β€” Self-host HTTP (WDS + SDMX, no DB)

Run a local server with the same tools as the hosted version.

Step 1 β€” Install uv:

# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows (PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Step 2 β€” Start the server:

uvx statcan-mcp-server --transport http
# Listening at http://localhost:8000

Step 3 β€” Connect your client to http://localhost:8000/mcp.


Option 3 β€” Full local setup (WDS + SDMX + SQLite)

Everything from Option 2, plus database tools for storing and querying data with SQL. Runs via stdio.

Step 1 β€” Install uv (same as above).

Step 2 β€” Configure your client with the stdio snippets in Setup by Client below.

uvx downloads and runs the server automatically on first use.


Option 4 β€” statcan CLI (no LLM needed)

Download StatCan data directly from the terminal. See statcan CLI.

uvx statcan-mcp-server        # installs the package
statcan search "labour force"
statcan metadata 14-10-0287-01                                    # run first to find dimension IDs
statcan download 14-10-0287-01 --key "1.1.1" --last 12 --output lfs.csv  # run `statcan metadata <pid>` first to find real dimension IDs
statcan describe                                                  # machine-readable command reference for agents

Examples

Chat examples
DatasetQueryDemoSource
Canada's Greenhouse Gas Emissions"Create a simple visualization for greenhouse emissions for Canada as a whole over the last 4 years"ChatTable 38-10-0097-01
Canada's International Trade in Services"Create a quick analysis for international trade in services for the last 6 months with a visualization"ChatTable 12-10-0144-01
Ontario Building Construction Price Index"Generate a visualization for Ontario's Building Price index from Q4 2023 to Q4 2024"ChatTable 18-10-0289-01
Canadian Unemployment Dashboard"Create a Canadian Unemployment Dashboard using statcan mcp"ChatTable 14-10-0287-01
Dashboard examples
TitleLinkSource
Canada's Critical Minerals EconomyDashboardTable 36-10-0708-01
Price of Everything: CPI Dashboard 2015–2026DashboardTable 18-10-0004-01
Canada's Biomedical & Biotech IndustriesDashboardTable 27-10-0297-01

Setup by Client

Hosted server (Option 1)

Claude Desktop β€” Settings β†’ Connectors β†’ Add Custom Connector

  • Name: mcp-statcan
  • URL: https://mcp-statcan.com/mcp

Claude Code

claude mcp add statcan --transport http https://mcp-statcan.com/mcp --scope global

Cursor β€” .cursor/mcp.json (project) or ~/.cursor/mcp.json (global):

{
  "mcpServers": {
    "statcan": {
      "url": "https://mcp-statcan.com/mcp"
    }
  }
}

VS Code (GitHub Copilot) β€” .vscode/mcp.json:

{
  "servers": {
    "statcan": {
      "type": "http",
      "url": "https://mcp-statcan.com/mcp"
    }
  }
}

Self-hosted HTTP (Option 2)

Start uvx statcan-mcp-server --transport http first, then configure your client.

Most clients need mcp-proxy to bridge stdio ↔ HTTP. Claude Code connects natively.

Claude Desktop β€” Settings β†’ Developer β†’ Edit Config:

{
  "mcpServers": {
    "statcan": {
      "command": "uvx",
      "args": ["mcp-proxy", "--transport", "streamablehttp", "http://localhost:8000/mcp"]
    }
  }
}

Claude Code

claude mcp add statcan --transport http http://localhost:8000/mcp --scope global

Cursor / VS Code / Gemini β€” same mcp-proxy wrapper, pointing to http://localhost:8000/mcp.


Full local / stdio (Option 3)

Claude Desktop β€” Settings β†’ Developer β†’ Edit Config:

{
  "mcpServers": {
    "statcan": {
      "command": "uvx",
      "args": ["statcan-mcp-server", "--db-path", "/Users/<you>/.statcan-mcp/statcan_data.db"]
    }
  }
}

Pass --db-path with an absolute path. Claude Desktop overrides the subprocess HOME env var, which can break default path resolution.

Claude Code

claude mcp add statcan --scope global -- uvx statcan-mcp-server

Cursor / VS Code / Gemini β€” use uvx statcan-mcp-server as the stdio command.


How Claude.ai Uses This Server

Claude.ai (web) has no bash sandbox β€” it can't run shell commands. Instead, it uses MCP tools for discovery and its Python script tool to fetch data without bloating the context window.

The pattern:

1. MCP tools (small payloads β€” metadata only):
   search_cubes_by_title("labour force")  β†’ productId
   get_sdmx_structure(productId=...)      β†’ dimension layout + codes
   get_sdmx_key_for_dimension(...)        β†’ OR key for large dimensions

2. Python script (data never enters context):
   url = "https://mcp-statcan.com/files/sdmx/<pid>/<key>?lastNObservations=12"
   β†’ validate URL domain β†’ write to ./statcan_<pid>.csv β†’ print summary only

3. Follow-up script (analysis from local file):
   rows = list(csv.DictReader(open("./statcan_<pid>.csv")))
   β†’ filter / sort / aggregate β†’ print only the result

For questions that need computation (rank, compare, growth), query_sdmx runs the SQL on the server and returns only the answer, so raw rows never enter the context window.

Claude Code (bash sandbox) uses the statcan CLI instead:

statcan search "labour force"
statcan metadata 14-10-0287-01  # run first to find dimension IDs
statcan download 14-10-0287-01 --key "1.1.1" --last 12 --output ./lfs.csv  # run `statcan metadata <pid>` first to find real dimension IDs
awk -F',' 'NR>1 && $1=="Canada"' ./lfs.csv | sort -t',' -rn -k5 | head -10

MCP Prompts

The server ships five prompts accessible as slash commands in supported clients. Each has dual instructions β€” Claude Code (bash) and Claude.ai web (Python script).

PromptWhat it teaches
/statcan-data-lookupEnd-to-end: search β†’ structure β†’ build key β†’ fetch to local file β†’ analyze
/sdmx-key-builderSDMX key syntax: wildcards, OR keys, time parameters, download URL format
/statcan-downloadDownload a specific table: CLI commands + Python script alternative
/statcan-exploreSample before committing: 3-period fetch, column layout, size estimate
/statcan-vector-pipelineMulti-series download and cross-series comparison

Usage in Claude Code:

/statcan-data-lookup topic="consumer price index" analysis_goal="trend last 5 years"
/statcan-download product_id=18100004 last_n=24

statcan CLI

A standalone CLI for downloading StatCan data without an LLM. Outputs pipe-friendly CSV/JSON to stdout; progress and errors go to stderr.

Exploring a table (search, then metadata, then download):

Terminal recording: statcan search, statcan metadata and statcan download for the Consumer Price Index table

Agent mode (--json envelopes, typed exit codes, statcan describe):

Terminal recording: statcan --json download writes a CSV and prints a summary envelope; a missing product returns a JSON error with exit code 3; statcan describe lists exit codes

Recordings are generated from the scripts in assets/tapes/ with VHS: vhs assets/tapes/cli-agent.tape.

Install:

pip install statcan-mcp-server   # or: uvx statcan-mcp-server (no install)

Commands:

statcan search <term>            Search tables by keyword
statcan metadata <product-id>    Show table structure (dimensions + members)
statcan download <product-id>    Download observations via SDMX
statcan vector <vector-id>...    Download one or more vector series
statcan codeset                  Show StatCan code definitions (UOM, frequency, etc.)
statcan describe                 Commands, options, exit codes and JSON envelopes as JSON

Common usage:

# Find a table
statcan search "consumer price index"
statcan search "labour force" --max-results 10 --format json

# Inspect structure before downloading
statcan metadata 18-10-0004-01
statcan metadata 18100004 --full        # show all dimension members

# Download data (run `statcan metadata <pid>` first to find real dimension IDs)
statcan download 18-10-0004-01 --key "1.1.1" --last 12 --output cpi.csv
statcan download 18-10-0004-01 --key "1.1.1" --start 2020-01 --end 2024-12
statcan download 18-10-0004-01 --key "1.1.1" --last 5 --dry-run   # preview SDMX URL

# Download by vector ID
statcan vector v41690973 --last 24 --output series.csv
statcan vector v41690973 v41690974 --last 12 --output multi.csv

# Decode numeric codes
statcan codeset --type uom
statcan codeset --type frequency --format json

Output formats: csv (default for download/vector), table (default for search/metadata/codeset), json, ndjson

For agents and scripts: put --json before the subcommand (or set STATCAN_OUTPUT=json) to get one JSON envelope on stdout, JSON errors with a hint on stderr, and stable exit codes (0 ok, 2 usage, 3 not found, 4 rejected, 5 retryable, 6 empty). With --output, the envelope summarizes the file (rows, columns, period range, series count) so it never has to be read back. Details: machine-readable mode.

Pipe patterns:

# Top 10 by value (run `statcan metadata <pid>` first to find real dimension IDs)
statcan download 14-10-0287-01 --key "1.1.1" --last 1 --format csv \
  | awk -F',' 'NR>1' | sort -t',' -k5 -rn | head -10

# Extract unique geographies (run `statcan metadata <pid>` first to find real dimension IDs)
statcan download 14-10-0287-01 --key "1.1.1" --last 1 --format csv \
  | awk -F',' 'NR>1 {print $1}' | sort -u

# Chain search β†’ download (run `statcan metadata <pid>` first to find real dimension IDs)
PID=$(statcan search "CPI" --format json | python3 -c "import sys,json; print(json.load(sys.stdin)[0]['Product ID'])")
statcan download $PID --key "1.1.1" --last 12 --output cpi.csv

For the complete CLI reference see src/cli/README.md.


Features & Tools

SDMX Tools β€” server-side filtered data fetch

Only the slice you request is returned. No downloading full tables.

ToolDescription
get_sdmx_structureDimension codelists + key syntax for a table. Call before get_sdmx_data.
get_sdmx_dataFiltered observations by productId + key, returned inline and grouped by series: each series' labels, VECTOR_ID and UOM once, then numeric [period, value] pairs (capped at 500 observations).
query_sdmxFetch observations and run one read-only SQL query server-side in a temporary in-memory table β€” returns only the result. Works on the hosted server; no row cap on the fetch.
get_sdmx_vector_dataObservations for a single vectorId via SDMX (same series-grouped output).
get_sdmx_key_for_dimensionAll leaf member IDs for a dimension as a ready-to-paste OR key β€” excludes totals and subtotals, so sums and rankings don't double-count (e.g. NOC, CMAs).

Key syntax (passed to get_sdmx_data):

  • "1.2.1" β€” Geography=1, Gender=2, Age=1
  • ".2.1" β€” all geographies (wildcard), Gender=2, Age=1
  • "1+2.2.1" β€” Geography 1 or 2, Gender=2, Age=1

Note: Wildcard (.) returns every member of the dimension, including totals and parent aggregates. For sums or rankings use leaf codes from get_sdmx_key_for_dimension. Large wildcards can exceed get_sdmx_data's 500-row cap; use query_sdmx for those.

WDS Discovery & Metadata
ToolDescription
search_cubes_by_titleFull-text search across all StatCan tables. AND logic, capped at 25 results; current tables before archived ones.
get_all_cubes_list / _litePaginated table inventory (offset/limit, default 100/page).
get_cube_metadataDimension info, member lists, date ranges. summary=True caps members at 10 per dimension.
get_code_setsDecode StatCan numeric codes. Summary by default; code_set="uom", search="percent" for large sets.
WDS Series Resolution & Change Detection
ToolDescription
get_series_infoResolve {productId, coordinate} pairs to vectorId + metadata.
get_series_info_from_vectorResolve a vectorId to productId, coordinate, titles, frequency.
get_changed_cube_listTables updated on a specific date.
get_changed_series_listSeries updated today (available after 08:30 ET); for historical dates, use get_changed_cube_list(date).
get_changed_series_data_from_cube_pid_coordData points that changed today for a coordinate (changed: false before 08:30 ET or if unchanged).
get_changed_series_data_from_vectorData points that changed today for a vectorId (changed: false before 08:30 ET or if unchanged).
get_bulk_vector_data_by_rangeMultiple vectors filtered by release date range.
Composite & Database Tools (local/stdio mode only)

These tools are not available on the hosted Render server β€” SQLite is per-process and not shared across users.

ToolDescription
fetch_sdmx_to_databaseFetch SDMX observations (productId + key) into the observation store β€” no row cap, rows stay out of context. table_name becomes a view.
fetch_vectors_to_databaseFetch vectors by reference period range into the same store. table_name becomes a view.
store_cube_metadataFetch full cube metadata into SQLite β€” browse all members and vectorIds with SQL.
query_databaseRead-only SQL against the local SQLite database.
create_table_from_data / insert_dataCreate or append to a table of your own data.
list_tables / get_table_schema / drop_tableDatabase utilities (tables and views; list_tables also reports store size).

Observation store. Both fetch tools upsert into two shared tables, so data accumulates instead of being copied into a new table per call:

TableOne row perKey columns
_statcan_seriesseriesseries_id (<productId>:<SDMX key>, e.g. 18100004:2.2), product_id, vector_id, dims_json (labels), uom
_statcan_observationsseries Γ— periodseries_id, ref_date (period start), period (2026, 2026-Q2, 2026-08), value, status, source, fetched_at, release_time
  • Re-fetching updates values in place; each fetch reports how many observations were new, revised or unchanged.
  • An SDMX and a WDS fetch of the same series share rows (WDS coordinates map to the same SDMX key).
  • table_name is a view over the series and periods that call fetched, with the same columns as before (dimension labels, period, value, VECTOR_ID, UOM, ...).
  • Cross-table analysis needs no schema discovery:
-- Seasonal component: CPI not seasonally adjusted (18100004) minus seasonally adjusted (18100006)
SELECT nsa.period, ROUND(nsa.value - sa.value, 2) AS seasonal
FROM _statcan_observations nsa JOIN _statcan_observations sa USING (ref_date)
WHERE nsa.series_id = '18100004:2.2' AND sa.series_id = '18100006:1.1'
ORDER BY ref_date DESC;
Typical workflow

Claude.ai web (hosted server):

1. search_cubes_by_title("unemployment rate")
   β†’ productId e.g. 14100287

2. get_sdmx_structure(productId=14100287)
   β†’ dimension positions + sample codes

3. get_sdmx_key_for_dimension(productId=14100287, dimension_position=3)
   β†’ or_key for large dimensions

4. get_sdmx_data(productId=14100287, key=".2.1", lastNObservations=24)
   β†’ returns download_csv URL

5. Python script: validate URL domain β†’ write to ./statcan_14100287.csv β†’ analyze β†’ print summary

Claude Code (bash sandbox):

statcan search "unemployment rate"
statcan metadata 14100287
statcan download 14-10-0287-01 --key "1.1.1" --last 24 --output ./lfs.csv  # run `statcan metadata <pid>` first to find real dimension IDs
awk -F',' 'NR>1 && $1=="Canada"' ./lfs.csv | sort -t',' -rn -k5 | head -10

Project Structure

src/
β”œβ”€β”€ api/
β”‚   β”œβ”€β”€ cube/
β”‚   β”‚   β”œβ”€β”€ discovery.py         # search_cubes_by_title, get_all_cubes_list
β”‚   β”‚   β”œβ”€β”€ metadata.py          # get_cube_metadata
β”‚   β”‚   └── series.py            # get_series_info, change detection
β”‚   β”œβ”€β”€ vector/
β”‚   β”‚   └── vector_tools.py      # vector series, bulk range fetch
β”‚   β”œβ”€β”€ sdmx/
β”‚   β”‚   └── sdmx_tools.py        # get_sdmx_structure, get_sdmx_data, query_sdmx, get_sdmx_key_for_dimension
β”‚   β”œβ”€β”€ composite_tools.py       # fetch_sdmx_to_database, fetch_vectors_to_database, store_cube_metadata (stdio only)
β”‚   └── metadata_tools.py        # get_code_sets
β”œβ”€β”€ cli/
β”‚   β”œβ”€β”€ main.py                  # statcan CLI entry point (Typer app)
β”‚   β”œβ”€β”€ output.py                # write_output, format helpers
β”‚   └── commands/
β”‚       β”œβ”€β”€ search.py            # statcan search
β”‚       β”œβ”€β”€ metadata.py          # statcan metadata
β”‚       β”œβ”€β”€ download.py          # statcan download
β”‚       β”œβ”€β”€ vector.py            # statcan vector
β”‚       └── codeset.py           # statcan codeset
β”œβ”€β”€ db/                          # SQLite: observation store (store.py), per-request SQL for query_sdmx (ephemeral.py), schema, queries
β”œβ”€β”€ models/                      # Pydantic input models
β”œβ”€β”€ util/
β”‚   β”œβ”€β”€ registry.py              # ToolRegistry β€” @decorator β†’ MCP Tool schema
β”‚   β”œβ”€β”€ truncation.py            # Response truncation + pagination guidance
β”‚   β”œβ”€β”€ sdmx_xml.py              # SDMX-ML (structure-specific XML) β†’ labelled tabular rows
β”‚   └── cache.py                 # 1-hour TTL cache for cube list
β”œβ”€β”€ config.py                    # BASE_URL, SDMX_BASE_URL, RENDER_BASE_URL, TRANSPORT, PORT
└── server.py                    # create_server(), MCP Prompts, resources, HTTP transport, CLI

Known Issues

IssueStatusWorkaround
"Unable to open database file" on Claude DesktopActivePass --db-path /Users/<you>/.statcan-mcp/statcan_data.db in your config
SSL verification on broken trust storesConfigurableVerification is on by default; set STATCAN_VERIFY_SSL=false only if your system can't validate StatCan's certificate
lastNObservations + startPeriod/endPeriod β†’ 406ActiveUse one or the other, not both
Wildcard/OR rows missing or mislabelled; wrong VECTOR_ID/UOMFixed in v0.12.0Caused by StatCan's SDMX-JSON index encoding; data is now read as structure-specific XML (#90)
Context overflow may cause data fabricationMitigatedUse query_sdmx (hosted) or fetch_sdmx_to_database (local) so raw rows never enter context; get_sdmx_data caps inline rows at 500

Contributors

Thanks to everyone who has contributed code, fixes and bug reports:

Want to help? See CONTRIBUTING.md.


Made with ❀️❀️❀️ for Statistics Canada

Comments

Markdown supported

New to MCP?

From what the protocol is to installing your first server, these answers cover most first questions.

An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.