{
  "markdown": "<a href=\"https://www.statcan.gc.ca/en/start\" target=\"_blank\"><img src=\"assets/StatCan-Header.jpg\" alt=\"Statistics Canada MCP Server\"></a>\n\n# Statistics Canada MCP Server\n\n<a href=\"https://www.python.org/downloads/\" target=\"_blank\"><img src=\"https://img.shields.io/badge/python-3.10+-blue.svg\" alt=\"Python 3.10+\"></a>\n<a href=\"https://opensource.org/licenses/MIT\" target=\"_blank\"><img src=\"https://img.shields.io/badge/License-MIT-yellow.svg\" alt=\"License: MIT\"></a>\n<a href=\"https://modelcontextprotocol.io/\" target=\"_blank\"><img src=\"https://img.shields.io/badge/MCP-ModelContextProtocol-green.svg\" alt=\"MCP\"></a>\n<a href=\"https://github.com/Aryan-Jhaveri\" target=\"_blank\"><img src=\"https://img.shields.io/badge/GitHub-Aryan--Jhaveri-lightgrey?logo=github\" alt=\"GitHub\"></a>\n[![SafeSkill 93/100](https://img.shields.io/badge/SafeSkill-93%2F100_Verified%20Safe-brightgreen)](https://safeskill.dev/scan/aryan-jhaveri-mcp-statcan)\n\n<!-- mcp-name: io.github.Aryan-Jhaveri/mcp-statcan -->\n\nMCP server and CLI for Statistics Canada's [Web Data Service (WDS)](https://www.statcan.gc.ca/eng/developers/wds) and [SDMX REST API](https://www150.statcan.gc.ca/t1/wds/sdmx/statcan/rest/). 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.\n\n> **Hosted on Render — no install required for most users. See [Quick Start](#quick-start).**\n\n**⚠️ LLMs may fabricate data. Always verify important figures against [official Statistics Canada sources](https://www.statcan.gc.ca/).**\n\n---\n\n## Table of Contents\n\n- [Quick Start](#quick-start)\n- [Examples](#examples)\n- [Setup by Client](#setup-by-client)\n- [How Claude.ai Uses This Server](#how-claudeai-uses-this-server)\n- [MCP Prompts](#mcp-prompts)\n- [statcan CLI](#statcan-cli)\n- [Features & Tools](#features--tools)\n- [Project Structure](#project-structure)\n- [Known Issues](#known-issues)\n\n---\n\n## Quick Start\n\nPick the option that fits you. You don't need to install anything for Option 1.\n\n### Option 1 — Use the hosted server (recommended)\n\nConnect directly to the public server on Render. No `uv`, no terminal, no local setup.\n\n**Claude Desktop / Claude.ai**\n1. Open **Settings → Connectors → Add Custom Connector**\n2. Name: `mcp-statcan`\n3. URL: `https://mcp-statcan.onrender.com/mcp`\n4. Save and restart\n\n**Claude Code**\n```bash\nclaude mcp add statcan --transport http https://mcp-statcan.onrender.com/mcp --scope global\n```\n\n> The hosted server provides all WDS + SDMX tools. Database tools (SQLite) require local setup (Option 3) — they are intentionally excluded from the shared server.\n\n---\n\n### Option 2 — Self-host HTTP (WDS + SDMX, no DB)\n\nRun a local server with the same tools as the hosted version.\n\n**Step 1** — Install [`uv`](https://docs.astral.sh/uv/):\n```bash\n# macOS / Linux\ncurl -LsSf https://astral.sh/uv/install.sh | sh\n\n# Windows (PowerShell)\npowershell -ExecutionPolicy ByPass -c \"irm https://astral.sh/uv/install.ps1 | iex\"\n```\n\n**Step 2** — Start the server:\n```bash\nuvx statcan-mcp-server --transport http\n# Listening at http://localhost:8000\n```\n\n**Step 3** — Connect your client to `http://localhost:8000/mcp`.\n\n---\n\n### Option 3 — Full local setup (WDS + SDMX + SQLite)\n\nEverything from Option 2, plus database tools for storing and querying data with SQL. Runs via stdio.\n\n**Step 1** — Install `uv` (same as above).\n\n**Step 2** — Configure your client with the stdio snippets in [Setup by Client](#setup-by-client) below.\n\n`uvx` downloads and runs the server automatically on first use.\n\n---\n\n### Option 4 — `statcan` CLI (no LLM needed)\n\nDownload StatCan data directly from the terminal. See [statcan CLI](#statcan-cli).\n\n```bash\nuvx statcan-mcp-server        # installs the package\nstatcan search \"labour force\"\nstatcan download 14-10-0287-01 --last 12 --output lfs.csv\n```\n\n---\n\n## Examples\n\n### Chat examples\n\n| Dataset | Query | Demo | Source |\n|---|---|---|---|\n| Canada's Greenhouse Gas Emissions | \"Create a simple visualization for greenhouse emissions for Canada as a whole over the last 4 years\" | <a href=\"https://claude.ai/share/7de892a1-e1d9-410f-96f7-90cd140e5dd9\" target=\"_blank\">Chat</a> | <a href=\"https://www150.statcan.gc.ca/t1/tbl1/en/cv.action?pid=3810009701\" target=\"_blank\">Table 38-10-0097-01</a> |\n| Canada's International Trade in Services | \"Create a quick analysis for international trade in services for the last 6 months with a visualization\" | <a href=\"https://claude.ai/share/c00eba2d-4e86-4405-878a-7ea4110cb7d3\" target=\"_blank\">Chat</a> | <a href=\"https://www150.statcan.gc.ca/t1/tbl1/en/cv.action?pid=1210014401\" target=\"_blank\">Table 12-10-0144-01</a> |\n| Ontario Building Construction Price Index | \"Generate a visualization for Ontario's Building Price index from Q4 2023 to Q4 2024\" | <a href=\"https://claude.ai/share/12ce906f-5a26-4e74-86d9-10451ab5bc4b\" target=\"_blank\">Chat</a> | <a href=\"https://www150.statcan.gc.ca/t1/tbl1/en/cv.action?pid=1810028901\" target=\"_blank\">Table 18-10-0289-01</a> |\n| Canadian Unemployment Dashboard | \"Create a Canadian Unemployment Dashboard using statcan mcp\" | <a href=\"https://claude.ai/share/50d72d90-ec98-463e-88ec-85032edc9c29\" target=\"_blank\">Chat</a> | <a href=\"https://www150.statcan.gc.ca/t1/tbl1/en/tv.action?pid=1410028701&pickMembers%5B0%5D=1.1&pickMembers%5B1%5D=3.1&pickMembers%5B2%5D=4.1&pickMembers%5B3%5D=5.1&cubeTimeFrame.startMonth=04&cubeTimeFrame.startYear=2023&cubeTimeFrame.endMonth=03&cubeTimeFrame.endYear=2026&referencePeriods=20230401%2C20260301\" target=\"_blank\">Table 14-10-0287-01</a> |\n\n### Dashboard examples\n\n| Title | Link | Source |\n|---|---|---|\n| Canada's Critical Minerals Economy | <a href=\"https://claude.ai/public/artifacts/15d289c7-f324-4ced-bcc0-53d6ac3218c9\" target=\"_blank\">Dashboard</a> | <a href=\"https://www150.statcan.gc.ca/t1/tbl1/en/cv.action?pid=3610070801\" target=\"_blank\">Table 36-10-0708-01</a> |\n| Price of Everything: CPI Dashboard 2015–2026 | <a href=\"https://claude.ai/public/artifacts/61e99645-934c-4fe2-9693-88dca714a634\" target=\"_blank\">Dashboard</a> | <a href=\"https://www150.statcan.gc.ca/t1/tbl1/en/cv.action?pid=1810000401\" target=\"_blank\">Table 18-10-0004-01</a> |\n| Canada's Biomedical & Biotech Industries | <a href=\"https://claude.ai/public/artifacts/749ab9ef-c7a2-4186-8a75-d94a6eb8772e\" target=\"_blank\">Dashboard</a> | <a href=\"https://www150.statcan.gc.ca/t1/tbl1/en/tv.action?pid=2710029701\" target=\"_blank\">Table 27-10-0297-01</a> |\n\n---\n\n## Setup by Client\n\n### Hosted server (Option 1)\n\n**Claude Desktop** — Settings → Connectors → Add Custom Connector\n- Name: `mcp-statcan`\n- URL: `https://mcp-statcan.onrender.com/mcp`\n\n**Claude Code**\n```bash\nclaude mcp add statcan --transport http https://mcp-statcan.onrender.com/mcp --scope global\n```\n\n**Cursor** — `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global):\n```json\n{\n  \"mcpServers\": {\n    \"statcan\": {\n      \"url\": \"https://mcp-statcan.onrender.com/mcp\"\n    }\n  }\n}\n```\n\n**VS Code (GitHub Copilot)** — `.vscode/mcp.json`:\n```json\n{\n  \"servers\": {\n    \"statcan\": {\n      \"type\": \"http\",\n      \"url\": \"https://mcp-statcan.onrender.com/mcp\"\n    }\n  }\n}\n```\n\n---\n\n### Self-hosted HTTP (Option 2)\n\n> Start `uvx statcan-mcp-server --transport http` first, then configure your client.\n\nMost clients need [`mcp-proxy`](https://github.com/sparfenyuk/mcp-proxy) to bridge stdio ↔ HTTP. Claude Code connects natively.\n\n**Claude Desktop** — Settings → Developer → Edit Config:\n```json\n{\n  \"mcpServers\": {\n    \"statcan\": {\n      \"command\": \"uvx\",\n      \"args\": [\"mcp-proxy\", \"--transport\", \"streamablehttp\", \"http://localhost:8000/mcp\"]\n    }\n  }\n}\n```\n\n**Claude Code**\n```bash\nclaude mcp add statcan --transport http http://localhost:8000/mcp --scope global\n```\n\n**Cursor / VS Code / Gemini** — same `mcp-proxy` wrapper, pointing to `http://localhost:8000/mcp`.\n\n---\n\n### Full local / stdio (Option 3)\n\n**Claude Desktop** — Settings → Developer → Edit Config:\n```json\n{\n  \"mcpServers\": {\n    \"statcan\": {\n      \"command\": \"uvx\",\n      \"args\": [\"statcan-mcp-server\", \"--db-path\", \"/Users/<you>/.statcan-mcp/statcan_data.db\"]\n    }\n  }\n}\n```\n> Pass `--db-path` with an absolute path. Claude Desktop overrides the subprocess `HOME` env var, which can break default path resolution.\n\n**Claude Code**\n```bash\nclaude mcp add statcan --scope global -- uvx statcan-mcp-server\n```\n\n**Cursor / VS Code / Gemini** — use `uvx statcan-mcp-server` as the stdio command.\n\n---\n\n## How Claude.ai Uses This Server\n\nClaude.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.\n\n**The pattern:**\n\n```\n1. MCP tools (small payloads — metadata only):\n   search_cubes_by_title(\"labour force\")  → productId\n   get_sdmx_structure(productId=...)      → dimension layout + codes\n   get_sdmx_key_for_dimension(...)        → OR key for large dimensions\n\n2. Python script (data never enters context):\n   url = \"https://mcp-statcan.onrender.com/files/sdmx/<pid>/<key>?lastNObservations=12\"\n   → validate URL domain → write to ./statcan_<pid>.csv → print summary only\n\n3. Follow-up script (analysis from local file):\n   rows = list(csv.DictReader(open(\"./statcan_<pid>.csv\")))\n   → filter / sort / aggregate → print only the result\n```\n\n`get_sdmx_data` on the hosted server always returns a `download_csv` URL instead of inline data — data stays out of the context window regardless of response size.\n\n**Claude Code (bash sandbox)** uses the `statcan` CLI instead:\n\n```bash\nstatcan search \"labour force\"\nstatcan download 14-10-0287-01 --last 12 --output ./lfs.csv\nawk -F',' 'NR>1 && $1==\"Canada\"' ./lfs.csv | sort -t',' -rn -k5 | head -10\n```\n\n---\n\n## MCP Prompts\n\nThe server ships five prompts accessible as slash commands in supported clients. Each has dual instructions — Claude Code (bash) and Claude.ai web (Python script).\n\n| Prompt | What it teaches |\n|---|---|\n| `/statcan-data-lookup` | End-to-end: search → structure → build key → fetch to local file → analyze |\n| `/sdmx-key-builder` | SDMX key syntax: wildcards, OR keys, time parameters, download URL format |\n| `/statcan-download` | Download a specific table: CLI commands + Python script alternative |\n| `/statcan-explore` | Sample before committing: 3-period fetch, column layout, size estimate |\n| `/statcan-vector-pipeline` | Multi-series download and cross-series comparison |\n\n**Usage in Claude Code:**\n```\n/statcan-data-lookup topic=\"consumer price index\" analysis_goal=\"trend last 5 years\"\n/statcan-download product_id=18100004 last_n=24\n```\n\n---\n\n## statcan CLI\n\nA standalone CLI for downloading StatCan data without an LLM. Outputs pipe-friendly CSV/JSON to stdout; progress and errors go to stderr.\n\n**Install:**\n```bash\npip install statcan-mcp-server   # or: uvx statcan-mcp-server (no install)\n```\n\n**Commands:**\n\n```\nstatcan search <term>            Search tables by keyword\nstatcan metadata <product-id>    Show table structure (dimensions + members)\nstatcan download <product-id>    Download observations via SDMX\nstatcan vector <vector-id>...    Download one or more vector series\nstatcan codeset                  Show StatCan code definitions (UOM, frequency, etc.)\n```\n\n**Common usage:**\n\n```bash\n# Find a table\nstatcan search \"consumer price index\"\nstatcan search \"labour force\" --max-results 10 --format json\n\n# Inspect structure before downloading\nstatcan metadata 18-10-0004-01\nstatcan metadata 18100004 --full        # show all dimension members\n\n# Download data\nstatcan download 18-10-0004-01 --last 12 --output cpi.csv\nstatcan download 18-10-0004-01 --key \"1.1.1\" --start 2020-01 --end 2024-12\nstatcan download 18-10-0004-01 --last 5 --dry-run   # preview SDMX URL\n\n# Download by vector ID\nstatcan vector v41690973 --last 24 --output series.csv\nstatcan vector v41690973 v41690974 --last 12 --output multi.csv\n\n# Decode numeric codes\nstatcan codeset --type uom\nstatcan codeset --type frequency --format json\n```\n\n**Output formats:** `csv` (default for download/vector), `table` (default for search/metadata/codeset), `json`\n\n**Pipe patterns:**\n```bash\n# Top 10 by value\nstatcan download 14-10-0287-01 --last 1 --format csv \\\n  | awk -F',' 'NR>1' | sort -t',' -k5 -rn | head -10\n\n# Extract unique geographies\nstatcan download 14-10-0287-01 --last 1 --format csv \\\n  | awk -F',' 'NR>1 {print $1}' | sort -u\n\n# Chain search → download\nPID=$(statcan search \"CPI\" --format json | python3 -c \"import sys,json; print(json.load(sys.stdin)[0]['Product ID'])\")\nstatcan download $PID --last 12 --output cpi.csv\n```\n\nFor the complete CLI reference see [cli.md](cli.md).\n\n---\n\n## Features & Tools\n\n### SDMX Tools — server-side filtered data fetch\n\nOnly the slice you request is returned. No downloading full tables.\n\n| Tool | Description |\n|---|---|\n| `get_sdmx_structure` | Dimension codelists + key syntax for a table. Call before `get_sdmx_data`. |\n| `get_sdmx_data` | Filtered observations by `productId` + key. Returns a CSV download URL on the hosted server — data stays out of context. |\n| `get_sdmx_vector_data` | Observations for a single vectorId via SDMX. |\n| `get_sdmx_key_for_dimension` | All leaf member IDs for a large dimension as a ready-to-paste OR key. Use when a dimension has >30 codes (e.g. NOC, CMAs). |\n\n**Key syntax** (passed to `get_sdmx_data`):\n- `\"1.2.1\"` — Geography=1, Gender=2, Age=1\n- `\".2.1\"` — all geographies (wildcard), Gender=2, Age=1\n- `\"1+2.2.1\"` — Geography 1 or 2, Gender=2, Age=1\n\n> **Note:** Wildcard (`.`) on dimensions with >30 codes returns a sparse, unpredictable sample. Use `get_sdmx_key_for_dimension` to get the correct OR key.\n\n### WDS Discovery & Metadata\n\n| Tool | Description |\n|---|---|\n| `search_cubes_by_title` | Full-text search across all StatCan tables. AND logic, capped at 25 results. |\n| `get_all_cubes_list` / `_lite` | Paginated table inventory (`offset`/`limit`, default 100/page). |\n| `get_cube_metadata` | Dimension info, member lists, date ranges. `summary=True` caps members at 10 per dimension. |\n| `get_code_sets` | Decode StatCan numeric codes (frequency, UOM, scalar factor, status). |\n\n### WDS Series Resolution & Change Detection\n\n| Tool | Description |\n|---|---|\n| `get_series_info` | Resolve `{productId, coordinate}` pairs to vectorId + metadata. |\n| `get_series_info_from_vector` | Resolve a vectorId to productId, coordinate, titles, frequency. |\n| `get_changed_cube_list` | Tables updated on a specific date. |\n| `get_changed_series_list` | Series updated on a specific date. |\n| `get_changed_series_data_from_cube_pid_coord` | Data points that changed for a coordinate. |\n| `get_changed_series_data_from_vector` | Data points that changed for a vectorId. |\n| `get_bulk_vector_data_by_range` | Multiple vectors filtered by release date range. |\n\n### Composite & Database Tools *(local/stdio mode only)*\n\nThese tools are not available on the hosted Render server — SQLite is per-process and not shared across users.\n\n| Tool | Description |\n|---|---|\n| `fetch_vectors_to_database` | Fetch vectors by reference period range and store to SQLite. |\n| `store_cube_metadata` | Fetch full cube metadata into SQLite — browse all members and vectorIds with SQL. |\n| `query_database` | Read-only SQL against the local SQLite database. |\n| `create_table_from_data` / `insert_data` | Create or append to a table. |\n| `list_tables` / `get_table_schema` / `drop_table` | Database utilities. |\n\n### Typical workflow\n\n**Claude.ai web (hosted server):**\n```\n1. search_cubes_by_title(\"unemployment rate\")\n   → productId e.g. 14100287\n\n2. get_sdmx_structure(productId=14100287)\n   → dimension positions + sample codes\n\n3. get_sdmx_key_for_dimension(productId=14100287, dimension_position=3)\n   → or_key for large dimensions\n\n4. get_sdmx_data(productId=14100287, key=\".2.1\", lastNObservations=24)\n   → returns download_csv URL\n\n5. Python script: validate URL domain → write to ./statcan_14100287.csv → analyze → print summary\n```\n\n**Claude Code (bash sandbox):**\n```bash\nstatcan search \"unemployment rate\"\nstatcan metadata 14100287\nstatcan download 14-10-0287-01 --last 24 --output ./lfs.csv\nawk -F',' 'NR>1 && $1==\"Canada\"' ./lfs.csv | sort -t',' -rn -k5 | head -10\n```\n\n---\n\n## Project Structure\n\n```\nsrc/\n├── api/\n│   ├── cube/\n│   │   ├── discovery.py         # search_cubes_by_title, get_all_cubes_list\n│   │   ├── metadata.py          # get_cube_metadata\n│   │   └── series.py            # get_series_info, change detection\n│   ├── vector/\n│   │   └── vector_tools.py      # vector series, bulk range fetch\n│   ├── sdmx/\n│   │   └── sdmx_tools.py        # get_sdmx_structure, get_sdmx_data, get_sdmx_key_for_dimension\n│   ├── composite_tools.py       # fetch_vectors_to_database, store_cube_metadata (stdio only)\n│   └── metadata_tools.py        # get_code_sets\n├── cli/\n│   ├── main.py                  # statcan CLI entry point (Typer app)\n│   ├── output.py                # write_output, format helpers\n│   └── commands/\n│       ├── search.py            # statcan search\n│       ├── metadata.py          # statcan metadata\n│       ├── download.py          # statcan download\n│       ├── vector.py            # statcan vector\n│       └── codeset.py           # statcan codeset\n├── db/                          # SQLite connection, schema, queries (stdio only)\n├── models/                      # Pydantic input models\n├── util/\n│   ├── registry.py              # ToolRegistry — @decorator → MCP Tool schema\n│   ├── truncation.py            # Response truncation + pagination guidance\n│   ├── sdmx_json.py             # SDMX-JSON → tabular rows\n│   └── cache.py                 # 1-hour TTL cache for cube list\n├── config.py                    # BASE_URL, SDMX_BASE_URL, RENDER_BASE_URL, TRANSPORT, PORT\n└── server.py                    # create_server(), MCP Prompts, HTTP routes (/files/sdmx/), CLI\n```\n\n---\n\n## Known Issues\n\n| Issue | Status | Workaround |\n|---|---|---|\n| **\"Unable to open database file\" on Claude Desktop** | Active | Pass `--db-path /Users/<you>/.statcan-mcp/statcan_data.db` in your config |\n| **SSL verification disabled** | Active | `VERIFY_SSL = False` in all API calls — StatCan cert issues made this necessary |\n| **`lastNObservations` + `startPeriod`/`endPeriod` → 406** | Active | Use one or the other, not both |\n| **OR syntax for Geography dimension unreliable** | Active | Use wildcard (`.`) for Geography; OR works fine for other dimensions |\n| **Wildcard returns sparse data for large dimensions** | Mitigated | Use `get_sdmx_key_for_dimension` to get the full OR key (e.g. NOC, CMAs) |\n| **Context overflow may cause data fabrication** | Mitigated | Hosted server returns `download_csv` URL — data processed via script, not context |\n\n---\n\n<div align=\"center\">Made with ❤️❤️❤️ for Statistics Canada</div>\n\n<div align=\"center\">\n<a href=\"https://github.com/Aryan-Jhaveri/mcp-statcan\" target=\"_blank\">GitHub</a> •\n<a href=\"https://github.com/Aryan-Jhaveri/mcp-statcan/issues\" target=\"_blank\">Report Bug</a> •\n<a href=\"https://www.statcan.gc.ca/\" target=\"_blank\">Statistics Canada</a>\n</div>\n",
  "bytes": 19070,
  "sha": "649d9259cb9e79f4b99176628a0300b0efcb3251ec126e2846ac2702db633482",
  "repo_slug": "aryan-jhaveri/mcp-statcan",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_aryan_jhaveri_mcp_statcan_9483bfd1/readme"
}