Skip to main content
Glama
Statistics Canada MCP Server

Statistics Canada MCP Server

Statistics Canada MCP Server

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.

The statcan CLI searching, inspecting and downloading a table. See statcan CLI.


Table of Contents


Related MCP server: mcp-statscan

Quick Start

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

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

Dataset

Query

Demo

Source

Canada's Greenhouse Gas Emissions

"Create a simple visualization for greenhouse emissions for Canada as a whole over the last 4 years"

Chat

Table 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"

Chat

Table 12-10-0144-01

Ontario Building Construction Price Index

"Generate a visualization for Ontario's Building Price index from Q4 2023 to Q4 2024"

Chat

Table 18-10-0289-01

Canadian Unemployment Dashboard

"Create a Canadian Unemployment Dashboard using statcan mcp"

Chat

Table 14-10-0287-01

Dashboard examples

Title

Link

Source

Canada's Critical Minerals Economy

Dashboard

Table 36-10-0708-01

Price of Everything: CPI Dashboard 2015–2026

Dashboard

Table 18-10-0004-01

Canada's Biomedical & Biotech Industries

Dashboard

Table 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, and its code sandbox can't reach StatCan, so all data comes through the connector's MCP tools.

The pattern:

1. Discovery (small payloads):
   search_cubes_by_title("labour force")  → productId + key_template
   get_sdmx_structure(productId=..., search="Ontario;Employment")  → codes by label

2. Answers: get_sdmx_data(..., sql="SELECT ...")  → only the result comes back
3. Files: get_sdmx_data(..., format="csv")        → copy the CSV verbatim into a file

Use sql for rankings, growth and averages; use format="csv" only when the rows need to go into a file.

After a server update, disconnect and reconnect the connector in claude.ai to refresh the tool list.

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 two sets of instructions: Claude Code (the statcan CLI in bash) and Claude.ai web (the MCP tools, with sql and format="csv" to keep rows out of context).

Prompt

What it teaches

/statcan-data-lookup

End-to-end: search → structure → build key → fetch (CLI to a file, or MCP tools with sql) → analyze

/sdmx-key-builder

SDMX key syntax: wildcards, OR keys, time parameters, download URL format

/statcan-download

Download a specific table: CLI commands, or the MCP tools on Claude.ai

/statcan-explore

Sample before committing: 3-period fetch, column layout, size estimate

/statcan-vector-pipeline

Multi-series download and cross-series comparison

The tool calls in the prompts follow the active toolset.

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): see the recording at the top of this page.

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

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

Tools

Five tools, in the default core toolset:

Tool

Description

search_cubes_by_title

Find tables by title keywords (English or French; all must appear). Paged (offset, max_results, default 10); every result includes key_template, the dimension order of the SDMX key.

get_sdmx_structure

A table's dimensions in key order with their codes (id, name, parent). search="Ontario;Employment" finds codes by label across all dimensions; dimension=<position> lists one full dimension plus its leaf_key (lowest-level codes as a + OR).

get_sdmx_data

Observations for productId + key, or one vectorId. Over 100 observations you get a per-series summary unless full=true (max 500 rows). Pass sql to run one read-only SELECT over the table data server-side (growth, averages, rankings) and get back only the result. format="csv" returns compact CSV instead of JSON (see below).

get_series_info

productId + coordinates, or vectorIds: decoded frequency, unit and scalar, English and French title, and the SDMX key.

get_changes

Tables released on a date (default today), or the new data points for one series (vectorId, or productId + coordinate).

CSV output: get_sdmx_data(format="csv") returns one row per series and one column per period, with fields shared by every series written once as # name: value lines. It always returns the rows (ignores full, max 500 observations) and works with sql. It is about 3x smaller than full=true JSON (table 15100032, key 7...: 7,892 to 2,637 characters; 18100004, key .2, last 12: 13,247 to 3,630).

get_sdmx_data(productId=18100004, key=".2", lastNObservations=12, format="csv")

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 the leaf_key from get_sdmx_structure(productId, dimension=N), and pass sql to compute over large result sets.

Toolsets

The server publishes one of two toolsets, chosen at startup. core is the default; legacy is the previous 17-tool set (hosted).

STATCAN_TOOLSET=legacy uvx statcan-mcp-server      # or: uvx statcan-mcp-server --toolset legacy

Smaller tool lists leave more of the context window for data. Sizes of the tools/list response, including outputSchema (o200k tokens):

Transport

legacy

core

Hosted (HTTP)

17 tools, about 8,044 tokens

5 tools, about 939 tokens

stdio (adds the database tools)

26 tools, about 11,407 tokens

14 tools, about 4,302 tokens

Legacy tool to core replacement:

Legacy

Core

get_all_cubes_list, get_all_cubes_list_lite

search_cubes_by_title (paged)

get_cube_metadata

get_sdmx_structure (search, dimension)

get_code_sets

decoded in get_series_info output

get_sdmx_key_for_dimension

get_sdmx_structure(dimension=N) returns leaf_key

get_sdmx_vector_data

get_sdmx_data(vectorId=...)

query_sdmx

get_sdmx_data(..., sql=...)

get_series_info_from_vector

get_series_info(vectorIds=[...])

get_bulk_vector_data_by_range

get_sdmx_data(vectorId=..., startPeriod=..., endPeriod=...) per series

get_changed_cube_list, get_changed_series_list

get_changes(date=...)

get_changed_series_data_from_vector, get_changed_series_data_from_cube_pid_coord

get_changes(vectorId=...) or get_changes(productId=..., coordinate=...)

Composite & Database Tools (local/stdio mode only, both toolsets)

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

Tool

Description

fetch_sdmx_to_database

Fetch SDMX observations (productId + key) into the observation store — no row cap, rows stay out of context. table_name becomes a view.

fetch_vectors_to_database

Fetch vectors by reference period range into the same store. table_name becomes a view.

store_cube_metadata

Fetch full cube metadata into SQLite — browse all members and vectorIds with SQL.

query_database

Read-only SQL against the local SQLite database.

create_table_from_data / insert_data

Create or append to a table of your own data.

list_tables / get_table_schema / drop_table

Database 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:

Table

One row per

Key columns

_statcan_series

series

series_id (<productId>:<SDMX key>, e.g. 18100004:2.2), product_id, vector_id, dims_json (labels), uom

_statcan_observations

series × period

series_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_structure(productId=14100287, search="Ontario;Employment")
   → ids of the codes you want, by label

4. get_sdmx_data(productId=14100287, key=".2.1", lastNObservations=24,
                 sql="SELECT ... FROM data")
   → server-side SQL: only the result comes back (omit sql for the observations)

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/
│   ├── core/
│   │   └── tools.py             # core toolset (default): the five merged tools
│   ├── cube/                    # legacy toolset: search/list cubes, metadata, series, change detection
│   ├── vector/
│   │   └── vector_tools.py      # legacy toolset: vector series, bulk range fetch
│   ├── sdmx/
│   │   └── sdmx_tools.py        # legacy toolset: SDMX structure/data/query_sdmx; shared fetch helpers
│   ├── composite_tools.py       # fetch_sdmx_to_database, fetch_vectors_to_database, store_cube_metadata (stdio only)
│   └── metadata_tools.py        # get_code_sets (legacy toolset)
├── 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 get_sdmx_data(sql=) (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
├── toolset.py                   # resolve_toolset(): STATCAN_TOOLSET / --toolset
└── server.py                    # create_server(), MCP Prompts, resources, HTTP transport, CLI

Known Issues

Issue

Status

Workaround

"Unable to open database file" on Claude Desktop

Active

Pass --db-path /Users/<you>/.statcan-mcp/statcan_data.db in your config

SSL verification on broken trust stores

Configurable

Verification is on by default; set STATCAN_VERIFY_SSL=false only if your system can't validate StatCan's certificate

lastNObservations + startPeriod/endPeriod → 406

Active

Use one or the other, not both

Wildcard/OR rows missing or mislabelled; wrong VECTOR_ID/UOM

Fixed in v0.12.0

Caused by StatCan's SDMX-JSON index encoding; data is now read as structure-specific XML (#90)

Context overflow may cause data fabrication

Mitigated

Use get_sdmx_data with sql (hosted) or fetch_sdmx_to_database (local) so raw rows never enter context; get_sdmx_data caps inline rows at 500

Slow or stalled StatCan requests

Mitigated

Stalled requests retry after about 10 s. An incomplete SDMX structure isn't cached, and labels fall back to WDS metadata (the result's _message says so)


Contributors

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

  • @dannyward630: fixed get_changed_series_list to use StatCan's undated endpoint (#130)

Want to help? See CONTRIBUTING.md.


Available Tools

25 tools
create_table_from_dataAInspect

Creates a new SQLite table from the provided data AND immediately inserts all rows. Infers column names and types from the first item in the data list. WARNING: Overwrites the table if it already exists.

Use this as a single step to store fetched API data — no need to call insert_data_into_table afterwards. Use insert_data_into_table only to append more rows to an already-existing table.

Args: table_input: Object containing table_name and data (list of dicts).

Returns: Dict[str, Any]: A summary with table name, columns created, and rows inserted.

IMPORTANT: The database is persistent and does NOT clean itself automatically. This tool overwrites the table if it exists, giving you a clean slate each call.

Inputs: data (required), table_name (required).

drop_tableAInspect

Permanently deletes (drops) a table from the SQLite database.

Use this to free up space or remove tables that are no longer needed. This action is irreversible — all data in the table will be lost.

Args: table_name_input: Object containing the table_name to drop.

Returns: Dict[str, Any]: A dictionary indicating success or an error message.

Inputs: table_name (required).

fetch_vectors_to_databaseAInspect

PREFERRED tool for multi-series analysis. Fetches data for multiple StatCan vector IDs in a single API call and immediately stores the results in a SQLite table — no separate create/insert steps needed.

*** USE THIS TOOL whenever you need data for multiple provinces, age groups, industries, or any other breakdown. It replaces the slow pattern of calling get_data_from_cube_pid_coord_and_latest_n_periods once per series. ***

Typical workflow:

  1. search_cubes_by_title("unemployment rate") → find productId

  2. get_cube_metadata(productId=...) → find vectorIds for each series you want

  3. fetch_vectors_to_database( vectorIds=["v111","v222","v333"], table_name="unemployment_by_province", startRefPeriod="2023-01-01", endRefPeriod="2024-12-31" ) ← single call fetches + stores everything

  4. query_database("SELECT * FROM unemployment_by_province") → analyze

Args: input_data.vectorIds: List of vector IDs to fetch (strings, e.g. ["111","222"]). input_data.table_name: SQLite table to create and populate. input_data.startRefPeriod: Optional start date (YYYY-MM-DD). input_data.endRefPeriod: Optional end date (YYYY-MM-DD).

Returns: Dict with table name, columns, rows_inserted, and a 5-row sample so you can verify the data looks right before querying.

IMPORTANT: In your final response cite the vectorIds and reference period used.

Inputs: vectorIds (required), table_name (required), sample_size, endRefPeriod, +1 more.

get_all_cubes_listAInspect

Provides a complete inventory of data tables available via the API, including dimension-level details. Disables SSL Verification. Corresponds to: GET /getAllCubesList

Results are paginated. Default returns first 100 cubes. Use offset/limit to page through. Prefer search_cubes_by_title if you know what you're looking for.

IMPORTANT: In your final response to the user, you MUST cite the source of your data. For cubes, this means including the ProductId (pid) and the Title.

Inputs: limit, offset.

get_all_cubes_list_liteAInspect

Provides a complete inventory of data tables available via the API, excluding dimension or footnote information (lighter version). Disables SSL Verification. Corresponds to: GET /getAllCubesListLite

Results are paginated. Default returns first 100 cubes. Use offset/limit to page through. Prefer search_cubes_by_title if you know what you're looking for.

IMPORTANT: In your final response to the user, you MUST cite the source of your data. For cubes, this means including the ProductId (pid) and the Title.

Inputs: limit, offset.

get_bulk_vector_data_by_rangeAInspect

Fetches bulk data for multiple vectors filtered by release date range (YYYY-MM-DDTHH:MM), NOT by reference period. Use this when you want data released within a specific date/time window (e.g., "all updates released yesterday").

*** IMPORTANT: release date vs reference period ***

  • Use THIS tool when you want: "data released between date A and date B"

  • Use get_sdmx_vector_data (startPeriod/endPeriod) when you want: "data for the time period YYYY to YYYY" get_sdmx_vector_data is more reliable and filters by reference period, not release date.

*** LARGE RESPONSE WARNING *** This tool can return hundreds of flattened data points. If the response exceeds context limits, narrow the request: use fewer vectorIds, or use offset/limit pagination to page through results in smaller chunks.

Response is pre-flattened: each element is one data point with vectorId, productId, coordinate, and all value fields injected at the top level.

Disables SSL Verification. Corresponds to: POST /getBulkVectorDataByRange

Returns: List[Dict[str, Any]]: Flat list of data point dicts, each tagged with vectorId, productId, and coordinate. Raises: httpx.HTTPStatusError: If the API returns an error status code. ValueError: If the API response format is unexpected or no vectors return SUCCESS. Exception: For other network or unexpected errors.

IMPORTANT: In your final response to the user, you MUST cite the source of your data. For vector data, this means including the VectorId and Release Time.

Inputs: vectorIds (required), limit, offset, endDataPointReleaseDate, +1 more.

get_changed_cube_listAInspect

Get the list of data tables/cubes that were updated on a specific date (YYYY-MM-DD). Disables SSL Verification. Corresponds to: GET /getChangedCubeList/{date}

Returns: List[Dict[str, Any]]: A list of dictionaries describing changed cube objects.

IMPORTANT: In your final response to the user, you MUST cite the source of your data. For changed cubes, this means including the ProductId (pid) and Title.

Inputs: date (required).

get_changed_series_data_from_cube_pid_coordAInspect

Retrieves changed series data (data points that have changed) using Cube ProductId and Coordinate string. Coordinates are automatically padded to 10 dimensions. Disables SSL Verification. Corresponds to: POST /getChangedSeriesDataFromCubePidCoord

Returns: Dict[str, Any]: A dictionary containing the changed series data object. Raises: httpx.HTTPStatusError: If the API returns an error status code. ValueError: If the API response format is unexpected or status is not SUCCESS. Exception: For other network or unexpected errors.

IMPORTANT: In your final response to the user, you MUST cite the source of your data. For changed series data, this means including the VectorId, ProductId (pid), and Coordinate.

Inputs: productId (required), coordinate (required).

get_changed_series_data_from_vectorBInspect

Get changed series data (data points that have changed) for a series identified by Vector ID. Disables SSL Verification. Corresponds to: POST /getChangedSeriesDataFromVector

Returns: Dict[str, Any]: A dictionary containing the changed series data object. Raises: httpx.HTTPStatusError: If the API returns an error status code. ValueError: If the API response format is unexpected or status is not SUCCESS. Exception: For other network or unexpected errors.

IMPORTANT: In your final response to the user, you MUST cite the source of your data. For changed series data, this means including the VectorId.

Inputs: vectorId (required).

get_changed_series_listAInspect

Get the list of series (vectorId, productId, coordinate, releaseTime) that were updated on a specific date (YYYY-MM-DD). Disables SSL Verification. Corresponds to: GET /getChangedSeriesList/{date}

Returns: List[Dict[str, Any]]: A list of dictionaries describing changed series objects. Raises: httpx.HTTPStatusError: If the API returns an error status code. ValueError: If date format is invalid or API response format is unexpected. Exception: For other network or unexpected errors.

IMPORTANT: In your final response to the user, you MUST cite the source of your data. For changed series, this means including the VectorId.

Inputs: date (required).

get_code_setsAInspect

Retrieves definitions for various code sets used by the API (e.g., frequency, units of measure). Corresponds to: GET /getCodeSets

Returns: Dict[str, Any]: Dictionary containing code set definitions (scalar, frequency, etc.). Raises: httpx.HTTPStatusError: If the API returns an error status code. ValueError: If the API response format is unexpected. Exception: For other network or unexpected errors.

IMPORTANT: In your final response to the user, you MUST cite the source of your data. For code sets, this means specifying which code set table or definition is being used.

Inputs: no named parameters.

get_cube_metadataAInspect

Retrieves detailed metadata for a specific data table/cube using its ProductId. Includes dimension info, titles, date ranges, codes, etc. Disables SSL Verification. Corresponds to: POST /getCubeMetadata

Start with summary=True (default). The summary strips noise (French translations, archive codes, footnotes) and shows only 3 sample members per dimension with _next_steps guidance. Safe for all context window sizes. Set summary=False only if you need the full raw member list or all API fields.

To browse dimension codes for get_sdmx_data key construction, use get_sdmx_structure. To resolve a coordinate to a vectorId, use get_series_info.

Returns: Dict[str, Any]: The metadata object for the specified cube on success. Raises: httpx.HTTPStatusError: If the API returns an error status code. ValueError: If the API response format is unexpected or status is not SUCCESS. Exception: For other network or unexpected errors.

IMPORTANT: In your final response to the user, you MUST cite the source of your data. For cubes, this means including the ProductId (pid) and the Title.

Inputs: productId (required), summary.

get_sdmx_dataAInspect

Fetch filtered time-series observations from a StatCan table via SDMX REST.

Filtering is done server-side — only the requested slice is returned. Call get_sdmx_structure first to see dimension positions and valid codes.

Key syntax (dot-separated codes in dimension position order): "1.2.1" = Geography=1 (Canada), Gender=2 (Men+), Age=1 (All ages) ".2.1" = all geographies, Gender=2, Age=1 (wildcard — preferred for multi-geo) "1+2.2.1" = Geography 1 or 2, Gender=2, Age=1 (OR)

IMPORTANT — key position codes:

  • Use member IDs from get_cube_metadata(), NOT SDMX codelist positions from get_sdmx_structure(). Member IDs and SDMX codelist codes are the same numbers.

  • Wildcard (omit a position) returns a SPARSE SAMPLE for large dimensions — do NOT use wildcard for dimensions with >30 codes (e.g. NOC occupations, CMA geographies). Use explicit member IDs joined with '+' instead.

  • To get all leaf IDs for a large dimension as a ready-to-use OR string, call get_sdmx_key_for_dimension(productId, dimension_position) first.

Time filtering (use one or the other, not both): lastNObservations=12 → last 12 periods (e.g. 1 year of monthly data) startPeriod="2020" → from 2020 onwards (annual); "2020-01" for monthly endPeriod="2023-12" → up to Dec 2023

LIMITATION: StatCan rejects combining lastNObservations with startPeriod/endPeriod (returns 406). NOTE: OR syntax (+) triggers a StatCan SDMX-JSON encoding bug (non-positional series keys). This is automatically corrected before rows are returned, so all OR-ed dimension labels should be present.

Output rows contain: dimension values, "period", "value", SCALAR_FACTOR, UOM, VECTOR_ID, STATUS, and other SDMX attributes.

Rows are returned inline, capped at MAX_SDMX_ROWS (500). For larger result sets, narrow the key or use startPeriod/endPeriod/lastNObservations.

IMPORTANT: In your final response to the user, you MUST cite the source of your data. This means including the _sdmx_url, table information and productId/key in your response.

Inputs: key (required), productId (required), endPeriod, startPeriod, +1 more.

get_sdmx_key_for_dimensionAInspect

Return all leaf member IDs for a single dimension as a ready-to-use OR key string.

Use this before get_sdmx_data when a dimension has many codes (e.g. 162 NOC minor groups, hundreds of CMA geographies). Avoids the need to call get_cube_metadata and manually parse a large JSON response.

Leaf codes are codes with no children — the lowest-level members in a hierarchy. For flat (non-hierarchical) codelists every code is a leaf.

Example: get_sdmx_key_for_dimension(productId=98100452, dimension_position=6) → { "dimension_id": "Occupation_...", "dimension_name": "Occupation - Minor group - NOC 2021", "position": 6, "leaf_count": 162, "total_count": 309, "or_key": "7+11+12+13+16+18+21+23+...", "note": "Paste or_key at position 6 in your get_sdmx_data key." }

Then use the or_key directly: get_sdmx_data(productId=98100452, key="7.3.1.1.1.<or_key>.1", ...)

Inputs: productId (required), dimension_position (required).

get_sdmx_rowsAInspect

Fetch SDMX observations and always return rows inline — use this when you need to embed data in an artifact or widget.

Use this tool when you need rows embedded directly in an artifact or widget:

  • Building a chart, table, or widget artifact that needs data at construction time

  • Sorting/filtering a small result set before embedding

Same key syntax and time parameters as get_sdmx_data — see that tool's description for key construction rules and wildcard warnings.

Rows are capped at MAX_SDMX_ROWS. For large dimensions use get_sdmx_key_for_dimension to build a precise OR key before calling this.

IMPORTANT: In your final response to the user, cite the _sdmx_url, table productId, and key used.

Inputs: key (required), productId (required), endPeriod, startPeriod, +1 more.

get_sdmx_structureAInspect

Fetch the Data Structure Definition (DSD) for a StatCan table via SDMX REST.

Returns dimension codelists with code IDs, names, and parent hierarchy. Use this BEFORE get_sdmx_data to understand the key syntax for that table.

Each dimension entry includes:

  • id: dimension identifier (e.g. "Geography")

  • position: its slot in the dot-separated key string (1-based)

  • codelist: the SDMX codelist ID (e.g. "CL_Geography")

  • codes: list of {id, name, ?parent} — truncated to 10 for large codelists

  • _code_count / _truncated: total size and truncation flag

Key construction rules:

  • "1.2.1" = position-1 code 1, position-2 code 2, position-3 code 1

  • ".2.1" = wildcard position 1 (all geographies), Gender=2, Age=1

  • "1+2.2.1" = Geography 1 or 2 (OR syntax)

  • WDS memberIds == SDMX codelist codes — no translation needed

IMPORTANT: In your final response to the user, you MUST cite the source of your data. This means including the _sdmx_url.

Inputs: productId (required).

get_sdmx_vector_dataAInspect

Fetch time-series observations for a single StatCan vector via SDMX REST.

Simpler alternative to get_sdmx_data when you already know the vectorId. Use get_series_info_from_cube_pid_coord or get_cube_metadata to find vectorIds.

Time filtering (use one or the other, not both): lastNObservations=5 → last 5 periods startPeriod="2020-01" → from Jan 2020 (monthly); "2020" for annual endPeriod="2023-12" → up to Dec 2023

LIMITATION: StatCan rejects combining lastNObservations with startPeriod/endPeriod (returns 406).

Output rows contain: dimension values, "period", "value", SCALAR_FACTOR, UOM, VECTOR_ID, STATUS, and other SDMX attributes.

IMPORTANT: In your final response to the user, you MUST cite the source of your data. This means including the _sdmx_url,and vectorId in your response.

Inputs: vectorId (required), endPeriod, startPeriod, lastNObservations.

get_series_infoAInspect

Resolve one or more {productId, coordinate} pairs to series metadata (vectorId, titles, frequency, UOM, etc.) in a single API call.

Use this to find vectorIds before fetching data with get_sdmx_data or get_sdmx_vector_data. Pass one item or many — same tool either way. Coordinates are automatically padded to 10 dimensions. Corresponds to: POST /getSeriesInfoFromCubePidCoord (accepts array)

NOTE: Response fields like scalarFactorCode, frequencyCode, and memberUomCode use StatCan numeric codes. Call get_code_sets() to decode them (e.g. frequencyCode 6 = "Monthly", scalarFactorCode 0 = "Units").

Returns: List of series metadata dicts, paginated with _guidance if >50 results. Raises: httpx.HTTPStatusError: If the API returns an error status code. ValueError: If no items return SUCCESS. Exception: For other network or unexpected errors.

IMPORTANT: In your final response cite the ProductId and Coordinate for each series.

Inputs: items (required), limit, offset.

get_series_info_from_vectorAInspect

Request series metadata (productId, coordinate, titles, frequency, etc.) by Vector ID. Disables SSL Verification. Corresponds to: POST /getSeriesInfoFromVector

Returns: Dict[str, Any]: A dictionary containing the series metadata object. Raises: httpx.HTTPStatusError: If the API returns an error status code. ValueError: If the API response format is unexpected or status is not SUCCESS. Exception: For other network or unexpected errors.

IMPORTANT: In your final response to the user, you MUST cite the source of your data. For series info, this means including the VectorId, ProductId (pid), and Coordinate.

Inputs: vectorId (required).

get_table_schemaBInspect

Retrieves the schema (column names and types) for a specific table.

Args: table_name_input: Object containing the table_name.

Returns: Dict[str, Any]: Dictionary describing the schema or an error message.

Inputs: table_name (required).

insert_data_into_tableAInspect

Appends rows (list of dicts) into an ALREADY EXISTING SQLite table. Use this only to add more data to a table that was previously created.

For the common "fetch API data then store" workflow, use create_table_from_data or fetch_vectors_to_database instead — both create the table AND insert data in a single call, so you do NOT need to call this tool after them.

This tool is useful when:

  • You want to merge data from multiple API calls into one table

  • You're appending new time periods to an existing dataset

Args: table_input: Object containing table_name and data (list of dicts).

Returns: Dict[str, str]: A dictionary indicating success (with row count) or failure.

IMPORTANT: In your final response to the user, you MUST cite the source of the data you are inserting if it comes from an API call (e.g., "Data from Product ID 123456").

Inputs: data (required), table_name (required).

list_tablesAInspect

Lists all user-created tables in the SQLite database.

Returns: Dict[str, Any]: Dictionary containing a list of table names or an error message.

IMPORTANT: The database is persistent. Use this to check for old tables that might need cleaning.

Inputs: no named parameters.

query_databaseAInspect

Executes a read-only SQL query (SELECT or PRAGMA) against the database and returns the results. WARNING: Potential security risk! Avoid using this tool with untrusted input or queries that modify data (INSERT, UPDATE, DELETE). Prefer more specific tools like list_tables or get_table_schema when possible. Results may be truncated.

Args: query_input: Object containing the sql_query string.

Returns: Dict[str, Any]: Dictionary with 'columns', 'rows' (list of dicts), and optionally a 'message', or an error message.

IMPORTANT: In your final response to the user, you MUST cite the source of your data (e.g., "Query results from table 'my_analysis'").

Inputs: sql_query (required).

search_cubes_by_titleAInspect

Searches for data cubes/tables where the English or French title contains the provided search term (case-insensitive). Returns a list of matching cubes in the 'lite' format (excluding dimensions/footnotes).

Multiple keywords use AND logic (e.g., "tobacco smoking age" finds cubes containing ALL three words). Results are capped at max_results (default 25).

IMPORTANT: In your final response to the user, you MUST cite the source of your data. For cubes, this means including the ProductId (pid) and the Title.

Raises: httpx.HTTPStatusError: If the underlying API call fails. Exception: For other network or unexpected errors during the fetch.

Inputs: search_term (required), max_results.

store_cube_metadataAInspect

Fetches FULL metadata for a cube and stores it into two normalized SQLite tables (_statcan_dimensions, _statcan_members) without returning the full data to the context window.

Use this when you need to browse all dimension members or look up vectorIds. The summary returned by get_cube_metadata only shows 5 members per dimension — call this tool first, then use SQL to drill into specific dimensions.

Typical workflow:

  1. store_cube_metadata(productId=1234567) → stores all members + vectorIds, returns compact summary

  2. query_database("SELECT * FROM _statcan_dimensions WHERE pid = 1234567") → see all dimension names and member counts

  3. query_database("SELECT member_name_en, vector_id FROM _statcan_members WHERE pid = 1234567 AND dim_index = 2") → browse all members for a specific dimension

  4. fetch_vectors_to_database(vectorIds=[...], ...) → fetch the data

Tables are shared across multiple pids — calling this for a new pid adds rows without affecting data for other pids already stored.

Returns a compact summary: dimension names + member counts + example SQL.

IMPORTANT: Cite the productId and cubeTitleEn in your final response.

Inputs: productId (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.6/5.0

    Scored across 25 tools

    Disambiguation2/5

    Multiple tools overlap in purpose, e.g., get_all_cubes_list vs get_all_cubes_list_lite, get_sdmx_data vs get_sdmx_rows, get_series_info vs get_series_info_from_vector, and two changed-series-data tools that differ only by identifier type. The detailed descriptions help, but the boundaries between these tools are subtle enough that an agent could easily pick the wrong one.

    Naming Consistency3/5

    Most tools follow a snake_case verb_noun pattern (get_, list_, create_, insert_, etc.), but the SDMX family is inconsistent: get_sdmx_data, get_sdmx_rows, get_sdmx_vector_data, and get_sdmx_structure all sound like data retrieval tools, and get_series_info vs get_series_info_from_vector is confusingly similar. Naming is readable but not perfectly predictable.

    Tool Count3/5

    At 25 tools, the server sits at the high end of 'feels heavy'. The scope is broad (Statistics Canada API plus SQLite persistence), which justifies many tools, but some functions could be merged (e.g., get_sdmx_data and get_sdmx_rows) to reduce redundancy and cognitive load.

    Completeness4/5

    The tool surface covers the full data lifecycle: cube discovery, search, metadata, series info, SDMX data, vector access, changed-data tracking, code-set definitions, plus a full SQLite persistence layer for storing and querying results. Minor gaps exist (e.g., no direct batch-metadata fetch, and lastNObservations can't combine with date ranges), but no critical dead ends.

    Maintenance

    ActivityMaintained
    ResponsivenessLimited data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      F
      maintenance
      Provides access to Statistics Canada official statistics without authentication, enabling AI agents to query Canadian economic and demographic data.
      6 npm
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      MCP server giving LLMs live access to Statistics Canada's full catalogue of 8,200+ data tables via the Web Data Service, with tools to search tables, inspect dimensions, and fetch time series data using plain-language prompts.
      5
      61 npm
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables an MCP client to discover ABS datasets, inspect SDMX dimension order and codelists, and run bounded statistical-data queries against the Australian Bureau of Statistics Data API.
      MIT