MCP server for querying U.S. Census Bureau data, including dataset discovery, variable search, geography resolution, and data retrieval from the Census API.
Six tools with complete Zod schemas and descriptions. Naming follows verb_noun pattern (discover_, list_, search_, resolve_, query_). Descriptions are detailed (100-250 chars) and include context on when to use each tool and dependencies. All parameters have type definitions and descriptions. However, output schemas are not formally documented, responses are inferred from code (JSON arrays/objects). Error handling is basic (generic 'no results' messages). No input validation beyond Zod type checking. Missing tool annotations (readOnlyHint, idempotentHint). All tools are READ_ONLY per spec, but this is not declared in the tool definitions.
Start here. Search the Census Bureau dataset catalog by topic (e.g., 'income', 'population', 'business'). Returns dataset IDs and available years. Use the dataset ID and a vintage year in subsequent tool calls. Common datasets: acs/acs5 for demographics, cbp for business patterns, cps/basic for employment.
After choosing a dataset, call this to see what geographic levels are available (state, county, tract, etc.). The 'requires' field shows what parent geography must be specified. For example, county requires state. Use the output to decide what 'for' and 'in' parameters to pass to query_data.
Fetch data from the Census API. Requires a dataset ID and vintage from discover_datasets, variable codes from search_variables, and geography from resolve_geography or list_geographies. Example: query_data({ dataset: 'acs/acs5', vintage: 2024, get: ['NAME', 'B19013_001E'], for: 'county:*', in: 'state:17' }) returns median household income for all counties in Illinois.
Convert a place name like 'California' or 'Cook County' to Census FIPS codes. Returns ready-to-use 'for' and 'in' clauses for query_data. Only covers states and counties. For tracts, block groups, or places, pass FIPS codes directly to query_data (use list_geographies to see what's available).
Output schemas not formally documented. Tool responses return JSON arrays/objects, but the structure (field names, types, required fields) is not declared in tool definitions. LLMs cannot reliably parse or chain results.
Tool annotations missing. All six tools are READ_ONLY (no side effects), but readOnlyHint is not declared in tool definitions. This prevents clients from optimizing caching and retry logic.
Error handling is generic. Responses return 'No results found' or 'No matches' without actionable recovery guidance. Per pattern:recovery-guide, errors should suggest next steps (e.g., 'Try broader search terms' or 'Call discover_datasets first').
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 59 | 2026-07-28+ | v2 |
Search for table groups — pre-built collections of related variables (e.g., 'B01001 = Sex by Age'). Often easier than searching individual variables. After finding a group, use search_variables to see its individual variables, or pass specific variable codes to query_data.
Search for specific variables (data columns) within a dataset by keyword. For example, search 'median household income' in acs/acs5. Returns variable codes like B19013_001E that you pass to query_data's 'get' parameter. Tip: also try search_groups to find pre-built tables of related variables.
No pagination support. query_data returns all rows without limit or offset parameters. Large result sets (e.g., all counties in the US) could exceed context windows. Per pattern:paginated-result, tools returning lists should support limit/offset and return total count.
Parameter descriptions include example values (e.g., 'e.g., income, population, business'). Per pattern:constrained-input, examples should be replaced with formal constraints (enums, regex patterns). LLMs may reuse examples literally.