Query U.S. Census Bureau data, variables, and geography via MCP.
Query U.S. Census Bureau data, variables, and geography via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://census.caseyjhand.com/mcp
U.S. Census Bureau data — datasets, variables, and geography — via the Census Data API, TIGERweb, and the Census Geocoder. Discover datasets and variables, resolve place names or addresses to FIPS codes, and query or rank demographic, economic, and housing estimates across geographies from any MCP client. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
| Tool | Description |
|---|---|
census_list_datasets | Browse available Census Bureau datasets (ACS5, ACS1, Population Estimates, Decennial, County Business Patterns, Economic Census, Nonemployer Statistics) with vintage years and dataset codes. |
census_list_geographies | List the geography levels supported by a dataset and year, with parent requirements and example FIPS values. |
census_search_variables | Keyword search across variable labels and concept groups. On ACS, returns estimate and margin-of-error codes together. |
census_get_variable | Fetch full metadata for one or more variable codes — label, concept, predicate type, universe, MOE sibling. |
census_list_predicate_values | List the codes a filter dimension accepts (EMPSZES, LFO, POPGROUP, NAICS2017…), from the dataset dictionary or a live wildcard enumeration. |
census_resolve_geography | Convert place names (e.g., "King County, WA") or street addresses to Census FIPS identifiers via TIGERweb and Census Geocoder. |
census_query_data | Query a Census dataset for variables at a specific geography. Returns estimates with MOE, suppression codes resolved to readable reasons, and predicate filtering for the business datasets. |
census_compare_geographies | Rank and compare variables across multiple geographies — all counties in a state, all states nationally, or a named set. Sorted table output, with the same predicate filtering. |
census_list_datasets toolcbp), Economic Census (ecnbasic), and Nonemployer Statistics (nonemp)acs/acs5) are the values to pass to other toolsavailable_years is exhaustive, not a sample: any other year fails with year_not_available before a request goes out, naming the years that do work. It is narrower than what the Census API hosts — pep/charv reaches its 2020-2022 estimates through the YEAR filter inside the 2023 vintage, and the cbp/nonemp vintages left out reject the NAME column every query here sendscensus_list_geographies toolgeography_level, whether a parent is required, required_parent_levels, and an example FIPS valuegeography_level values are the exact inputs to geography_level in census_query_data and census_compare_geographiesyear defaults to the dataset's latest available vintagedataset_not_found when the dataset code is unrecognized; year_not_available when the dataset has no geography data for the requested yearcensus_search_variables toolNAICS2017 in cbptotal_matches indicates how many matched before the limitcensus_get_variable toolestimate_code/moe_code sibling references; other families publish no margins of error and carry neither fieldNAICS2017, SEX) to confirm a dimension exists in a dataset — census_list_predicate_values lists the values it acceptsdataset defaults to acs/acs5, year defaults to the dataset's latest available vintagevariable_not_found when a code isn't defined in the dataset and yearcensus_list_predicate_values toolNAICS* and POPGROUP always publish one (thousands of codes — narrow them with query); on the current vintages EMPSZES, LFO, RCPSZES, TAXSTAT, and TYPOP publish none, so the live route is the only place their codes appeardec/ddhca declares 5,543 POPGROUP codes and publishes 2,996, cbp declares 6,694 NAICS2017 codes and publishes 2,003. The declared list is checked against the dataset's own published rows and the dead codes are dropped; source says whether that check ran and the notice says how many were withheldquery matches code and label; results are sorted by code and a truncated list is disclosed rather than passed off as complete (default limit 50, max 500)ecnbasic publishes TAXSTAT and TYPOP per industry, so within_naics scopes the enumeration — and the notice says the result is complete for that industry alonecensus_resolve_geography toolgeography_type for state, county, place, and tract; metropolitan/micropolitan statistical areas, combined statistical areas, and consolidated cities are never auto-detected and need an explicit geography_type, since their names overlap city namescounty_fips scopes resolution to the county and tract levels only — required when a tract name matches more than one county; county_scope_unsupported when paired with any other level or a street addressambiguous_name, with every candidate's FIPS code and the state that separates themstate_fips (→ parent_fips) and fips_summary (→ geography_fips) ready to pass to other tools; a statistical area omits state_fips since it can span several statescensus_query_data toolcensus_resolve_geography for place names) and up to 50 variable codes per call; geography_fips: "*" returns every geography at the level within the parent, and each row carries both geography_fips and the nationally-unique geography_geoidparent_required and parent_not_accepted name what's missing or unaccepted rather than surfacing a raw Census 400predicates map filters the business/pep/dec datasets (e.g., {"NAICS2017": "5112"}); a dimension left unset applies a Census-chosen default — an all-categories total on some datasets, a single category on others — echoed per row in applied_filterspep/charv) returns multiple rows, each carrying a record field; pin one with predicates (e.g., {"MONTH": "7"})estimate means the value is either suppressed, a text cell (returned under value), or genuinely emptyCENSUS_API_KEYcensus_compare_geographies toolgeographies list of GEOIDs/bare level codes, in one call; within/within_county scope to a state/county, omit for a national comparisonsort_by variable, sort_dir (default desc), and limit (default 50, max 500); total_count reports how many geographies matched before the limitpredicates map, geography validation, and applied_filters default-echoing as census_query_data, applied to every geography in the rankingpep/charv) fails with ambiguous_rows unless predicates pins one (e.g., {"MONTH": "7"})CENSUS_API_KEYBuilt on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
Census-specific:
Agent-friendly output:
fips_summary and state_fips return values are ready to pass as geography_fips and parent_fips to the next tool-666666666) surfaced as human-readable reasons instead of raw numbersA public instance is available at https://census.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"census-mcp-server": {
"type": "streamable-http",
"url": "https://census.caseyjhand.com/mcp"
}
}
}
API key: Register a free key at api.census.gov/data/key_signup.html. Variable search and geography resolution work without a key; data queries (
census_query_data,census_compare_geographies) require one.
Add the following to your MCP client configuration file:
{
"mcpServers": {
"census-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/census-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"CENSUS_API_KEY": "your-census-api-key"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"census-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/census-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"CENSUS_API_KEY": "your-census-api-key"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"census-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"-e", "CENSUS_API_KEY=your-census-api-key",
"ghcr.io/cyanheads/census-mcp-server:latest"
]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 CENSUS_API_KEY=... bun run start:http
# Server listens at http://localhost:3010/mcp
census_query_data and census_compare_geographies; other tools work without it.git clone https://github.com/cyanheads/census-mcp-server.git
cd census-mcp-server
bun install
cp .env.example .env
# edit .env and set CENSUS_API_KEY
| Variable | Description | Default |
|---|---|---|
CENSUS_API_KEY | Required for data queries. Register free at api.census.gov/data/key_signup.html. | — |
CENSUS_DEFAULT_YEAR | Default vintage year when no year is specified. | 2024 |
CENSUS_VARIABLE_CACHE_TTL_HOURS | Hours to cache variables.json per dataset+year in memory. | 24 |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_SESSION_MODE | HTTP session mode: stateful, stateless, or auto. The server declares stateless in src/index.ts; set this only to override it. | stateless |
MCP_HTTP_PORT | Port for HTTP server. | 3010 |
MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, notice, warning, error). | info |
OTEL_ENABLED | Enable OpenTelemetry instrumentation. | false |
See .env.example for the full list of optional overrides.
# One-time build
bun run rebuild
# Run the built server
bun run start:stdio
# or
bun run start:http
Run checks and tests:
bun run devcheck # Lint, format, typecheck, security audit
bun run test # Vitest test suite
bun run lint:mcp # Validate MCP definitions against spec
docker build -t census-mcp-server .
docker run --rm -e CENSUS_API_KEY=your-key -p 3010:3010 census-mcp-server
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/census-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
| Path | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools and initializes services. |
src/config/server-config.ts | Census-specific env var parsing and validation with Zod. |
src/mcp-server/tools/definitions/ | Tool definitions (*.tool.ts). |
src/services/census-api/ | Census Data API client — data queries, suppression code mapping, retry logic. |
src/services/geography/ | Geography resolution — TIGERweb named-place lookup and Census Geocoder address-to-tract. |
src/services/variable-cache/ | In-process variables.json cache with TTL and keyword search. |
tests/ | Vitest tests mirroring src/ structure. |
See CLAUDE.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for request-scoped logging, ctx.state for tenant-scoped storagesrc/mcp-server/tools/definitions/index.tsIssues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
Apache-2.0 — see LICENSE for details.
Source-derived launch command. Check the maintainer’s required arguments and credentials before running:
npx -y @cyanheads/census-mcp-serverMerge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.
{
"mcpServers": {
"io-github-cyanheads-census-mcp-server": {
"command": "npx",
"args": [
"-y",
"@cyanheads/census-mcp-server"
]
}
}
}Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.
Claude Desktop setup referenceio.github.cyanheads/census-mcp-server works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.
~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.~/.cursor/mcp.jsonRestart Cursor for changes to take effect..vscode/mcp.jsonReload VS Code window for changes to take effect.~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect..mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.