Un serveur de données canadiennes pour les gouverner tous.
Les données canadiennes se trouvent en des dizaines d'endroits. MapleStats en réunit 175 derrière une seule connexion, pour que votre agent IA trouve, récupère et cite les chiffres pendant que vous vous concentrez sur ce qu'ils signifient.
Une seule connexion
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
La plupart des appels de données peuvent revenir sous forme de script R, Python, Stata ou Julia (ou une requête Power Query pour Excel) 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 connecte MapleStats au serveur hébergé : rien à installer, ni compte ni clé. L'agent lit les étapes dans le dépôt et s'occupe du reste.
Connectez le serveur MCP MapleStats à cet agent. Suivez les étapes 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 ajoutez vous-même le serveur hébergéOu exécutez-le sur votre machineParcourir les 358 outils
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 financiers, condamnés à expliquer le taux…
Mais MapleStats est le MCP unique pour les gouverner tous.
Trop d'endroits où chercher, trop d'interfaces à apprendre.
Les MCP ont 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. J'ai conçu MapleStats pour prendre le chemin inverse : un seul MCP pour les données ouvertes canadiennes.
Centraliser l'interface, pas les données.
Le serveur ne garde aucun jeu de données, seulement des caches de courte durée. 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.
Démos
Ce que vous pouvez en faire
Neuf questions de neuf 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
Ce qu'attendent les marchés obligataires, en une ligne
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 huit 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
- Spécialistes du marketing Ce que coûte une carte à récompenses
- Gestionnaires de patrimoine Qui alimente le REER
- Scientifiques L'apprentissage automatique au bureau des brevets
- Analystes financiers 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.
- 42
- sources fédérales et nationales
- 133
- sources provinciales, territoriales et municipales
- 12/13
- provinces et territoires avec des données locales
- 175
- sources, une seule connexion
Voir chaque source et ce qu'elle couvreParcourir les 358 outils
Fonctionnement
D'une question à un chiffre sourcé
Un agent connecté voit trois outils : plan_query, search_tools et call_tool. Les 357 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
Logement : mises en chantier, loyers, prix, prêts hypothécaires
cmhc_Mises en chantier, achèvements, loyers et inoccupation de la SCHL : category_level_1 et _2list_ categories cmhc_options column_field et row_field pour cette catégorie et cette géographieget_ table_ options cmhc_le tableau : category_level_1, category_level_2, column_field et row_field (row_field='TIMESERIES' pour une série), geography_type et geography_idget_ table_ data cmhc_tableaux Excel publiés par la SCHL : category='rental-market' ou 'household-characteristics'dt_ list_ tables wds_Indice des prix des logements neufs et permis de bâtir de Statistique Canadasearch_ cubes boc_taux hypothécaires et taux directeur de la Banque du Canadasearch_ series crea_Indice des prix des propriétés MLS® de l'ACI (prix de revente) : lien de téléchargement, attribution et conditions seulement ; aucune valeur, car les conditions de l'ACI en interdisent la publicationget_ hpi_ links statcan_frais de logement et mode d'occupation au recensement : level, geography_codes (DGUID tirés de statcan_census_profile_search_geography) et characteristic_codescensus_ profile_ get_ data
Mise en garde La SCHL publie par région métropolitaine de recensement et par centre ; les indices de prix de Statistique Canada sont aussi par RMR, mais les deux sources délimitent certaines régions différemment : nommez la géographie de chaque chiffre.
Taux d'intérêt, taux de change et marchés
boc_trouver la série Valet (p. ex. FXUSDCAD, V39079)search_ series boc_extraire la série pour la périodeget_ observations
Calgary (ville)
socrata_recherche avec 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 or list Bank of Canada Valet's ~16,000 statistical series.series - 2boc_
get_ Banque du CanadaGet observations for one or more Bank of Canada Valet series.observations - 3boc_
get_ Banque du CanadaGet a Bank of Canada Valet group's description and member series.group - 4boc_
search_ Banque du CanadaSearch or list Bank of Canada Valet's series groups (~2,500).groups - 5boc_
get_ Banque du CanadaGet observations for every series in a Bank of Canada Valet group.group_ observations
Les 5 premiers des 357 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 2026Demande
Quel est le taux cible du financement à un jour de la Banque du Canada ?
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
- Interrogé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, ou une requête Power Query pour Excel, 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 pageDemande
Écrivez-moi un script qui va chercher ces données, pour que je puisse le relancer moi-même.
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, )
Installation
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"]
}
}
}
Toutes les sources. Un seul serveur.
Voir les données
| Sujet | Outils |
|---|---|
| Provinces et villes : catalogues de données ouvertes, provincial, municipal | 117 |
| Statistique : statistiques et recensement | 92 |
| Terre et énergie : agriculture et alimentation, environnement et risques naturels, énergie et mines, géographie, transport et sécurité | 55 |
| Population : santé, logement, immigration | 40 |
| Argent et affaires : monnaie, prix et finances publiques, entreprises, PI et concurrence | 30 |
| Parlement : parlement, droit et élections | 21 |