Query U.S. Census Bureau data, variables, and geography via MCP.
One address for any MCP client. No sign-in needed on this server.
8 read
Grouped by tool name. At most: only reads or looks up information.
Odel charges nothing per call. The publisher may charge for its own service.
Query U.S. Census Bureau data, variables, and geography via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://census.caseyjhand.com/mcp
US Census Bureau demographic, economic, and housing data. Find datasets and variables, resolve place names or addresses to FIPS codes, and query or rank estimates across any set of geographies.
| Tool | Description |
|---|---|
census_list_datasets | Browse available Census Bureau datasets (ACS5 and ACS1 with their profile, subject, and comparison tables, ACS Supplemental Estimates and Selected Population Profiles, 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, or lookup by variable or table code. 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 — including the annotation and flag columns the data tools accept. |
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"), ZIP codes, 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, Census sentinel values and withheld business values resolved to their published meanings, 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 toolacs/acs1/spp), acs/acsse, Population Estimates, the 2020 Decennial files (Redistricting (P.L. 94-171), dec/dhc, dec/dp, dec/sdhc, dec/ddhca, dec/ddhcb), cbp, ecnbasic, and nonempacs/acs5) are what the other tools take. Case is ignored and a two-part code works by its last part alone (acs5, pl); three-part codes such as acs/acs5/profile must be given in fullavailable_years is exhaustive: any other year fails with year_not_available, naming the years that workcensus_list_geographies toolgeography_level, whether a parent is required, required_parent_levels, and an example FIPS value. totalLevels counts each level oncegeography_level values are the exact inputs to census_query_data and census_compare_geographies; year defaults to the latest vintagedataset_not_found or year_not_available (naming the years served)census_search_variables toolB19013 returns B19013_001E then B19013_001M)S1701 in acs/acs5 points to acs/acs5/subject) or census_get_variable for an attribute column such as B19013_001EAnaics) or code (NAICS2017 in cbp)limit is an integer from 1 to 100 (default 20), rejected rather than clamped when out of range; totalMatches counts matches before the limit. variables.json is cached per dataset+year (default TTL 24h)census_get_variable toolgroups.json can't be fetched, variables come back without universe and a notice says to retryB19013_001EA and EMP_F, with attribute_of naming the column each belongs to and attribute_type its kind (ANNOTATION, FLAG, MARGIN_OF_ERROR). On ACS datasets it also returns estimate_code/moe_code sibling referencesNAICS2017, SEX) exist in a dataset; census_list_predicate_values lists their values. dataset defaults to acs/acs5 and year to the latest vintagevariable_not_found when a code isn't defined, naming the sibling dataset that publishes it when one is found (DP03_0062E in acs/acs5 points to acs/acs5/profile)census_list_predicate_values toolNAICS* and POPGROUP have long lists (thousands of codes; narrow with query); EMPSZES, LFO, RCPSZES, TAXSTAT, and TYPOP are live-onlysource says whether the check ran and the notice gives the number withheldquery matches code and label; results are sorted by code, and truncation is disclosed (limit an integer from 1 to 500, default 50; totalCount counts matches). On ecnbasic, within_naics scopes TAXSTAT and TYPOP to one industrycensus_resolve_geography toolblock_group_fips and place_fips. A 5-digit ZIP (or ZIP+4) resolves to its ZIP Code Tabulation Area (zip code tabulation area)geography_type for state, county, place, tract, and ZIP. Metropolitan/micropolitan statistical areas, combined statistical areas, consolidated cities, and economic places need an explicit geography_type; economic place returns the 8-digit ecnbasic code (Seattle 03363000)geography_type: "place" to reach one. Optional county_fips scopes county and tract resolution (county_scope_unsupported for any other level or an address)047) with a noticeambiguous_name with every candidate's FIPS code and state. Results carry state_fips (→ parent_fips) and fips_summary (→ geography_fips) for the next tool; statistical areas and ZCTAs omit state_fipscensus_query_data toolcensus_resolve_geography for place names). geography_fips: "*" returns every geography at the level within the parent; each row carries geography_fips and the nationally unique geography_geoid. Wildcards return up to limit rows (default 50, max 500) in GEOID order, with offset for paging and totalCount and truncated for the resttoo_many_variables states the maximum). Unknown codes fail with variable_not_found; level and parent problems fail with parent_required or parent_not_accepted. Optional tract_fips (6 digits, with a concrete county_fips) scopes a block-group or decennial block query to one tractpredicates map filters the business, pep, and dec datasets and acs/acs1/spp (e.g., {"NAICS2017": "5112"}). Unset dimensions take a Census default, echoed per row in applied_filters; "*" returns one row per category, labelled in record. pep/charv returns several rows per geography, so pin one (e.g., {"MONTH": "7"})open_ended. On cbp, ecnbasic, and nonemp, withheld values are reported as suppressed with the flag's meaning. A null estimate means suppressed, a text cell (returned under value), or emptydec/ddhca and dec/ddhcb, a geography's POPGROUP group sits in only one table of a family; a no_data hint or notice names the sibling tables. Requires CENSUS_API_KEYcensus_compare_geographies toolgeographies list of GEOIDs/bare level codes; within/within_county scope to a state/county, omit for a national comparisonsort_by (default the first code; must be a requested code, else sort_by_not_requested), sort_dir (default desc), and limit (1 to 500, default 50); totalCount counts geographies before the limit. A count ranks by size, not rate, so rank a published percentage for a rate (S1701_C03_001E in acs/acs5/subject, DP04_0047PE in acs/acs5/profile)predicates, variable limit, validation, and applied_filters as census_query_data; multi-record datasets (pep/charv) or a "*" predicate fail with ambiguous_rows unless predicates pins one. Suppressed values sort last with the same reasons, and text values can't be ranked (the notice says so). Requires 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) and business-dataset withholding flags (e.g., D) surfaced as their published meanings instead of raw numbers or false zerosA public instance is available at https://census.caseyjhand.com/mcp — no installation required. It serves Streamable HTTP; each client takes its own configuration shape.
For Claude Code:
claude mcp add --transport http census-mcp-server https://census.caseyjhand.com/mcp
For Cline (cline_mcp_settings.json):
{
"mcpServers": {
"census-mcp-server": {
"type": "streamableHttp",
"url": "https://census.caseyjhand.com/mcp"
}
}
}
For Cursor (mcp.json):
{
"mcpServers": {
"census-mcp-server": {
"url": "https://census.caseyjhand.com/mcp"
}
}
}
Claude Desktop adds remote servers under Settings → Connectors.
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. | 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 |
LOG_TOOL_FAILURE_PAYLOADS | Log each failed tool call's arguments and result, redacted by key name and capped at LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES (default 16384). A secret inside a free-form value is not redacted. | false |
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.