Building a TigerWeb + Census Data 3D MCP Pipeline

Turning Census Boundaries into a 3D Interactive Map with Zero Manual Tooling
May 2026 · 7 min read


What if you could point an AI agent at a ZIP code and get back a fully interactive 3D map of any Census variable — complete with animated fly-to tours, color-ramped extrude columns, and satellite basemaps — in under two minutes, with no GIS software installed?

That's exactly what we built this week, and it took four natural-language prompts.

▲ Live Demo: Interactive 3D map of ZIP 80921 — drag to rotate, scroll to zoom, click blocks for details.

↗ Open Interactive Map Full Screen

The Stack

Three MCP servers working together through a Hermes agent:

ComponentRoleMCP Server
TigerWebGeography — census block boundaries as GeoJSONCustom FastMCP wrapper around the Census Bureau's GeoServices REST API
Census Data APIDemographics — population counts by age, sex, raceCustom FastMCP wrapper with variable search, group expansion, and query execution
MapLibre GL JSVisualization — 3D extruded choropleth in the browserGenerated inline by the agent (no server — pure client-side)

The agent orchestrates all three: it discovers variables, queries geometry, runs the census query, joins data to shapes, and writes a self-contained HTML file. No middleware. No manual shapefile downloads.

The Prompt

"Census 2020 SF1 children under 16 by census block in ZIP code 80921 displayed map with color range"

That's it. Here's what happened under the hood:

Step 1: Variable Discovery

The agent searched the 2020 Decennial DHC dataset (the 2020 successor to SF1) for age-related variables. It found table P14 — "Sex by Age for the Population Under 20 Years" — containing single-year age bins from 0 to 19. Children under 16? That's P14_001N (total under 20) minus the eight variables for ages 16–19.

No manual lookups. No variable code sheets.

Step 2: ZIP → Blocks

ZIP codes aren't Census geographies. The agent used TigerWeb's PUMA_TAD_TAZ_UGA_ZCTA service to fetch the ZCTA 80921 polygon, then queried all 2020 census blocks in El Paso County, Colorado, and ran a point-in-polygon spatial filter on block centroids to identify exactly which 292 blocks fall inside the ZIP code.

That's the kind of spatial logic you'd normally write in PostGIS or geopandas. The agent did it with Python stdlib in 15 lines.

Step 3: Census Query

With the GEOID list in hand, the agent ran the Census API query, pulling 11,930 rows for El Paso County, filtering to 292, and computing under_16 on the fly.

Result: 4,658 children under 16 across 292 blocks.

Step 4: Join & Visualize

The agent merged census data into the block GeoJSON properties, then wrote a MapLibre GL JS HTML file with:

All self-contained in a single 1.1 MB HTML file — uploadable to any blob storage.


What Makes This Interesting

1. Geography + Demographics, No GIS Stack

Normally this workflow requires downloading TIGER/Line shapefiles from the Census FTP, importing into QGIS or PostGIS, writing SQL joins or geopandas scripts, and running a local web server with GeoJSON endpoints.

Here, TigerWeb serves as a live GeoJSON API for every census geographic layer — states, counties, tracts, block groups, blocks, ZCTAs, PUMA, CBSA, tribal areas, and more. The Census Data API serves the demographic tables. The agent is the glue.

2. Spatial Reasoning Without a Spatial Database

The point-in-polygon filter (centroid lat/lon against ZCTA polygon vertices) ran in pure Python with a classic ray-casting algorithm. No PostGIS, no geopandas, no Docker container. Just math.

This is the kind of thing MCP enables: domain-specific servers that expose exactly the primitives an LLM needs, in the shape it expects.

3. 3D Population as Architecture

Extruding census blocks by population turns an abstract choropleth into something you can read intuitively. The blocks with 447 children rise dramatically above their neighbors. The 78 blocks with zero children are dark, flat specks. You can fly around, tilt, and zoom — it feels like looking at a city skyline where every building is a neighborhood's child population.


The MCP Servers

TigerWeb MCP Server

ToolDescription
list_services36 map services (States, Tracts, ZCTAs, CBSA, etc.)
get_service_infoLayer metadata
query_boundariesFeature query with WHERE clause + geometry
identify_locationPoint-in-layer lookup
find_boundaryText search by name
export_mapRendered PNG export with bbox

Key design choice: TigerWeb's REST API supports WHERE clauses, outFields, resultRecordCount, and returnGeometry=true/false. The MCP server exposes these directly rather than abstracting them — giving the agent full control over query shape and payload size.

Census Data MCP Server

ToolDescription
search_variablesKeyword search across ~30K variables
get_group_variablesExpand a table (e.g., P14) into all columns
get_variable_detailsFull metadata for a single variable
run_queryFull Census API query with geography clauses
list_datasetsDataset discovery (ACS, Decennial, etc.)

The variable search is the killer feature — 9,068 matches for "age under 16" across the DHC dataset, scored by relevance. Without it, you'd be scrolling through PDF data dictionaries.


The Result

Census blocks292
Tracts spanned10
Children under 164,658
Mean per block16.0
Median per block10
90th percentile37
Highest block447
Time to build~2 minutes (4 prompts)

The final product is a single HTML file you can open locally, upload to S3/R2/Azure Blob, drop on GitHub Pages, or share as an attachment. No build step. No API keys in the client. No server dependency.


Why This Matters

The Census Bureau publishes some of the richest demographic data in the world, but accessing it has historically required specialized tools and domain knowledge — understanding table schemas, geographic hierarchies, FIPS codes, and the difference between a ZIP code and a ZCTA.

MCP servers compress that learning curve to zero. You don't need to know that ZIP 80921 is in El Paso County (08041), or that children-under-16 is P14 minus ages 16–19, or that TigerWeb's blocks layer is layer_id=2 in the Tracts_Blocks service. The agent discovers all of that on its own.

What took half a day with QGIS + ACS downloads + Python joins now takes two minutes with natural language.


Try It Yourself

All three MCP servers are available as open-source FastMCP projects. Wire them into your Hermes agent config and you can go from "show me" to "interactive 3D map" with a single prompt.

# ~/.hermes/config.yaml
mcp_servers:
  census_data:
    command: python
    args: ["/path/to/census-mcp/server.py"]
    env:
      CENSUS_API_KEY: ${CENSUS_API_KEY}
  tigerweb:
    command: python
    args: ["/path/to/tigerweb-mcp/server.py"]

Then just ask:

"Show me median household income by census tract in Chicago as a 3D map"

"Map population density by block group in Phoenix with a color ramp"

"Export all block-level housing units in ZIP 90210 as a standalone HTML file"

The agent handles the rest.


Built with Hermes Agent, FastMCP, TigerWeb GeoServices REST API, Census Data API, and MapLibre GL JS.