Un MCP pour les gouverner tous.
MapleStats est un seul serveur MCP pour les données canadiennes accessibles au public. Le MCP (Model Context Protocol) est la norme ouverte qu'utilisent les agents IA pour accéder à des outils et à des données externes. Connectez MapleStats à votre agent IA pour obtenir des statistiques et des rapports précis et bien sourcés, tirés de 86 sources, dont Statistique Canada, la Banque du Canada et la SCHL. Ne vous souciez plus de trouver des données canadiennes : concentrez-vous sur ce qui compte vraiment pour vous.
Une seule connexion
Le MCP est la façon standard de brancher des données sur un agent IA. MapleStats réunit les organismes et portails canadiens derrière une seule connexion, en français ou en anglais, avec la source de chaque chiffre.
Des scripts à relancer
Tout appel de données peut revenir sous forme de script R, Python, Stata ou Julia qui va chercher les mêmes chiffres à la source : l'analyse tourne sans l'agent.
Le plus simple : demandez à votre agent Une seule requête installe MapleStats et le connecte. L'agent lit les étapes d'installation dans le dépôt et s'occupe du reste.
Installe le serveur MCP MapleStats et connecte-le à cet agent. Suis les étapes d'installation de https://github.com/dsanchezp18/maplestats-mcp- Copiez la requête.
- Collez-la dans Claude Code, Codex, Cursor ou tout agent qui peut exécuter des commandes sur votre ordinateur.
- Redémarrez l'agent quand il vous le dit, puis demandez des données.
Ou installez-le vous-mêmeParcourir les 215 outils
Version 0.1.0, bêta. S'exécute sur votre machine par stdio, sans compte ni clé d'API.
3
MCP pour les statisticiens, gardiens des poids et des marges,
7
pour les démographes, qui comptent ceux qui viennent s'établir,
9
pour les analystes, condamnés à expliquer le taux…
Bon, peut-être pas tout à fait.
Trop d'endroits où chercher, trop d'interfaces à apprendre.
Le MCP a facilité la connexion des agents IA aux données, et l'on s'est mis à construire des serveurs étroits pour des pans étroits de l'écosystème canadien : un pour Statistique Canada, un autre pour la Banque du Canada, d'autres pour les portails de données ouvertes. Chacun peut être utile. Ensemble, ils recréent un vieux problème canadien : trop de choix avant de poser une question pourtant simple. MapleStats prend le chemin inverse : un seul MCP pour les données ouvertes canadiennes.
Centraliser l'interface, pas les données.
Le serveur ne garde aucune copie. Il interroge chaque source directement ; chaque chiffre vient de son éditeur.
Une couche de découverte, de nombreux adaptateurs.
Chaque organisme garde son adaptateur. L'agent ne voit qu'une recherche commune à tous.
Partir de la question, pas du portail.
L'agent n'a pas à savoir si la réponse est un tableau de StatCan, une série Valet, un jeu de données de la SCHL ou une ressource CKAN.
Études de cas
Que pouvez-vous en faire
Huit questions de huit métiers différents, chacune résolue par des appels aux outils de MapleStats. En voici une : celle d'un macroéconomiste qui lit la courbe des taux.
Macroéconomistes
Quand la courbe des taux s'inverse
L'écart entre les rendements des obligations du gouvernement du Canada à 10 ans et à 2 ans, chaque jour ouvrable depuis 2001. Quand il passe sous zéro, les marchés s'attendent à une baisse des taux, le plus souvent parce qu'ils prévoient un ralentissement. La courbe s'est inversée en 2007, 2019 à 2020 et 2022 à 2024.
Voir les données
| Année | Moyenne | Mois le plus bas | Mois le plus haut |
|---|---|---|---|
| 2001 | 117 pb | 41 pb | 213 pb |
| 2002 | 169 pb | 139 pb | 225 pb |
| 2003 | 155 pb | 124 pb | 188 pb |
| 2004 | 165 pb | 126 pb | 199 pb |
| 2005 | 88 pb | 23 pb | 131 pb |
| 2006 | 18 pb | 6 pb | 32 pb |
| 2007 | 9 pb | -5 pb | 32 pb |
| 2008 | 95 pb | 54 pb | 171 pb |
| 2009 | 200 pb | 167 pb | 218 pb |
| 2010 | 169 pb | 139 pb | 221 pb |
| 2011 | 141 pb | 110 pb | 158 pb |
| 2012 | 76 pb | 65 pb | 100 pb |
| 2013 | 115 pb | 76 pb | 157 pb |
| 2014 | 118 pb | 84 pb | 150 pb |
| 2015 | 97 pb | 77 pb | 117 pb |
| 2016 | 69 pb | 49 pb | 96 pb |
| 2017 | 69 pb | 34 pb | 95 pb |
| 2018 | 29 pb | 8 pb | 51 pb |
| 2019 | -0 pb | -18 pb | 14 pb |
| 2020 | 24 pb | -12 pb | 49 pb |
| 2021 | 88 pb | 47 pb | 124 pb |
| 2022 | -13 pb | -87 pb | 58 pb |
| 2023 | -91 pb | -123 pb | -71 pb |
| 2024 | -33 pb | -69 pb | 17 pb |
| 2025 | 63 pb | 36 pb | 81 pb |
| 2026 | 70 pb | 61 pb | 84 pb |
Les sept autres
- Statisticiens Le baccalauréat, province par province
- Démographes Ceux qui viennent s'établir : Edmonton et Calgary
- Urbanistes Le Canada construit en hauteur, pas en largeur
- Microéconomistes Le faible revenu d'une génération à l'autre
- Marketing Ce que coûte une carte à récompenses
- Scientifiques L'apprentissage automatique au bureau des brevets
- Analystes Le taux directeur depuis 2015
Sources
D'où viennent les données
Les organismes fédéraux répondent à la plupart des questions nationales ; les organismes provinciaux et les catalogues de données ouvertes ajoutent le détail local. Chaque résultat vient directement de son éditeur.
- 30
- sources fédérales et nationales
- 56
- sources provinciales, territoriales et municipales
- 12/13
- provinces et territoires avec des données locales
- 215
- outils derrière une seule connexion
Voir chaque source et ce qu'elle couvreParcourir les 215 outils
Fonctionnement
D'une question à un chiffre sourcé
Un agent connecté voit trois outils : plan_query, search_tools et call_tool. Les 214 autres restent hors de son contexte jusqu'à ce qu'une recherche les trouve. Tous les résultats ci-dessous sont réels. Le serveur a produit le plan, la recherche et les scripts lors de la construction de cette page ; l'appel de données a été enregistré le 27 septembre 2026.
-
01
Planifier
plan_queryLe planificateur lit la question et énumère les organismes à interroger, dans l'ordre, avec les précautions à prendre pour combiner leurs résultats. Les lieux qu'il reconnaît ajoutent leurs portails locaux. Le planificateur rédige pour l'instant son plan en anglais.
plan_queryExécuté à la construction de la pageQuestion
Comment les loyers et les taux d'intérêt ont-ils évolué à Calgary depuis 2020 ?
Plan
Housing: starts, rents, prices, mortgages
cmhc_CMHC starts, completions, rents and vacancy by centrelist_ categories cmhc_pull the CMHC table for the place and periodget_ table_ data wds_StatCan New Housing Price Index, building permitssearch_ cubes boc_Bank of Canada mortgage and policy ratessearch_ series statcan_census shelter cost and tenure by areacensus_ profile_ get_ data
Précaution CMHC reports by census metropolitan area and centre; StatCan price indexes are by CMA too, but the two define some areas differently, so name the geography each figure uses.
Interest rates, exchange rates and markets
boc_find the Valet series (e.g. FXUSDCAD, V39079)search_ series boc_pull the series for the periodget_ observations
Calgary (ville)
socrata_search with portal='calgary'search_ datasets
-
02
Trouver
search_toolsLa recherche classe le catalogue selon une requête en langage courant et renvoie les 5 premiers outils. Le français fonctionne, car chaque outil porte des mots-clés français, même si sa description est en anglais. Le panneau exécute l'index du serveur dans votre navigateur : essayez votre propre requête.
search_toolsS'exécute dans votre navigateurEssayez
- 1boc_
search_ Banque du CanadaSearch Bank of Canada Valet's ~16,000 statistical series by keyword.series - 2boc_
get_ Banque du CanadaGet observations for one or more Bank of Canada Valet series.observations - 3boc_
search_ Banque du CanadaSearch Bank of Canada Valet's series groups by keyword.groups - 4boc_
get_ Banque du CanadaGet a Bank of Canada Valet group's description and member series.group - 5boc_
get_ Banque du CanadaGet observations for every series in a Bank of Canada Valet group.group_ observations
Les 5 premiers des 214 outils indexés, classés par le même index BM25 que search_tools.
- 1boc_
-
03
Récupérer
call_toolL'agent appelle l'outil par son nom. Le résultat est un objet typé, et son bloc de provenance, mis en évidence ici, donne l'URL exacte de la source et l'heure de la requête.
call_toolEnregistré le 27 septembre 2026Requête
{"name": "boc_get_observations", "arguments": {"series_names": ["V39079"], "recent": 3}}Résultat
Target for the overnight rate (business daily) V39079Date Valeur 24 septembre 2026 2,25 23 septembre 2026 2,25 22 septembre 2026 2,25 Provenance
- URL de la source
- https://www.bankofcanada.ca/valet/observations/V39079/json
- Consultée le
- 27 septembre 2026, 17 h 00 UTC
- Type de résultat
boc.ObservationsResult
Afficher la réponse complète en JSON
{ "series": { "V39079": {"name": "V39079", "label": "Target for the overnight rate (business daily)", "description": "Also called the policy interest rate, the average rate that the Bank of Canada wants to see in the market for overnight money market financing. (V39079)"} }, "observations": [ {"ref_date": "2026-09-24", "values": {"V39079": 2.25}}, {"ref_date": "2026-09-23", "values": {"V39079": 2.25}}, {"ref_date": "2026-09-22", "values": {"V39079": 2.25}} ], "provenance": { "source": "boc", "url": "https://www.bankofcanada.ca/valet/observations/V39079/json", "queried_at": "2026-09-27T17:00:48.227537Z", "as_of": null, "freshness": null, "coverage": null, "limits": null, "cached": false, "schema_name": "boc.ObservationsResult", "reproduce": "For R, Python, Stata and Julia scripts that fetch and clean this data, call reproduce_code with this tool's name and arguments." }} -
04
Reproduire
reproduce_codeLe même appel devient un script R, Python, Stata ou Julia qui télécharge, vérifie et nettoie les données, pour que l'analyse ne dépende pas de l'agent.
reproduce_codeExécuté à la construction de la pageRequête
{"tool_name": "boc_get_observations", "arguments": {"series_names": ["V39079"], "recent": 3}}R
# ============================================================ # Bank of Canada Valet: V39079 # Purpose: Fetch the data behind MapleStats MCP's boc_get_observations # (exact: Valet request rebuilt from the tool's arguments) # Inputs: https://www.bankofcanada.ca/valet/observations/V39079/json?recent=3 # Outputs: data/raw/valet_observations.json; the prepared table as `data` # ============================================================ # 0. Setup ---- library(dplyr) library(httr2) library(janitor) library(jsonlite) library(lubridate) library(readr) library(stringr) library(tibble) library(tidyr) dir.create("data/raw", recursive = TRUE, showWarnings = FALSE) # 1. Read inputs ---- response <- request("https://www.bankofcanada.ca/valet/observations/V39079/json?recent=3") |> req_perform() writeLines(resp_body_string(response), "data/raw/valet_observations.json") payload <- fromJSON("data/raw/valet_observations.json", flatten = TRUE) data <- as_tibble(payload[["observations"]]) # 2. Check inputs ---- stopifnot("https://www.bankofcanada.ca/valet/observations/V39079/json?recent=3 returned no rows" = nrow(data) > 0) # 3. Prepare data ---- data <- data |> clean_names() # Valet nests each series as <series>.v; make one row per date and series. data <- data |> pivot_longer(-d, names_to = "series", values_to = "value") |> mutate( series = str_remove(series, "_v$") |> str_to_upper(), value = as.numeric(value), date = ymd(d) ) |> select(date, series, value) # Standard cleaning: trimmed text, empty strings as missing, and numbers # stored as text converted to numbers. data <- data |> mutate(across(where(is.character), \(x) na_if(str_trim(x), ""))) |> type_convert()Python
# ============================================================ # Bank of Canada Valet: V39079 # Purpose: Fetch the data behind MapleStats MCP's boc_get_observations # (exact: Valet request rebuilt from the tool's arguments) # Inputs: https://www.bankofcanada.ca/valet/observations/V39079/json?recent=3 # Outputs: data/raw/valet_observations.json; the prepared table as `data` # ============================================================ # %% 0. Setup import json import re import unicodedata from pathlib import Path import httpx import polars as pl RAW_DIR = Path("data/raw") RAW_DIR.mkdir(parents=True, exist_ok=True) raw_path = RAW_DIR / "valet_observations.json" # %% 1. Read inputs with httpx.Client( http2=True, follow_redirects=True, timeout=300, headers={"User-Agent": "Mozilla/5.0 (compatible; research script)"}, ) as client: response = client.get('https://www.bankofcanada.ca/valet/observations/V39079/json?recent=3') response.raise_for_status() raw_path.write_bytes(response.content) payload = json.loads(raw_path.read_text(encoding="utf-8")) records = payload['observations'] data = pl.json_normalize(records, strict=False) # %% 2. Check inputs assert data.height > 0, "https://www.bankofcanada.ca/valet/observations/V39079/json?recent=3 returned no rows" # %% 3. Prepare data # Valet nests each series as <series>.v; make one row per date and series. data = data.unpivot(index="d", variable_name="series", value_name="value") data = data.with_columns( pl.col("d").str.to_date().alias("date"), pl.col("series").str.replace(r"\.v$", ""), pl.col("value").cast(pl.Float64, strict=False), ).select("date", "series", "value") # Standard cleaning: snake_case names without accents (PÉRIODE -> periode, # referenceNumber -> reference_number, as janitor does in R), trimmed text, # empty strings as missing. Names that clean alike are numbered as janitor # numbers them (Indicator, indicator -> indicator, indicator_2). clean_names = [ re.sub( r"[^0-9a-z]+", "_", re.sub( r"([a-z0-9])([A-Z])", r"\1_\2", unicodedata.normalize("NFKD", column).encode("ascii", "ignore").decode(), ).lower(), ).strip("_") for column in data.columns ] while len(set(clean_names)) < len(clean_names): name_counts = {} numbered = [] for name in clean_names: name_counts[name] = name_counts.get(name, 0) + 1 count = name_counts[name] numbered.append(name if count == 1 else f"{name}_{count}") clean_names = numbered data = data.rename(dict(zip(data.columns, clean_names))) data = data.with_columns(pl.col(pl.Utf8).str.strip_chars().replace("", None))Stata
* ============================================================ * Bank of Canada Valet: V39079 * Purpose: Fetch the data behind MapleStats MCP's boc_get_observations * (exact: Valet request rebuilt from the tool's arguments) * Inputs: https://www.bankofcanada.ca/valet/observations/V39079/json?recent=3 * Outputs: data/raw/valet_observations.json; the prepared table as `data` * ============================================================ version 18 clear all set more off * 0. Setup capture mkdir "logs" capture log close log using "logs/boc_get_observations.log", replace capture mkdir "data" capture mkdir "data/raw" * 1. Read inputs * Stata reads no JSON or HTML and truncates long column names, so its * built-in Python (Stata 16+) fetches, filters and writes a CSV. Point * Stata at a Python with these packages first: python set exec <path>. python: import json from pathlib import Path import httpx import polars as pl RAW_DIR = Path("data/raw") RAW_DIR.mkdir(parents=True, exist_ok=True) raw_path = RAW_DIR / "valet_observations.json" with httpx.Client(http2=True, follow_redirects=True, timeout=300, headers={"User-Agent": "Mozilla/5.0 (compatible; research script)"}) as client: response = client.get('https://www.bankofcanada.ca/valet/observations/V39079/json?recent=3') response.raise_for_status() raw_path.write_bytes(response.content) payload = json.loads(raw_path.read_text(encoding="utf-8")) records = payload['observations'] data = pl.json_normalize(records, strict=False) nested = [name for name, dtype in data.schema.items() if dtype.is_nested()] data = data.with_columns(pl.col(name).map_elements(lambda value: json.dumps(value.to_list() if isinstance(value, pl.Series) else value, default=str), return_dtype=pl.Utf8) for name in nested) data.write_csv(RAW_DIR / "valet_observations_prepared.csv") end import delimited "data/raw/valet_observations_prepared.csv", clear varnames(1) encoding("utf-8") * 2. Check inputs assert _N > 0 * 3. Prepare data * One column per series (<series>_v), one row per date. generate date = date(d, "YMD") format date %td drop d * Standard cleaning: lower-case names, trimmed text, and numbers stored as * text converted (destring leaves genuinely non-numeric text alone). * rename *, lower stops at a clash (Indicator next to indicator), so names * that lower-case alike are numbered as janitor numbers them (indicator, * indicator_2), then renamed in one group rename, which allows swaps. local names foreach var of varlist _all { local names `names' `=strlower("`var'")' } local dups : list dups names while "`dups'" != "" { local numbered local before foreach name of local names { local count 1 foreach earlier of local before { if "`earlier'" == "`name'" local ++count } local before `before' `name' if `count' > 1 { local name = substr("`name'", 1, 32 - strlen("_`count'")) + "_`count'" } local numbered `numbered' `name' } local names `numbered' local dups : list dups names } local old_names local new_names local i 0 foreach var of varlist _all { local ++i local name : word `i' of `names' if "`name'" != "`var'" { local old_names `old_names' `var' local new_names `new_names' `name' } } if "`old_names'" != "" { rename (`old_names') (`new_names') } quietly ds, has(type string) local text_vars `r(varlist)' foreach var of local text_vars { replace `var' = strtrim(`var') } destring, replace log closeJulia
# ============================================================ # Bank of Canada Valet: V39079 # Purpose: Fetch the data behind MapleStats MCP's boc_get_observations # (exact: Valet request rebuilt from the tool's arguments) # Inputs: https://www.bankofcanada.ca/valet/observations/V39079/json?recent=3 # Outputs: data/raw/valet_observations.json; the prepared table as `data` # ============================================================ # 0. Setup using DataFrames using Downloads using JSON3 using Tables using TidierData mkpath("data/raw") # 1. Read inputs Downloads.download("https://www.bankofcanada.ca/valet/observations/V39079/json?recent=3", "data/raw/valet_observations.json") payload = JSON3.read(read("data/raw/valet_observations.json", String)) records = payload["observations"] data = DataFrame(Tables.dictrowtable(records)) # 2. Check inputs @assert nrow(data) > 0 "https://www.bankofcanada.ca/valet/observations/V39079/json?recent=3 returned no rows" # 3. Prepare data # Standard cleaning: snake_case names, trimmed text, empty strings as missing. data = @chain data begin @clean_names end data = mapcols( col -> eltype(col) <: Union{Missing, AbstractString} ? [ismissing(x) || isempty(strip(x)) ? missing : strip(x) for x in col] : col, data, )
Connexion
Ajoutez-le à votre client
Votre client lance le serveur avec uv au besoin. Il n'y a rien à héberger ni aucun port à ouvrir.
Claude Code
Exécutez cette commande une fois dans un terminal. --scope user rend le serveur accessible dans tous les projets ; sans cette option, il n'est ajouté qu'au projet courant.
claude mcp add --scope user maplestats -- uvx maplestats-mcp
Claude Desktop
Dans Claude Desktop, ouvrez Réglages, puis Développeur, puis Modifier la configuration. Ajoutez le serveur au fichier qui s'ouvre, enregistrez et redémarrez Claude.
{
"mcpServers": {
"maplestats": {
"command": "uvx",
"args": ["maplestats-mcp"]
}
}
}
Le fichier se trouve dans ~/Library/Application Support/Claude/claude_desktop_config.json sous macOS et dans %APPDATA%\Claude\claude_desktop_config.json sous Windows.
Cursor
Le lien ouvre Cursor avec l'entrée déjà remplie. Pour l'ajouter à la main, placez-la dans ~/.cursor/mcp.json pour tous les projets, ou dans .cursor/mcp.json pour un seul projet.
{
"mcpServers": {
"maplestats": {
"command": "uvx",
"args": ["maplestats-mcp"]
}
}
}
VS Code
Pour l'ajouter à la main, notez que VS Code utilise la clé servers, et non mcpServers. Placez ceci dans .vscode/mcp.json d'un espace de travail, ou lancez la commande MCP: Open User Configuration dans la palette de commandes pour l'ajouter à tous vos espaces de travail.
{
"servers": {
"maplestats": {
"type": "stdio",
"command": "uvx",
"args": ["maplestats-mcp"]
}
}
}
Codex CLI
Ajoutez-le depuis le terminal, ou écrivez vous-même la même entrée dans ~/.codex/config.toml.
codex mcp add maplestats -- uvx maplestats-mcp
[mcp_servers.maplestats]
command = "uvx"
args = ["maplestats-mcp"]
Gemini CLI
Ajoutez-le depuis le terminal (-s user pour tous les projets), ou écrivez l'entrée dans ~/.gemini/settings.json, à côté des réglages qui s'y trouvent déjà.
gemini mcp add -s user maplestats uvx maplestats-mcp
{
"mcpServers": {
"maplestats": {
"command": "uvx",
"args": ["maplestats-mcp"]
}
}
}
Autres clients
La plupart des clients MCP acceptent cette entrée mcpServers. Si vous avez installé la commande avec uv tool install ou pip, utilisez "command": "maplestats-mcp" et retirez args.
{
"mcpServers": {
"maplestats": {
"command": "uvx",
"args": ["maplestats-mcp"]
}
}
}
Remerciements
MapleStats s'appuie sur le travail ouvert de nombreuses personnes. Il a tiré des leçons des nombreux serveurs MCP de données canadiennes et gouvernementales qui l'ont précédé, et une bonne part de son architecture et de son inspiration vient d'EcuDataMCP, mon autre projet, un serveur MCP pour les données ouvertes du gouvernement de l'Équateur.
Des paquets libres existants ont aussi aidé, surtout les paquets R de Jens von Bergmann (mountainMath) et de leurs coauteurs :
cmhcLe module de la SCHL a été vérifié par rapport à sa rétro-ingénierie du Portail de l'information sur le marché de l'habitation.cansimLe code R que reproduce_code écrit pour les tableaux et vecteurs de Statistique Canada l'utilise.cancensusUn modèle pour l'accès aux données du recensement en R.canivtLit les fichiers Beyond 20/20. Les tableaux du recensement et les dépôts Borealis offerts seulement dans ce format lui sont confiés.
Merci aux développeurs qui ont conçu et partagé ces outils, et à toutes les personnes qui publient des données canadiennes au grand jour.
Pour d'autres façons d'obtenir des données canadiennes, dont certains de ces paquets, voyez les autres options à la page À propos.
Un MCP pour que l'Agent les trouve, un MCP pour les réunir tous.
Voir les données
| Sujet | Outils |
|---|---|
| Statistique : statistiques et recensement | 62 |
| Provinces et villes : catalogues de données ouvertes, provincial, municipal | 47 |
| Argent et affaires : monnaie, prix et finances publiques, entreprises, PI et concurrence | 31 |
| Terre et énergie : agriculture et alimentation, environnement et risques naturels, énergie, géographie, transport et sécurité | 31 |
| Population : santé, logement, immigration | 22 |
| Parlement : parlement, droit et élections | 20 |