MapleStatsMCP

One MCP to rule them all.

MapleStats is one MCP server for publicly available Canadian data. MCP, the Model Context Protocol, is the open standard AI agents use to reach outside tools and data. Connect MapleStats to your AI agent to retrieve precise, well-sourced statistics and reports from 86 sources, including Statistics Canada, the Bank of Canada and CMHC. Never worry about Canadian data retrieval again: focus on what actually matters to you.

One connection

MCP is the standard way to plug data into AI agents. MapleStats puts Canada's scattered agencies and portals behind one connection, in English or French, with the source on every number.

Scripts you can rerun

Any data call can come back as an R, Python, Stata or Julia script that fetches the same numbers straight from the source, so the analysis runs without the agent.

The easiest way: ask your agent One prompt installs MapleStats and connects it. The agent reads the setup steps from the repository and does the rest.

Install the MapleStats MCP server and connect it to this agent. Follow the setup steps in https://github.com/dsanchezp18/maplestats-mcp
  1. Copy the prompt.
  2. Paste it into Claude Code, Codex, Cursor or any agent that can run commands on your computer.
  3. Restart the agent when it says so, then ask for data.

Version 0.1.0, beta. Runs on your machine over stdio, with no account or API key.

3

MCPs for the statisticians, keepers of the weights and the margins,

7

for the demographers, counting who comes to stay,

9

for the analysts, doomed to explain the rate…

Okay, perhaps not quite.

Too many places to look, too many interfaces to learn.

As MCP made it easier to connect AI agents to data, we started building narrow servers for narrow pieces of the Canadian data ecosystem: one for Statistics Canada, another for the Bank of Canada, others for open-data portals. Each can be useful. Together they recreate an old Canadian data problem: too many choices before asking a relatively simple question. MapleStats takes the opposite approach, one MCP for Canadian open data.

Centralize the interface, not the data.

The server keeps no copy. It calls each source directly, so every number comes from its publisher.

One discovery layer, many adaptors.

Each agency keeps its own adaptor underneath. The agent sees one search across all of them.

Start from the question, not the portal.

The agent does not need to know whether the answer is a StatCan table, a Valet series, a CMHC dataset or a CKAN resource.

Case studies

What can you do with it

Eight questions, from eight kinds of work, each answered with MapleStats tool calls. Here is one of them: a macroeconomist reading the yield curve.

Macroeconomists

When the yield curve turns upside down

The gap between the 10-year and 2-year Government of Canada bond yields, every business day since 2001. When it drops below zero, markets expect rates to fall, usually because they expect the economy to slow. It inverted in 2007, 2019 to 2020 and 2022 to 2024.

The yield curve: 10-year minus 2-year Government of Canada benchmark bond yields, monthly average of daily values, with inverted months shaded-200 bp-100 bp0 bp100 bp200 bp300 bpInverted200520102015202020252001-01-01: 41 bp2001-02-01: 50 bp2001-03-01: 63 bp2001-04-01: 82 bp2001-05-01: 98 bp2001-06-01: 92 bp2001-07-01: 98 bp2001-08-01: 107 bp2001-09-01: 163 bp2001-10-01: 187 bp2001-11-01: 207 bp2001-12-01: 213 bp2002-01-01: 225 bp2002-02-01: 207 bp2002-03-01: 161 bp2002-04-01: 139 bp2002-05-01: 144 bp2002-06-01: 141 bp2002-07-01: 171 bp2002-08-01: 183 bp2002-09-01: 150 bp2002-10-01: 165 bp2002-11-01: 176 bp2002-12-01: 170 bp2003-01-01: 166 bp2003-02-01: 153 bp2003-03-01: 132 bp2003-04-01: 131 bp2003-05-01: 124 bp2003-06-01: 131 bp2003-07-01: 170 bp2003-08-01: 188 bp2003-09-01: 172 bp2003-10-01: 169 bp2003-11-01: 161 bp2003-12-01: 165 bp2004-01-01: 183 bp2004-02-01: 199 bp2004-03-01: 195 bp2004-04-01: 188 bp2004-05-01: 181 bp2004-06-01: 163 bp2004-07-01: 160 bp2004-08-01: 165 bp2004-09-01: 147 bp2004-10-01: 133 bp2004-11-01: 126 bp2004-12-01: 138 bp2005-01-01: 131 bp2005-02-01: 129 bp2005-03-01: 122 bp2005-04-01: 109 bp2005-05-01: 103 bp2005-06-01: 98 bp2005-07-01: 88 bp2005-08-01: 81 bp2005-09-01: 74 bp2005-10-01: 59 bp2005-11-01: 40 bp2005-12-01: 23 bp2006-01-01: 24 bp2006-02-01: 22 bp2006-03-01: 26 bp2006-04-01: 32 bp2006-05-01: 29 bp2006-06-01: 17 bp2006-07-01: 21 bp2006-08-01: 16 bp2006-09-01: 12 bp2006-10-01: 9 bp2006-11-01: 6 bp2006-12-01: 6 bp2007-01-01: 7 bp2007-02-01: 4 bp2007-03-01: 10 bp2007-04-01: 7 bp2007-05-01: -3 bp2007-06-01: -5 bp2007-07-01: -5 bp2007-08-01: 8 bp2007-09-01: 15 bp2007-10-01: 16 bp2007-11-01: 32 bp2007-12-01: 27 bp2008-01-01: 54 bp2008-02-01: 77 bp2008-03-01: 93 bp2008-04-01: 84 bp2008-05-01: 75 bp2008-06-01: 58 bp2008-07-01: 62 bp2008-08-01: 80 bp2008-09-01: 82 bp2008-10-01: 145 bp2008-11-01: 171 bp2008-12-01: 157 bp2009-01-01: 167 bp2009-02-01: 175 bp2009-03-01: 186 bp2009-04-01: 189 bp2009-05-01: 208 bp2009-06-01: 217 bp2009-07-01: 218 bp2009-08-01: 213 bp2009-09-01: 211 bp2009-10-01: 196 bp2009-11-01: 209 bp2009-12-01: 213 bp2010-01-01: 221 bp2010-02-01: 210 bp2010-03-01: 192 bp2010-04-01: 177 bp2010-05-01: 165 bp2010-06-01: 163 bp2010-07-01: 162 bp2010-08-01: 160 bp2010-09-01: 147 bp2010-10-01: 139 bp2010-11-01: 145 bp2010-12-01: 154 bp2011-01-01: 153 bp2011-02-01: 158 bp2011-03-01: 152 bp2011-04-01: 152 bp2011-05-01: 151 bp2011-06-01: 152 bp2011-07-01: 145 bp2011-08-01: 146 bp2011-09-01: 128 bp2011-10-01: 129 bp2011-11-01: 119 bp2011-12-01: 110 bp2012-01-01: 100 bp2012-02-01: 96 bp2012-03-01: 91 bp2012-04-01: 77 bp2012-05-01: 71 bp2012-06-01: 74 bp2012-07-01: 66 bp2012-08-01: 67 bp2012-09-01: 69 bp2012-10-01: 71 bp2012-11-01: 65 bp2012-12-01: 67 bp2013-01-01: 76 bp2013-02-01: 86 bp2013-03-01: 88 bp2013-04-01: 79 bp2013-05-01: 89 bp2013-06-01: 110 bp2013-07-01: 129 bp2013-08-01: 143 bp2013-09-01: 145 bp2013-10-01: 136 bp2013-11-01: 145 bp2013-12-01: 157 bp2014-01-01: 150 bp2014-02-01: 142 bp2014-03-01: 141 bp2014-04-01: 138 bp2014-05-01: 126 bp2014-06-01: 121 bp2014-07-01: 110 bp2014-08-01: 98 bp2014-09-01: 104 bp2014-10-01: 99 bp2014-11-01: 100 bp2014-12-01: 84 bp2015-01-01: 77 bp2015-02-01: 94 bp2015-03-01: 89 bp2015-04-01: 83 bp2015-05-01: 107 bp2015-06-01: 117 bp2015-07-01: 113 bp2015-08-01: 99 bp2015-09-01: 100 bp2015-10-01: 93 bp2015-11-01: 101 bp2015-12-01: 92 bp2016-01-01: 87 bp2016-02-01: 70 bp2016-03-01: 72 bp2016-04-01: 73 bp2016-05-01: 75 bp2016-06-01: 64 bp2016-07-01: 51 bp2016-08-01: 49 bp2016-09-01: 53 bp2016-10-01: 60 bp2016-11-01: 81 bp2016-12-01: 96 bp2017-01-01: 95 bp2017-02-01: 94 bp2017-03-01: 93 bp2017-04-01: 79 bp2017-05-01: 82 bp2017-06-01: 64 bp2017-07-01: 69 bp2017-08-01: 64 bp2017-09-01: 52 bp2017-10-01: 55 bp2017-11-01: 48 bp2017-12-01: 34 bp2018-01-01: 42 bp2018-02-01: 51 bp2018-03-01: 39 bp2018-04-01: 39 bp2018-05-01: 41 bp2018-06-01: 34 bp2018-07-01: 22 bp2018-08-01: 19 bp2018-09-01: 21 bp2018-10-01: 20 bp2018-11-01: 14 bp2018-12-01: 8 bp2019-01-01: 8 bp2019-02-01: 12 bp2019-03-01: 10 bp2019-04-01: 14 bp2019-05-01: 8 bp2019-06-01: 5 bp2019-07-01: -0 bp2019-08-01: -16 bp2019-09-01: -18 bp2019-10-01: -13 bp2019-11-01: -7 bp2019-12-01: -6 bp2020-01-01: -9 bp2020-02-01: -12 bp2020-03-01: 20 bp2020-04-01: 29 bp2020-05-01: 26 bp2020-06-01: 26 bp2020-07-01: 24 bp2020-08-01: 28 bp2020-09-01: 30 bp2020-10-01: 36 bp2020-11-01: 42 bp2020-12-01: 49 bp2021-01-01: 64 bp2021-02-01: 89 bp2021-03-01: 124 bp2021-04-01: 124 bp2021-05-01: 121 bp2021-06-01: 105 bp2021-07-01: 79 bp2021-08-01: 74 bp2021-09-01: 82 bp2021-10-01: 82 bp2021-11-01: 69 bp2021-12-01: 47 bp2022-01-01: 58 bp2022-02-01: 44 bp2022-03-01: 29 bp2022-04-01: 24 bp2022-05-01: 27 bp2022-06-01: 16 bp2022-07-01: -13 bp2022-08-01: -50 bp2022-09-01: -58 bp2022-10-01: -63 bp2022-11-01: -79 bp2022-12-01: -87 bp2023-01-01: -80 bp2023-02-01: -88 bp2023-03-01: -83 bp2023-04-01: -81 bp2023-05-01: -88 bp2023-06-01: -119 bp2023-07-01: -123 bp2023-08-01: -105 bp2023-09-01: -97 bp2023-10-01: -72 bp2023-11-01: -71 bp2023-12-01: -78 bp2024-01-01: -66 bp2024-02-01: -68 bp2024-03-01: -69 bp2024-04-01: -56 bp2024-05-01: -58 bp2024-06-01: -55 bp2024-07-01: -37 bp2024-08-01: -21 bp2024-09-01: -4 bp2024-10-01: 11 bp2024-11-01: 10 bp2024-12-01: 17 bp2025-01-01: 36 bp2025-02-01: 37 bp2025-03-01: 47 bp2025-04-01: 58 bp2025-05-01: 63 bp2025-06-01: 66 bp2025-07-01: 72 bp2025-08-01: 73 bp2025-09-01: 72 bp2025-10-01: 71 bp2025-11-01: 73 bp2025-12-01: 81 bp2026-01-01: 84 bp2026-02-01: 80 bp2026-03-01: 68 bp2026-04-01: 65 bp2026-05-01: 61 bp2026-06-01: 63 bp2026-07-01: 71 bp2026-08-01: 72 bp2026-09-01: 62 bp62 bpThe yield curve: 10-year minus 2-year Government of Canada benchmark bond yields, monthly average of daily values, with inverted months shaded-200 bp-100 bp0 bp100 bp200 bp300 bpInverted200520102015202020252001-01-01: 41 bp2001-02-01: 50 bp2001-03-01: 63 bp2001-04-01: 82 bp2001-05-01: 98 bp2001-06-01: 92 bp2001-07-01: 98 bp2001-08-01: 107 bp2001-09-01: 163 bp2001-10-01: 187 bp2001-11-01: 207 bp2001-12-01: 213 bp2002-01-01: 225 bp2002-02-01: 207 bp2002-03-01: 161 bp2002-04-01: 139 bp2002-05-01: 144 bp2002-06-01: 141 bp2002-07-01: 171 bp2002-08-01: 183 bp2002-09-01: 150 bp2002-10-01: 165 bp2002-11-01: 176 bp2002-12-01: 170 bp2003-01-01: 166 bp2003-02-01: 153 bp2003-03-01: 132 bp2003-04-01: 131 bp2003-05-01: 124 bp2003-06-01: 131 bp2003-07-01: 170 bp2003-08-01: 188 bp2003-09-01: 172 bp2003-10-01: 169 bp2003-11-01: 161 bp2003-12-01: 165 bp2004-01-01: 183 bp2004-02-01: 199 bp2004-03-01: 195 bp2004-04-01: 188 bp2004-05-01: 181 bp2004-06-01: 163 bp2004-07-01: 160 bp2004-08-01: 165 bp2004-09-01: 147 bp2004-10-01: 133 bp2004-11-01: 126 bp2004-12-01: 138 bp2005-01-01: 131 bp2005-02-01: 129 bp2005-03-01: 122 bp2005-04-01: 109 bp2005-05-01: 103 bp2005-06-01: 98 bp2005-07-01: 88 bp2005-08-01: 81 bp2005-09-01: 74 bp2005-10-01: 59 bp2005-11-01: 40 bp2005-12-01: 23 bp2006-01-01: 24 bp2006-02-01: 22 bp2006-03-01: 26 bp2006-04-01: 32 bp2006-05-01: 29 bp2006-06-01: 17 bp2006-07-01: 21 bp2006-08-01: 16 bp2006-09-01: 12 bp2006-10-01: 9 bp2006-11-01: 6 bp2006-12-01: 6 bp2007-01-01: 7 bp2007-02-01: 4 bp2007-03-01: 10 bp2007-04-01: 7 bp2007-05-01: -3 bp2007-06-01: -5 bp2007-07-01: -5 bp2007-08-01: 8 bp2007-09-01: 15 bp2007-10-01: 16 bp2007-11-01: 32 bp2007-12-01: 27 bp2008-01-01: 54 bp2008-02-01: 77 bp2008-03-01: 93 bp2008-04-01: 84 bp2008-05-01: 75 bp2008-06-01: 58 bp2008-07-01: 62 bp2008-08-01: 80 bp2008-09-01: 82 bp2008-10-01: 145 bp2008-11-01: 171 bp2008-12-01: 157 bp2009-01-01: 167 bp2009-02-01: 175 bp2009-03-01: 186 bp2009-04-01: 189 bp2009-05-01: 208 bp2009-06-01: 217 bp2009-07-01: 218 bp2009-08-01: 213 bp2009-09-01: 211 bp2009-10-01: 196 bp2009-11-01: 209 bp2009-12-01: 213 bp2010-01-01: 221 bp2010-02-01: 210 bp2010-03-01: 192 bp2010-04-01: 177 bp2010-05-01: 165 bp2010-06-01: 163 bp2010-07-01: 162 bp2010-08-01: 160 bp2010-09-01: 147 bp2010-10-01: 139 bp2010-11-01: 145 bp2010-12-01: 154 bp2011-01-01: 153 bp2011-02-01: 158 bp2011-03-01: 152 bp2011-04-01: 152 bp2011-05-01: 151 bp2011-06-01: 152 bp2011-07-01: 145 bp2011-08-01: 146 bp2011-09-01: 128 bp2011-10-01: 129 bp2011-11-01: 119 bp2011-12-01: 110 bp2012-01-01: 100 bp2012-02-01: 96 bp2012-03-01: 91 bp2012-04-01: 77 bp2012-05-01: 71 bp2012-06-01: 74 bp2012-07-01: 66 bp2012-08-01: 67 bp2012-09-01: 69 bp2012-10-01: 71 bp2012-11-01: 65 bp2012-12-01: 67 bp2013-01-01: 76 bp2013-02-01: 86 bp2013-03-01: 88 bp2013-04-01: 79 bp2013-05-01: 89 bp2013-06-01: 110 bp2013-07-01: 129 bp2013-08-01: 143 bp2013-09-01: 145 bp2013-10-01: 136 bp2013-11-01: 145 bp2013-12-01: 157 bp2014-01-01: 150 bp2014-02-01: 142 bp2014-03-01: 141 bp2014-04-01: 138 bp2014-05-01: 126 bp2014-06-01: 121 bp2014-07-01: 110 bp2014-08-01: 98 bp2014-09-01: 104 bp2014-10-01: 99 bp2014-11-01: 100 bp2014-12-01: 84 bp2015-01-01: 77 bp2015-02-01: 94 bp2015-03-01: 89 bp2015-04-01: 83 bp2015-05-01: 107 bp2015-06-01: 117 bp2015-07-01: 113 bp2015-08-01: 99 bp2015-09-01: 100 bp2015-10-01: 93 bp2015-11-01: 101 bp2015-12-01: 92 bp2016-01-01: 87 bp2016-02-01: 70 bp2016-03-01: 72 bp2016-04-01: 73 bp2016-05-01: 75 bp2016-06-01: 64 bp2016-07-01: 51 bp2016-08-01: 49 bp2016-09-01: 53 bp2016-10-01: 60 bp2016-11-01: 81 bp2016-12-01: 96 bp2017-01-01: 95 bp2017-02-01: 94 bp2017-03-01: 93 bp2017-04-01: 79 bp2017-05-01: 82 bp2017-06-01: 64 bp2017-07-01: 69 bp2017-08-01: 64 bp2017-09-01: 52 bp2017-10-01: 55 bp2017-11-01: 48 bp2017-12-01: 34 bp2018-01-01: 42 bp2018-02-01: 51 bp2018-03-01: 39 bp2018-04-01: 39 bp2018-05-01: 41 bp2018-06-01: 34 bp2018-07-01: 22 bp2018-08-01: 19 bp2018-09-01: 21 bp2018-10-01: 20 bp2018-11-01: 14 bp2018-12-01: 8 bp2019-01-01: 8 bp2019-02-01: 12 bp2019-03-01: 10 bp2019-04-01: 14 bp2019-05-01: 8 bp2019-06-01: 5 bp2019-07-01: -0 bp2019-08-01: -16 bp2019-09-01: -18 bp2019-10-01: -13 bp2019-11-01: -7 bp2019-12-01: -6 bp2020-01-01: -9 bp2020-02-01: -12 bp2020-03-01: 20 bp2020-04-01: 29 bp2020-05-01: 26 bp2020-06-01: 26 bp2020-07-01: 24 bp2020-08-01: 28 bp2020-09-01: 30 bp2020-10-01: 36 bp2020-11-01: 42 bp2020-12-01: 49 bp2021-01-01: 64 bp2021-02-01: 89 bp2021-03-01: 124 bp2021-04-01: 124 bp2021-05-01: 121 bp2021-06-01: 105 bp2021-07-01: 79 bp2021-08-01: 74 bp2021-09-01: 82 bp2021-10-01: 82 bp2021-11-01: 69 bp2021-12-01: 47 bp2022-01-01: 58 bp2022-02-01: 44 bp2022-03-01: 29 bp2022-04-01: 24 bp2022-05-01: 27 bp2022-06-01: 16 bp2022-07-01: -13 bp2022-08-01: -50 bp2022-09-01: -58 bp2022-10-01: -63 bp2022-11-01: -79 bp2022-12-01: -87 bp2023-01-01: -80 bp2023-02-01: -88 bp2023-03-01: -83 bp2023-04-01: -81 bp2023-05-01: -88 bp2023-06-01: -119 bp2023-07-01: -123 bp2023-08-01: -105 bp2023-09-01: -97 bp2023-10-01: -72 bp2023-11-01: -71 bp2023-12-01: -78 bp2024-01-01: -66 bp2024-02-01: -68 bp2024-03-01: -69 bp2024-04-01: -56 bp2024-05-01: -58 bp2024-06-01: -55 bp2024-07-01: -37 bp2024-08-01: -21 bp2024-09-01: -4 bp2024-10-01: 11 bp2024-11-01: 10 bp2024-12-01: 17 bp2025-01-01: 36 bp2025-02-01: 37 bp2025-03-01: 47 bp2025-04-01: 58 bp2025-05-01: 63 bp2025-06-01: 66 bp2025-07-01: 72 bp2025-08-01: 73 bp2025-09-01: 72 bp2025-10-01: 71 bp2025-11-01: 73 bp2025-12-01: 81 bp2026-01-01: 84 bp2026-02-01: 80 bp2026-03-01: 68 bp2026-04-01: 65 bp2026-05-01: 61 bp2026-06-01: 63 bp2026-07-01: 71 bp2026-08-01: 72 bp2026-09-01: 62 bp62 bp
Show the data
The 10-year minus 2-year yield gap by year: the average, lowest and highest of that year's monthly averages
YearAverageLowest monthHighest month
2001117 bp41 bp213 bp
2002169 bp139 bp225 bp
2003155 bp124 bp188 bp
2004165 bp126 bp199 bp
200588 bp23 bp131 bp
200618 bp6 bp32 bp
20079 bp-5 bp32 bp
200895 bp54 bp171 bp
2009200 bp167 bp218 bp
2010169 bp139 bp221 bp
2011141 bp110 bp158 bp
201276 bp65 bp100 bp
2013115 bp76 bp157 bp
2014118 bp84 bp150 bp
201597 bp77 bp117 bp
201669 bp49 bp96 bp
201769 bp34 bp95 bp
201829 bp8 bp51 bp
2019-0 bp-18 bp14 bp
202024 bp-12 bp49 bp
202188 bp47 bp124 bp
2022-13 bp-87 bp58 bp
2023-91 bp-123 bp-71 bp
2024-33 bp-69 bp17 bp
202563 bp36 bp81 bp
202670 bp61 bp84 bp
Source: https://www.bankofcanada.ca/valet/observations/BD.CDN.2YR.DQ.YLD,BD.CDN.10YR.DQ.YLD/json, queried 27 September 2026

The other seven

Sources

Where the data comes from

Federal agencies answer most national questions; provincial agencies and open-data catalogues add the local detail. Every result comes straight from its publisher.

30
federal and national sources
56
provincial, territorial and city sources
12/13
provinces and territories with local data
215
tools behind one connection

Statistics Canada, in depth Every table, series and microdata file an agent can reach, and the hard parts MapleStats handles for you.

How it works

From a question to a cited number

A connected agent sees three tools: plan_query, search_tools and call_tool. The other 214 stay out of its context until a search finds them. Every output below is real. The server produced the plan, the search and the scripts when this page was built; the data call was recorded on 27 September 2026.

  1. 01

    Planplan_query

    The planner reads the question and lists the agencies to ask, in order, with the caveats on combining them. Places it recognizes add their local portals.

    plan_queryRun when this page was built

    Question

    How have rents and interest rates moved in Calgary since 2020?

    Plan

    Housing: starts, rents, prices, mortgages

    1. cmhc_list_categoriesCMHC starts, completions, rents and vacancy by centre
    2. cmhc_get_table_datapull the CMHC table for the place and period
    3. wds_search_cubesStatCan New Housing Price Index, building permits
    4. boc_search_seriesBank of Canada mortgage and policy rates
    5. statcan_census_profile_get_datacensus shelter cost and tenure by area

    Caveat 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

    1. boc_search_seriesfind the Valet series (e.g. FXUSDCAD, V39079)
    2. boc_get_observationspull the series for the period

    Calgary (city)

    1. socrata_search_datasetssearch with portal='calgary'
  2. 02

    Findsearch_tools

    Search ranks the catalogue against a plain-language query and returns the top 5. French works because every tool carries French keywords. The panel runs the server's own index in your browser, so type your own query.

  3. 03

    Fetchcall_tool

    The agent calls the tool by name. The result is a typed object, and its provenance block, marked here, gives the exact upstream URL and the time of the query.

    call_toolRecorded 27 September 2026

    Request

    {"name": "boc_get_observations", "arguments": {"series_names": ["V39079"], "recent": 3}}

    Result

    Target for the overnight rate (business daily) V39079
    DateValue
    24 September 20262.25
    23 September 20262.25
    22 September 20262.25

    Provenance

    Queried
    27 September 2026, 17:00 UTC
    Result type
    boc.ObservationsResult
    Show the full response as 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."
      }}
  4. 04

    Reproducereproduce_code

    The same call becomes an R, Python, Stata or Julia script that downloads, checks and cleans the data, so the analysis does not depend on the agent.

    reproduce_codeRun when this page was built

    Request

    {"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 close

    Julia

    # ============================================================
    # 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,
    )

Connect

Add it to your client

Your client starts the server with uv when it needs it. There is nothing to host and no port to open.

Full setup, hosting and troubleshooting

Claude Code

Run this once in a terminal. --scope user makes the server available in every project; leave it out to add it to the current project only.

Terminal
claude mcp add --scope user maplestats -- uvx maplestats-mcp

Claude Desktop

In Claude Desktop, open Settings, then Developer, then Edit Config. Add the server to the file that opens, save it and restart Claude.

claude_desktop_config.json
{
  "mcpServers": {
    "maplestats": {
      "command": "uvx",
      "args": ["maplestats-mcp"]
    }
  }
}

The file lives at ~/Library/Application Support/Claude/claude_desktop_config.json on macOS and %APPDATA%\Claude\claude_desktop_config.json on Windows.

Cursor

The link opens Cursor with the entry filled in. To add it by hand, put it in ~/.cursor/mcp.json for every project, or in .cursor/mcp.json for one project.

~/.cursor/mcp.json
{
  "mcpServers": {
    "maplestats": {
      "command": "uvx",
      "args": ["maplestats-mcp"]
    }
  }
}

VS Code

To add it by hand, note that VS Code uses a servers key, not mcpServers. Put this in .vscode/mcp.json in a workspace, or run MCP: Open User Configuration from the Command Palette to add it for every workspace.

.vscode/mcp.json
{
  "servers": {
    "maplestats": {
      "type": "stdio",
      "command": "uvx",
      "args": ["maplestats-mcp"]
    }
  }
}

Codex CLI

Add it from the terminal, or write the same entry into ~/.codex/config.toml yourself.

Terminal
codex mcp add maplestats -- uvx maplestats-mcp
~/.codex/config.toml
[mcp_servers.maplestats]
command = "uvx"
args = ["maplestats-mcp"]

Gemini CLI

Add it from the terminal (-s user for every project), or write the entry into ~/.gemini/settings.json next to any settings already there.

Terminal
gemini mcp add -s user maplestats uvx maplestats-mcp
~/.gemini/settings.json
{
  "mcpServers": {
    "maplestats": {
      "command": "uvx",
      "args": ["maplestats-mcp"]
    }
  }
}

Other clients

Most MCP clients accept this mcpServers entry. If you installed the command with uv tool install or pip, use "command": "maplestats-mcp" and drop args.

JSON
{
  "mcpServers": {
    "maplestats": {
      "command": "uvx",
      "args": ["maplestats-mcp"]
    }
  }
}

Acknowledgements

MapleStats builds on the open work of many others. It learned from the many MCP servers for Canadian and government data that came before it, and much of its architecture and inspiration comes from EcuDataMCP, my other project, an MCP server for Ecuador's open government data.

Existing open-source packages helped too, above all the R packages of Jens von Bergmann (mountainMath) and their co-authors:

  • cmhc The CMHC module was checked against its reverse-engineering of CMHC's Housing Market Information Portal.
  • cansim The R code that reproduce_code writes for Statistics Canada tables and vectors uses it.
  • cancensus A model for census data access in R.
  • canivt Reads Beyond 20/20 files. Census tables and Borealis deposits that exist only in that format are routed to it.

Thank you to the developers who built and shared these tools, and to everyone who publishes Canadian data in the open.

For other ways to get Canadian data, some of these packages among them, see the alternatives on the About page.

One MCP for the Agent to find them, one MCP to bring them all together.

The ring: 30 federal publishers around the outside, the provinces and cities with local sources inside, and 215 tools as arcs by subjectBank of Canada · Borealis · Canada Gazette · CanadaBuys · CDC · CER · CFIA · CGC · CIHI · CMHC · Competition Bureau · CRA · DFO tides · Earthquakes Canada · ECCC · Elections Canada · FCAC · GC InfoBase · IRCC · ISED · NRCan burned areas · NRCan energy use · NRCan places · OpenParliament · PBO · PHAC Health Infobase · Recalls · Senate · StatCan · Transport Canada · British Columbia · Surrey · Vancouver · Victoria · Alberta · Airdrie · Calgary · Edmonton · Grande Prairie · Lethbridge · Medicine Hat · Parkland · Red Deer · St. Albert · Strathcona · Sturgeon · Saskatchewan · Regina · Saskatoon · Manitoba · Winnipeg · Ontario · Aurora · Durham · Hamilton · Kitchener · London · Markham · Mississauga · Newmarket · Ottawa · Peel · Toronto · Waterloo · Windsor · York · Quebec · Montreal · New Brunswick · Nova Scotia · Halifax · Prince Edward Island · Newfoundland and Labrador · Yukon · Northwest Territories · Statistics: statistics and census (62)Provinces and cities: open-data catalogues, provincial, municipal (47)Money and business: money, prices and public finance, business, IP and competition (31)Land and energy: agriculture and food, environment and hazards, energy, geography, transport and safety (31)People: health, housing, immigration (22)Parliament: parliament, law and elections (20)Statistics 62Provinces and cities 47Money and business 31Land and energy 31People 22Parliament 20215tools,one connection
Show the data
The 215 tools by subject (the arcs); MapleStats' own tools are counted only in the total
SubjectTools
Statistics: statistics and census62
Provinces and cities: open-data catalogues, provincial, municipal47
Money and business: money, prices and public finance, business, IP and competition31
Land and energy: agriculture and food, environment and hazards, energy, geography, transport and safety31
People: health, housing, immigration22
Parliament: parliament, law and elections20