claude mcp add --transport http censusmapper https://censusmapper.ca/mcpCensusMapper is a web-based Geographic Information System that allows for dynamic mapping of Canadian Census data. CensusMapper is eleven years old now, and it was showing it’s age. Today CensusMapper go a major update.
TL;DR
History
Here we focus on what’s new in this CensusMapper update, we refer those interested in how CensusMapper started and how we got here to posts chronicling the motivation and history of CensusMapper. (von Bergmann 2015b, 2015a, 2016b, 2016a, 2017a, 2019, 2020b, 2020a, 2021; von Bergmann and Cervantes 2015b, 2015a)
The new CensusMapper
After putting off revamping CensusMapper for many years in early 2025 the stars aligned. Just as Jens was mapping out what it would take to modernize CensusMapper Leia, a UBC Computer Science student, approached Jens for an internship opportunity. After some discussions to make sure skills and interested aligned we decided to take the plunge. With both of us having other main responsibilities it took some time, but we have been slowly hacking away at it and are finally ready to roll it out.
The technology stack
The old CensusMapper used to be one giant Ruby on Rails app is now a collection of services. We only kept the Postgres/PostGIS database structure and bits and pieces of custom front-end javascript logic and rebuilt everything else from scratch. Now we have
- An API backend that serves data and map tiles, and does some minimal user management using FastApi backed by our existing PostgreSQL/PostGIS database.
- A React frontend that is responsible for the user interface.
- A mapping stack built on Maplibre and custom Deck.gl extensions for the GPU-powered data layer rendering.
- A background tile server that’s responsible for creating and serving contextual map tiles designed to work with the census data powered by Martin.
- An IP geolocation microservice so maps can scroll by default to regions relevant to each user using duckdb and a lightweight approximate IP geolocation lookup table from IP2Location.
- A microservice to create social preview images for sharing on social media, built on Puppeteer.
- A Model Context Protocol (MCP) microservice that breaks down the data API into useful components for LLMs to interact with census data using natural language queries, built on the MCP Python SDK.
Changed user experience
How do these technology choices translate to the user experience? We want to highlight a couple of the impacts of these changes.
New mapping stack
Switching to full vector maps gives much more control over how the data is shown, and tying into WebGL makes this very performant and fast. In In particular, we can be much more precise on how we want to render contextual information. In the original CensusMapper we used Stamen Toner themed overlays that just show the roads and labels and are drawn on top of the census data. That allows for rich colours for the census data with simple and informative contextual data as overlays. Unfortunately we lost this layer when these base maps moved to Stadia and we regressed to drawing census data on top of base maps, requiring opacity for the census data and losing a lot colour vibrancy and contrast in the process.
With the new CensusMapper we decided to build our own Canada-wide contextual vector tiles that are specifically designed to work well with census data. For this we took the same design as the original Stamen Toner base maps and are drawing roads and place names on top, with some adjustments on what to show and what not to show, especially at high zoom levels. And for added context we decided to fade in building footprints at very high zoom levels, making it easier to orient oneself when zoomed in far.
An important addition in the new CensusMapper is that we also built a custom layer meant to filter out land uses where nobody lives. That is forests, parks, gold courses, and similar natural areas, as well as industrial areas. This layer is now by default also overplayed with a slight transparency to hint at which census regions extend below them. The idea is that it helps users to focus on areas where people are likely to live, and so that areas that contain large parks don’t dominate the map. This is only available at medium and high zoom levels, at low zoom levels much of the country gets blocked out that way and it distracts more than it helps. When people view Canadian data at the provincial or census district level the areas not covered by forests can we really small and hard to discern at that zoom level, and places with inconsistent or missing land use data in Open Street Map, which can become a concern at medium zoom levels too, become very visible.
The last change is the water layer. We now overlay water with full opacity, blocking out water helps provides important context. Census regions generally don’t block out internal bodies of water and even rivers, which can make it hard to orient oneself on a map. For the 2021 census data release we spent a lot of work on cropping out internal waterways to alleviate this, but this does not come without tradeoffs. Simply overlaying water on top of maps is the simpler and much better solution to this problem.
The dynamic nature of vector base maps also means that we can dynamically turn some of these layers on or off, and there is a new layer menu in the map controls that facilitate this.
In summary, this results in a much better user experience when viewing maps on CensusMapper.
New map making
The public version of the old CensusMapper only allowed for making simple maps based on single census variables or percentages that involved two variables. The internal version allowed for much more flexible and interesting map making, but it remained locked off for general users because it required extensive knowledge how it worked and was not user friendly. A big part of the revamp of CensusMapper was to make the more flexible map making functionality available to everyone in a user friendly way. Users can now write arbitrary functions based on census variables, and create custom intermediate variables to help organize things. And we have four basic types of maps available, with plans to add more. The currently available map types are:
- Choropleth maps, which are the classic type of map where each geographic unit is coloured based on the value of a variable.
- Multi-choropleth maps, where users can create a selection of related choropleth maps with buttons that allow users to switch between them. This is useful for comparing different variables, or the same variable across different census years.
- Category maps, a simple map that ranks (custom) variables and colours each region based on the dominant one.
- Tricolour maps, which show proportions between three different variables. We have had these from the beginning of CensusMapper, but our original implementation was simply mixing RGB channels, for the new CensusMapper we looked toward the {tricolore} R package (Schöley and Kashnitsky 2018) that we quite like for a better way to implement this in HCL colour space and we added discrete colour brackets, which makes the maps more legible.
- Bivariate maps, these were an obvious blind spot in the original CensusMapper that we filled in this release. Bivariate maps can be a useful way to show data along two dimensions, but similar to tricolour maps the resolution along each dimension is somewhat limited so that the map remains legible.
A big part of CensusMapper has been the map stories in the side panel. Stories allow map makers to frame the data and lead the reader. Map makers have likely spent a lot of time thinking about the data they are mapping and can share that knowledge. It’s perfectly possible, and even desirable, that two different map makes tell quite different stories using the same underlying map.
To enrich the storytelling the map stories accept markdown content, and we have made it a bit easier for newcomers to write richer stories. And we made it much easier for users to add custom widgets, for example scatterplots, based on the data in the map view. This has been available in CensusMapper for a long time but was not exposed to the general public because it wasn’t user friendly. Now it is, so anyone can make widgets based on custom variables they create.
Visual impact
The changes in mapping stack and refinements in map representation discussed above change the visual impact of the maps as illustrated in the comparisons below. We believe this significantly improves the user experience and the readability of the maps.
Geolocation services
CensusMapper always had the problem that some users would not fully grasp the interactive Canada-wide nature of the maps and would not pan over to regions they are interested in but just stay at the map view the map originally loads in.
CensusMapper always had the ability for users to geo-locate them and automatically zoom and pan to where they are. But that requires users selecting that button and allowing access to their location, which some users are understandably reluctant to do. In principle this fairly precise geolocation information can then get harvested by web services, and while on CensusMapper this information remains in your web browser and never gets uploaded to the server, many web services do just that and it’s impossible for casual users to distinguish these cases.
But census data is inherently local and users tend to be interested in data around where they are. As a compromise CensusMapper now has an IP geolocation service. This is simply a lookup table that identifies the user IP address, something that is always shared with a web service, to an approximate geographic location. We are using a free database, so accuracy is somewhat limited and generally off by around 5 kilometres, frequently worse. This does not work when using VPN, or a corporate network connection, and sometimes it’s very far off for other reasons. But it most cases it works reasonably well to give a rough idea where a user is located, and this mechanism is now used at CensusMapper to pan and zoom maps to the rough (presumed) user location when navigating to the site without indicating a target location. If the user is located to be in Canada.
This makes the content when landing on the page more relevant to users without requiring additional information from users.
The MCP server
No matter your opinion on the advent of generative AI and LLMs, they are here and will stick around in one form or another. CensusMapper is based on users making, narrating, and sharing maps. And it is also a general API server that provinces pinpointed access to census data, mostly for analysis purposes. The underlying motivation has always been to make census data more accessible for the general population, and to foster the (responsible) use of data throughout Canada and build up a community of data users.
CensusMapper, and the data APIs, significantly reduce the barriers to working with Canadian census data. But significant barriers remain. An important part of the back story to CensusMapper is that it started after an initial furor into census data triggered a lot of interest from journalists, and I started to get questions about census data, questions like:
- What is the average income in Vancouver?
- What’s the share of the Filipino population in Burnaby?
- How many German-born people live in Toronto?
These are fairly simple questions that require the lookup of one or two census variables. At the time, just a couple of years after StatCan first opened up their data in February 2012, these were hard questions. CensusMapper made it easier to get these answers, but they required some clicking through maps and maybe using the “Census Wheel”, and some familiarity with CensusMapper to understand how to do this. Or using the API, which in many ways is simpler for this purpose but comes with it’s own set of barriers.
Nowadays these questions have become easier, not just because of CensusMapper but also because StatCan has gotten better at making their data more accessible. But chances are high that nowadays people who have questions like this will use a search engine, and just read the AI Overview. Or go straight to their preferred LLM, be it ChatGPT, Claude, Gemini or something else. And this creates a problem. While the barrier to ask – and get answers to – these kind of questions is virtually non-existent at this point, it has gotten harder to figure out how reliable the answer is. One way to improve this is a dedicated MCP server.
What’s an MCP server?
A Model Context Protocol (MCP) server is, roughly speaking, and API for an LLM. It’s provides a principled way for an LLM to access data. The MCP includes an overview on what kind of data access is available together with some brief instructions on how to use this service. To be useful it should be tailored specifically to LLMs.
We already have a data API that is great for querying and working with census data, but it’s not that useful for LLMs. The main reason is that the typical amount of data provided is too large. For example to discover census variables and decide which might be useful for a specific question, the data API workflow would download all census variable descriptions for a given census year and then filter and search through those variables. That’s not something an LLM can do effectively. The MCP implements a similar functionality by filtering on the server and only sending a filtered set of census variables down to the LLM. Small chunks that the LLM can handle, and potentially iterate on to narrow things down further or iterate on.
Basically, the data API is designed to allow for larger data download for desired regions, variables and aggregation level, the MCP is design to facilitate discovery of a very small number of relevant regions and variables and pinpointed data download. Because each query is small and pinpointed we have removed the quota we have on regular API calls, but we rate-limit the service so it won’t get overwhelmed.
How to use the MCP server?
You will have to point your LLM of choice to the CensusMapper server. For example for Claude you would either edit the config file or run
For almost all queries you won’t need an API key, but if you are planning of using this for more complex queries it’s recommended to install your CensusMapper API key when registering the MCP, substituting in your API key for “YOUR_API_KEY”:
claude mcp add --transport http --scope user censusmapper \
https://censusmapper.ca/mcp \
--header "X-Api-Key: YOUR_API_KEY"Then Claude will use the CensusMapper MCP when asking census related questions for Canada. Here is the transcript when asking a somewhat vague but fairly common question:
❯ what is the average income in vancouver?
⏺ Let me search for income variables and the Vancouver region in parallel.
Called censusmapper 2 times (ctrl+o to expand)
⏺ Now let me fetch the income data for both the Vancouver CMA and City of Vancouver.
Called censusmapper 3 times (ctrl+o to expand)
⏺ Here's what the 2021 Census (2020 income data) shows for Vancouver:
City of Vancouver
┌───────────────────────────────────────────────────────────────────┬──────────┐
│ Measure │ Amount │
├───────────────────────────────────────────────────────────────────┼──────────┤
│ Average household income │ $114,900 │
├───────────────────────────────────────────────────────────────────┼──────────┤
│ Average economic family income │ $151,000 │
├───────────────────────────────────────────────────────────────────┼──────────┤
│ Average individual income (persons 15+, not in economic families) │ $55,850 │
├───────────────────────────────────────────────────────────────────┼──────────┤
│ — Male │ $58,500 │
├───────────────────────────────────────────────────────────────────┼──────────┤
│ — Female │ $53,350 │
└───────────────────────────────────────────────────────────────────┴──────────┘
Vancouver Metro Area (CMA)
┌───────────────────────────────────────────────────────────────────┬──────────┐
│ Measure │ Amount │
├───────────────────────────────────────────────────────────────────┼──────────┤
│ Average household income │ $117,300 │
├───────────────────────────────────────────────────────────────────┼──────────┤
│ Average economic family income │ $141,000 │
├───────────────────────────────────────────────────────────────────┼──────────┤
│ Average individual income (persons 15+, not in economic families) │ $52,650 │
├───────────────────────────────────────────────────────────────────┼──────────┤
│ — Male │ $55,400 │
├───────────────────────────────────────────────────────────────────┼──────────┤
│ — Female │ $50,040 │
└───────────────────────────────────────────────────────────────────┴──────────┘
Source: Statistics Canada, 2021 Census of Population (25% sample data, income year 2020).
A few notes on interpreting these:
- Household income is the most commonly cited figure — $114,900–$117,300 across city/metro.
- Economic family income is higher because it pools multiple earners in the same family unit.
- Individual income (~$53–56K) covers people 15+ who are not part of an economic family (e.g., single adults living alone).
- The gender gap is ~$5,000–$5,400 (male > female) across both geographies.This gives some indication of how this works, and how the MCP helps Claude understand the question and find the relevant data, in this case opting to give a range of answers to cover the somewhat vague question. In other cases Claude may ask clarifying questions if the region or variable query yield too many results. Or no results at all and Claude widens the search categories.
There are several problems that, while obvious, should get stated explicitly:
- An MCP is just mitigating the problem that LLMs are generally bad at surfacing facts like this, and sometimes will make things up. One should view the CensusMapper MCP has a light harness that tries to steer the LLM toward more reliable ways of obtaining the data and answer (often vague) user questions, but it can’t eliminate the LLM problem of sometimes giving unreliable results.
- Quoting numbers right from the MCP works reasonably well, but things get wonky fast when the LLM has to do calculations. Simple ratios, say share of population of a given visible minority, kind of works. The LLM will likely make the fairly common mistake of dividing by the total population when visible minority status is only available for the population in private households. But more importantly, LLMs are really bad at math. It may get even simple things like ratios wrong, but once things get more complicated mistakes can happen. And changing the question slightly by asking something like “Give me the top 10 cities by share of population with Turkish origin.” it completely collapses. It would have to pull the data for all cities and then rank them, that’s something an LLM will have a really hard time with.
- At this point the CensusMapper MCP is not registered in any of the MCP server directories, it will have to be installed manually. People who know how to do that aren’t that different from the people who could call the API directly, so the benefit is very limited. (At some point we might advertise the MCP server more broadly which might spread it’s use more broadly.)
- Census data is not the only source of socio-economic data in Canada. And it generally lags quite a bit. Right now the newest census data we have is from 2021, with income data from 2020. There is newer income data available, from the T1FF taxfiler data as well as from the Canadian Income Survey, which might be more appropriate for answering the question. (Yes, we have been contemplating of setting up an MCP server for this too.)
- The CensusMapper MCP server is only a small step toward making it easier to interact with Canadian data via natural language queries. And it may well not be part of a more comprehensive attempt at this that implements a solution that has clear easily verifiable answers to natural language queries.
To unregister the CensusMapper MCP run
claude mcp remove censusmapperIn summary, does the MCP server solve the problem of LLMs giving unreliable answers about (census) data? No. Does it significantly improve the accuracy and reliability of LLMs? Yes. Is it “good enough”? No, but better than not having it.
The future
There are lots of things that did not make it into this release of CensusMapper, but are in some form or another in the planning pipeline. In the near term we will be focused on ironing out quirks and little issue as we collect user feedback. But medium to longer term we got more exciting things in the pipeline.
More data
Right now CensusMapper has census data back to 1996, and StatCan has been making progress to make data from earlier censuses available in electronic form. We are planning to import data from earlier years. At the same time, there are a lot of interesting custom tabulations available that aren’t part of the regular census profiles that we import by default. For a mapping-focused system like CensusMapper we generally only consider datasets that at least go down to census tracts, and will start to set up processes that import some of these for at least the most recent census.
More TongFen
TongFen is the process of making census (and other) data comparable through time, where geographies might change but do so in a way that one can usefully build a “least common denominator” geography and re-aggregate the data, resulting in a semi-custom tabulation on the fly. We have automated this process in the TongFen R package. (von Bergmann 2020c) For CensusMapper we have done this for the 2011 and 2016 census geographies, which enables interesting cross-census maps. It would be nice to have the ability to do this for more census years. There is a tradeoff between granularity and length of timelines, but there is value in creating these multi-census maps right in CensusMapper, and we plan on enabling more of this.
Dot-density maps
One notable omission from this list is dot-density maps. We had an implementation of these in the original CensusMapper (von Bergmann 2017b), but there are a number of tradeoffs with these, especially on interactive maps, and we were never quite happy with the old implementation. We have plans for an improved version of this to be added in the medium term.
CensusMapper Chat
This is the logical extension of the MCP server, but it requires more work to implement a safe and verifiable way to to translate natural language queries into answers based in data. It will require some experimentation to train a foundation model to understand the language and delicacies of census data, and query and report on the data in a verifiable way. We have some ideas, but there are no guarantees we can implement a useful solution that people who can’t code can verify.
Upshot
We got a new and shiny CensusMapper, try it out, browse maps others have made, go back to maps you made previously and spiff them up, and make new maps!
There likely are still a number of quirks and issues, if you come across any ping us on any of our contact channels so we can fix it.
References
Reuse
Citation
@misc{censusmapper-20.2026,
author = {{von Bergmann}, Jens and Chen, Leia},
title = {CensusMapper 2.0},
date = {2026-05-04},
url = {https://doodles.mountainmath.ca/posts/2026-05-04-censusmapper-20/},
langid = {en}
}