census-mcp-server
Server Details
Query U.S. Census Bureau data, variables, and geography via MCP.
- Status
- Healthy
- Last Tested
- Transport
- Streamable HTTP · MCP 2025-11-25
- URL
- Repository
- cyanheads/census-mcp-server
- GitHub Stars
- 2
- Server Listing
- census-mcp-server
TDQS
Scored across 8 tools
Each tool has a clearly distinct role: dataset discovery, geography discovery, variable search/retrieval, predicate enumeration, place resolution, data querying, and multi-geography comparison. Even the two variable-related tools are cleanly separated by search vs. metadata lookup.
All tool names follow the same census_verb_noun pattern in snake_case, with verbs like list, get, search, query, compare, and resolve. The naming convention is uniform and predictable across the entire set.
Eight tools is well-scoped for a Census data access server. The set covers discovery, metadata exploration, querying, and comparison without unnecessary redundancy or bloat.
The tool surface covers the full Census workflow: discover datasets, list geography levels, enumerate predicate values, search and inspect variables, resolve place names to FIPS, and query or compare data. There are no obvious dead ends or missing operations for read-only Census data access.
Available Tools
8 toolscensus_compare_geographiesCompare Census GeographiesARead-onlyInspect
Compare one or more variables across multiple geographies at the same level — all counties in a state, all states nationally, or a named set of specific geographies. Results are sorted and ranked. Covers queries like "rank states by poverty rate", "compare median income across WA counties", or "which census tracts in King County have the highest renter rate." Omit within to compare all geographies nationally at the level. Suppressed values are decoded to human-readable labels rather than passed through as raw negative sentinels. On the business datasets (cbp, ecnbasic, nonemp), pep/charv, and dec/ddhca, use predicates to rank within one industry, size class, or population group — a comparison that omits one ranks on a default the Census API picks, which is an all-categories total on some dimensions and a single category on others. Each row names the defaults that were applied in applied_filters, and census_list_predicate_values enumerates the codes a dimension accepts. A dataset that publishes several records per geography cannot be ranked until one is pinned: pep/charv publishes an April estimates base and a July estimate, so a comparison that pins neither fails with ambiguous_rows rather than giving every geography two ranks — pass predicates {"MONTH": "7"} for the July estimate.
| Name | Required | Description | Default |
|---|---|---|---|
| year | No | Vintage year (default: latest available for the dataset). | |
| limit | No | Maximum geographies to return (default: 50, max: 500). When results are truncated, total_count indicates how many matched. | |
| within | No | State FIPS to constrain results (e.g., "53" to compare counties or tracts within WA only). Omit to compare all geographies at the level nationally. Use census_resolve_geography to get state_fips. Pass "*" to span every state. Blank is treated as omitted. | |
| dataset | No | Dataset to query (default: "acs/acs5"). Use census_list_datasets for valid values. | |
| sort_by | No | Variable code to sort by (default: first variable in the list). Must be one of the requested variable codes. | |
| sort_dir | No | Sort direction (default: "desc" — highest value first). | |
| variables | Yes | Variable codes to compare (e.g., ["B17001_002E", "B17001_001E"]). On ACS datasets, add the margin-of-error counterpart of a code (same code, E suffix swapped for M) for reliability context. Other dataset families (pep, dec, cbp, ecnbasic, nonemp) publish no margins of error. | |
| predicates | No | Filter values keyed by variable code, applied to every geography in the comparison — e.g. {"NAICS2017": "5112"} to rank counties by their software-publisher establishment count in cbp. The business datasets (cbp, ecnbasic, nonemp), pep/charv, and dec/ddhca declare filter dimensions such as industry (NAICS2017/NAICS2022), legal form (LFO), size class (EMPSZES/RCPSZES), tax status (TAXSTAT), operation type (TYPOP), sex (SEX), age (AGE), and population group (POPGROUP). Leaving one unset is not an error: the Census API substitutes its own default, which is the all-categories total on cbp NAICS2017 but a single population group on dec/ddhca POPGROUP and a single sector on ecnbasic NAICS2022 — so a ranking can read like an overall one without being it. Every unset dimension is named in the response notice and its applied default is echoed per row in applied_filters. Code names vary by dataset and vintage — cbp 2023 uses NAICS2017 while nonemp 2023 uses NAICS2022 — so read them from the notice or from census_search_variables. Call census_list_predicate_values for the codes a dimension accepts; NAICS values are standard North American Industry Classification System codes at any depth (51 information, 5112 software publishers). | |
| geographies | No | Optional list of specific geographies to include; only these are returned. Prefer full GEOIDs — the level concatenated with its parents, e.g. "53033" for King County WA and "06037" for Los Angeles County CA — which are nationally unique and so work across states. Bare level codes ("033") are also accepted but match that code in every state unless within scopes them to one. A GEOID is easiest taken from the geography_geoid field of a census_query_data or census_compare_geographies row; from census_resolve_geography, concatenate state_fips, then county_fips when it is present, then fips_summary. Entries that match nothing, and bare codes that match more than one state, are named in the response notice. | |
| within_county | No | County FIPS to constrain tract or block-group comparisons to a single county within the state specified by within (e.g., "033" for King County). Required when geography_level is "tract" or "block group" and you want county-scoped results. census_resolve_geography returns this as county_fips. Pass "*" to span every county in the state, which is the only way a block-group comparison reaches a whole state. Blank is treated as omitted. | |
| geography_level | Yes | The level to compare across (e.g., "state", "county", "tract"). Use census_list_geographies to see valid values for the dataset. |
Output Schema
| Name | Required | Description |
|---|---|---|
| rows | No | Geographies sorted by the requested variable. Suppressed values are labeled. |
| year | No | Vintage year queried. |
| error | No | Present when the call failed. Absent on success. |
| notice | No | Guidance when results were truncated, when geographies entries matched no row, when a bare level code matched more than one state, or when the dataset declares filter dimensions the comparison left unset — how to narrow scope, raise the limit, correct the FIPS codes, or add the predicates that pin what the ranking covers. For an unset dimension it also quotes the label of the default the Census API applied, which is what says whether the ranking is on a total or on one category. |
| dataset | No | Dataset queried. |
| truncated | No | True when totalCount exceeds the limit and results were cut off. |
| totalCount | No | Total number of geographies matched before the limit was applied. |
| sortVariable | No | Variable code used for sorting. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, and the description adds substantial behavior beyond that: suppressed values are decoded to readable labels, unset predicates use Census-chosen defaults with per-row applied_filters, and unpinned multi-record datasets fail with ambiguous_rows rather than returning duplicate ranks. This covers failure modes and default behavior that the agent would otherwise have to discover at runtime.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
At around 350 words the description is long, but every sentence carries a distinct piece of guidance: scope, ranking, suppressed values, predicate defaults, ambiguous_rows, and GEOID construction. It is front-loaded with the core purpose and examples before moving to edge cases, which is appropriate for a complex 11-parameter tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity – 11 parameters, nested predicates, multiple dataset families, and subtle failure modes – the description covers the critical traps (default predicate categories, ambiguous_rows, bare GEOID ambiguity) and points to sibling tools for valid values. The presence of an output schema covers return-shape details, so nothing essential is left for the agent to guess.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already has exhaustive descriptions for all 11 parameters (100% coverage), so the baseline is 3. The description nonetheless adds practical value by explaining the ACS margin-of-error convention, the MONTH pinning example for pep/charv, and how to construct GEOIDs from census_resolve_geography fields. It does not restate the schema, and the added examples are directly actionable.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb-resource pair ('Compare one or more variables across multiple geographies at the same level'), provides three concrete query examples, and emphasizes sorted/ranked output, which clearly separates it from a plain data retrieval tool. It also states scope behavior ('Omit within to compare all geographies nationally'), making the tool's job unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear usage contexts: ranking states/counties/tracts, comparing within a state vs nationally, and using named geography sets. It references sibling tools for prerequisites (census_resolve_geography, census_list_predicate_values, census_search_variables), but it does not explicitly say when to prefer census_query_data or when this tool is inappropriate, so exclusions are absent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
census_get_variableGet Census Variable MetadataARead-onlyInspect
Fetch full metadata for one or more Census variable codes — label, concept group, predicate type, universe, and margin-of-error sibling references. Use to confirm a variable code before building a query, or to look up what a known code means. On ACS datasets it returns estimate_code and moe_code sibling references so you can request both without a separate search; other dataset families publish no margins of error and carry neither field. It also resolves predicate codes such as NAICS2017 or SEX, confirming a filter dimension exists in a dataset before a query uses it — for the values a dimension accepts rather than the dimension itself, call census_list_predicate_values.
| Name | Required | Description | Default |
|---|---|---|---|
| year | No | Vintage year (default: latest available for the dataset). | |
| dataset | No | Dataset the variables belong to (default: "acs/acs5"). Use census_list_datasets to discover valid values. | |
| variables | Yes | One or more variable codes to look up (e.g., ["B19013_001E", "B19013_001M"]). Variable codes are case-sensitive. |
Output Schema
| Name | Required | Description |
|---|---|---|
| year | No | Vintage year queried. |
| error | No | Present when the call failed. Absent on success. |
| dataset | No | Dataset queried. |
| variables | No | Variable metadata in the same order as the input array. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With readOnlyHint=true already signaling safety, the description adds valuable behavioral detail: ACS datasets return estimate_code and moe_code siblings while other dataset families do not, and predicate codes are resolved to confirm filter dimensions. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is slightly longer than minimal but each sentence carries distinct value: primary purpose, use cases, dataset-family differences, and routing to a sibling tool. The key purpose is front-loaded in the first sentence.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only metadata lookup tool with an output schema and full schema coverage, the description covers purpose, use cases, alternatives, and cross-dataset behavioral differences. Nothing essential is missing for an agent to select and invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3 and the schema already documents all parameters. The description adds useful examples and output behavior, but does not materially deepen parameter semantics beyond what the schema provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Fetch full metadata for one or more Census variable codes,' and enumerates the returned fields. It also contrasts with census_list_predicate_values, making it easy to distinguish from siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly says when to use the tool: 'Use to confirm a variable code before building a query, or to look up what a known code means.' It also names an alternative for a related case: predicate values should go to census_list_predicate_values.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
census_list_datasetsList Census DatasetsARead-onlyInspect
Browse available Census Bureau datasets with their supported vintage years. Use as the starting point when the right dataset is unknown — ACS5, ACS1, population estimates, decennial census, and the business datasets (County Business Patterns, Economic Census, Nonemployer Statistics) serve different use cases. Pass the dataset_id value to the dataset parameter in other census tools. Each description names the predicates a dataset requires and the geography levels it publishes, both of which vary by dataset.
| Name | Required | Description | Default |
|---|---|---|---|
| filter | No | Keyword to filter datasets by name or description. Omit to list all datasets. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| notice | No | Guidance when no datasets matched the filter keyword. |
| datasets | No | Matching Census datasets. |
| totalCount | No | Total number of matching datasets. |
| filterApplied | No | Filter keyword applied to the dataset list, when provided. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already provide readOnlyHint=true and openWorldHint=false, so the description adds context beyond those: it discloses that datasets have supported vintage years and that each entry names required predicates and geography levels, which vary by dataset. This tells the agent what kind of information the tool returns without contradicting the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The four-sentence description is efficient: purpose, when-to-use, downstream output consumption, and the nature of each returned entry. The enumeration of dataset types is slightly listy but adds useful context. The core purpose is front-loaded in the first sentence.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a list tool with one optional parameter, no required parameters, a read-only annotation, and an output schema, the description is complete. It covers what the tool does, when to use it, what the returned entries contain, and how to use the dataset_id afterward. No critical information is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There is only one parameter, filter, and the schema already fully describes it ('Keyword to filter datasets by name or description. Omit to list all datasets.'), giving 100% schema coverage. The description adds output-related context (dataset_id reuse) but not additional input semantics, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Browse available Census Bureau datasets', a clear verb+resource pair, and explicitly names the dataset families covered (ACS5, ACS1, population estimates, decennial census, business datasets). It frames itself as the discovery/starting point, distinguishing it from sibling list tools like census_list_geographies and census_list_predicate_values.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives an explicit selection condition: 'Use as the starting point when the right dataset is unknown.' It also explains what each dataset description contains (required predicates, geography levels) so an agent can judge fit, and notes that the resulting dataset_id is fed into other census tools. It does not explicitly list exclusions, but the starting-point framing strongly implies when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
census_list_geographiesList Census Geography LevelsARead-onlyInspect
List the geography levels available for a given Census dataset and year, along with the parent geographies each level requires. Use before querying to confirm that the target geography level exists — ACS1 omits many sub-state levels, and not all datasets support tracts or block groups. The geography_level values returned here are the valid inputs to the geography_level parameter in census_query_data and census_compare_geographies.
| Name | Required | Description | Default |
|---|---|---|---|
| year | No | Vintage year. Defaults to the latest available year for the dataset. | |
| dataset | Yes | Dataset code (e.g., "acs/acs5", "acs/acs1"). Use census_list_datasets to discover valid values. |
Output Schema
| Name | Required | Description |
|---|---|---|
| year | No | Vintage year queried. |
| error | No | Present when the call failed. Absent on success. |
| dataset | No | Dataset queried. |
| totalLevels | No | Total number of geography levels available for this dataset and year. |
| geography_levels | No | Geography levels supported by this dataset and year. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the description need not repeat that. It adds useful context beyond annotations: the output is intended as input for census_query_data and census_compare_geographies, and it warns that not all datasets support all geography levels. This enriches the behavioral profile without contradicting any annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences with no filler. The main purpose is front-loaded, followed by usage context and a practical note about output reusability. Every sentence earns its place, and the description is well-structured for quick scanning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity, the presence of an output schema, and annotations covering read-only behavior, the description is complete. It explains when to use it, what it returns (geography levels and required parent geographies), and how to use the result. No critical information is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%: both year and dataset have clear descriptions, including the default behavior for year and the pointer to census_list_datasets for valid dataset codes. The tool description does not add any additional parameter-level meaning beyond what the schema already provides, so a baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('List') and resource ('geography levels available for a given Census dataset and year'), and immediately clarifies the tool's role as a pre-query validation step. It distinguishes itself from siblings like census_query_data by emphasizing it returns valid inputs for those tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs 'Use before querying to confirm that the target geography level exists' and gives a concrete reason (ACS1 omits sub-state levels). It also notes the returned values are valid inputs to other tools, effectively stating when to use it and how the output connects to follow-up calls.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
census_list_predicate_valuesList Census Predicate ValuesARead-onlyInspect
List the codes a Census filter dimension accepts, so a predicates map can be written without guessing. Answers the question left open when census_query_data or census_compare_geographies reports that a dimension was left unset. Which route a dimension takes depends on the vintage: NAICS and POPGROUP always publish a value list in the dataset dictionary, and on the current vintages EMPSZES, LFO, RCPSZES, TAXSTAT, and TYPOP publish none and are enumerated here against the live data endpoint instead. A dictionary value list is a classification shared across Census products rather than a list of what one dataset serves, and roughly half of its codes typically return no rows anywhere — those are checked against the dataset's own published rows and dropped, and the response source field says whether that check ran. The dictionary lists run to thousands of codes and are best narrowed with query. Pass the returned code as the dimension's value in predicates.
| Name | Required | Description | Default |
|---|---|---|---|
| year | No | Vintage year (default: latest available for the dataset). | |
| limit | No | Maximum codes to return (default: 50, max: 500). | |
| query | No | Keyword to narrow the list, matched case-insensitively against each code and label (e.g., "software" against NAICS2017, "exempt" against TAXSTAT). Omit to list from the start. NAICS and POPGROUP run to thousands of codes, so a keyword is the practical way to use them. | |
| dataset | Yes | Dataset the dimension belongs to (e.g., "cbp", "nonemp", "ecnbasic", "dec/ddhca", "pep/charv"). Use census_list_datasets to discover valid values. Dimension codes are vintage-specific, so the dataset and year must match the query the values are for. | |
| predicate | Yes | Filter dimension code to enumerate (e.g., "EMPSZES", "LFO", "POPGROUP", "NAICS2017"). Case-sensitive. The response notice of census_query_data names the dimensions a dataset declares, and census_search_variables finds them by keyword. | |
| within_naics | No | Industry code to scope the enumeration by, for dimensions the Census publishes per industry. On ecnbasic, TAXSTAT and TYPOP return only the all-establishments row until a NAICS sector is named — pass a sector code such as "62" (Health Care) or "42" (Wholesale Trade) and the result is complete for that industry alone. Ignored for dimensions with a published value list. Get sector codes by calling this tool on the dataset's own NAICS dimension. Blank is treated as omitted. |
Output Schema
| Name | Required | Description |
|---|---|---|
| year | No | Vintage year queried. |
| error | No | Present when the call failed. Absent on success. |
| notice | No | Guidance when the list was truncated, when query matched no published code, when codes from the dataset dictionary could not be checked against its published rows, or when the codes returned are complete only for a named industry rather than for the dimension as a whole. |
| source | No | Where the codes came from. "live_query" is a wildcard group-by against the data endpoint, which returns only codes the dataset publishes. "dataset_dictionary_verified" is the dataset's published value map with the codes it serves no rows for removed. Plain "dataset_dictionary" is that map unchecked — every code in it is declared by the dataset, but some of them return nothing at any geography, and the notice says why the check did not run. |
| values | No | Codes the dimension accepts, sorted by code. |
| dataset | No | Dataset queried. |
| predicate | No | Filter dimension enumerated. |
| truncated | No | True when totalCount exceeds the limit and the list was cut. |
| totalCount | No | Codes matched before the limit was applied. |
| predicate_label | No | Label of the dimension itself (e.g., "Employment size of establishments code"). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare readOnlyHint=true and openWorldHint=false, and the description adds substantial behavioral detail: dictionary lists are shared classifications, codes that return no rows are dropped, and the response source field indicates whether that check ran. It also warns that dictionary lists run to thousands of codes and benefit from query narrowing, which goes well beyond the structured annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence earns its place: core purpose is front-loaded, then usage context, then behavioral caveats, then practical guidance. No filler or restatement of the title.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with six parameters, two required, and an output schema, the description covers purpose, when to use it, how to avoid pitfalls, parameter relationships, and post-call behavior. The presence of an output schema means return-value documentation is already handled elsewhere, so nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description goes much further. It explains the special behavior of within_naics for TAXSTAT and TYPOP on ecnbasic, gives concrete query examples, notes case-sensitivity for predicate, and explains why dataset and year must align. This adds real operational meaning beyond the raw parameter descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'List the codes a Census filter dimension accepts'. It also explains the exact use case (writing predicates without guessing) and connects to sibling tools by addressing the gap left when census_query_data or census_compare_geographies reports a dimension unset.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says when to use the tool — when a dimension was left unset — and names discovery alternatives: census_list_datasets for datasets and census_search_variables for predicate dimensions. It also explains the vintage-dependent routing between dictionary value lists and live endpoint enumeration, so an agent knows how the tool behaves in each case.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
census_query_dataQuery Census DataARead-onlyInspect
Query a Census dataset for one or more variables at a specific geography. Accepts FIPS codes for the target geography — use census_resolve_geography to convert place names to FIPS when needed. On ACS datasets, labeled estimates and margin-of-error values are returned together. Suppression codes (geography too small, data not collected) are decoded into human-readable reasons rather than passed through as raw negative numbers. Pass geography_fips as "*" to return all geographies at the level within the parent. On the business datasets (cbp, ecnbasic, nonemp), pep/charv, and dec/ddhca, use predicates to filter by industry, size class, or population group — a query that omits one is answered with a default the Census API picks, which is an all-categories total on some dimensions and a single category on others. Each row names the defaults that were applied in applied_filters, and census_list_predicate_values enumerates the codes a dimension accepts. One geography can also come back on more than one row: pep/charv publishes an April estimates base alongside its July estimate, and each row carries a record field saying which it is.
| Name | Required | Description | Default |
|---|---|---|---|
| year | No | Vintage year (default: latest available for the dataset). | |
| dataset | No | Dataset to query (default: "acs/acs5"). Use census_list_datasets to discover valid values. | |
| variables | Yes | Variable codes to retrieve (e.g., ["B19013_001E", "B19013_001M"]). Max 50 per request. Use census_search_variables to find codes. On ACS datasets only, each estimate has a margin-of-error counterpart at the same code with the E suffix swapped for M — request both to get the margin alongside the estimate. Other dataset families (pep, dec, cbp, ecnbasic, nonemp) publish no margins of error, and an E-final code there is an ordinary code with no M sibling. A code can also name a text column rather than a measure — GEO_ID, on every dataset, is the nationally unique geography identifier and comes back under value with estimate null, which is the code to request when a stable join key is what is wanted. | |
| predicates | No | Filter values keyed by variable code, sent as extra query parameters — e.g. {"NAICS2017": "5112"} to count only software publishers in cbp. The business datasets (cbp, ecnbasic, nonemp), pep/charv, and dec/ddhca declare filter dimensions such as industry (NAICS2017/NAICS2022), legal form (LFO), size class (EMPSZES/RCPSZES), tax status (TAXSTAT), operation type (TYPOP), sex (SEX), age (AGE), and population group (POPGROUP). Leaving one unset is not an error: the Census API substitutes its own default, which is the all-categories total on cbp NAICS2017 but a single population group on dec/ddhca POPGROUP and a single sector on ecnbasic NAICS2022 — so an unfiltered value can read like a total without being one. Every unset dimension is named in the response notice and its applied default is echoed per row in applied_filters. Code names vary by dataset and vintage — cbp 2023 uses NAICS2017 while nonemp 2023 uses NAICS2022 — so read them from the notice or from census_search_variables. Call census_list_predicate_values for the codes a dimension accepts; NAICS values are standard North American Industry Classification System codes at any depth (51 information, 5112 software publishers). | |
| county_fips | No | County FIPS code when querying tracts or block groups within a specific county (e.g., "033" for King County within WA). Required for tract and block-group queries scoped to a county — use alongside parent_fips (state). census_resolve_geography returns this as county_fips. Pass "*" to span every county in the state, which is the only way a block-group query reaches a whole state. Blank is treated as omitted. | |
| parent_fips | No | State FIPS code when querying sub-state levels (e.g., "53" for Washington). Required for county, tract, and block-group queries. census_resolve_geography returns this as state_fips. Pass "*" to span every state. Blank is treated as omitted. | |
| geography_fips | Yes | FIPS code for the target geography (e.g., "033" for a county, "*" for all geographies at the level within the parent). Use census_resolve_geography to obtain this value — it is returned as fips_summary. The Census API matches this literally and its width follows geography_level, so it is passed through unpadded: a county is 3 digits ("051", not "51") and a tract is 6. parent_fips and county_fips are zero-padded for you; this one is not. | |
| geography_level | Yes | Level of the target geography (e.g., "county", "tract", "state", "zip code tabulation area"). Use census_list_geographies to see valid values for the dataset. |
Output Schema
| Name | Required | Description |
|---|---|---|
| rows | No | One row per geography. When geography_fips is "*", includes all geographies at the level within the parent. |
| year | No | Vintage year queried. |
| error | No | Present when the call failed. Absent on success. |
| notice | No | Warning that the dataset declares filter dimensions the query left unset, naming each one alongside the label of the default the Census API applied to it. That default is an all-categories total on some dimensions and one ordinary category on others, so the label is what says which. Also carries the warning that a geography came back on more than one row, naming the column that separates the records and the values it took. |
| dataset | No | Dataset queried. |
| totalRows | No | Number of geography rows returned. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations provide readOnlyHint=true and openWorldHint=false, and the description adds substantial behavior beyond those: suppression codes are decoded into human-readable reasons, omitted predicates are filled with Census defaults and echoed in applied_filters, and pep/charv can return a geography on multiple rows distinguished by a record field. This far exceeds the annotation-only baseline with no contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is long but appropriately dense for a complex tool: it front-loads the core action, then covers preparation, dataset-specific behavior, defaults, and multi-row results without fluff. Each section adds distinct information an agent needs, and the structure moves from general to specific cleanly.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity — 8 parameters, multiple dataset families, nested predicates, and an output schema that exists — the description covers all high-risk behaviors: FIPS construction, variable selection, suppression handling, default predicate values, and duplicate rows. The helper tool names fill the remaining gaps, so an agent can invoke this correctly in nearly all cases.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Even though schema coverage is 100% and the input schema is already detailed, the tool description adds significant meaning beyond the schema: FIPS width and padding rules, ACS E/M suffix pairing, how to request stable geography keys via GEO_ID, and the consequences of omitting predicates. This gives an agent the operational judgment needed to construct valid queries.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb and resource ('Query a Census dataset for one or more variables at a specific geography') and immediately distinguishes this from sibling helpers by routing FIPS conversion to census_resolve_geography. It also adds dataset-specific behavior such as ACS MOE pairing, suppression decoding, and pep/charv duplicate rows, so an agent knows exactly what this tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly directs agents to sibling tools when preparation is needed: census_resolve_geography for FIPS codes, census_list_geographies for valid levels, census_search_variables for variable codes, and census_list_predicate_values for predicate codes. It lacks a direct 'do not use this when...' statement, but the routing is clear enough to guide correct selection among the siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
census_resolve_geographyResolve Census GeographyARead-onlyInspect
Resolve a place name or street address to Census FIPS identifiers. Converts names like "King County, WA", "Seattle, WA", or "Seattle-Tacoma-Bellevue, WA" to the codes required by census_query_data and census_compare_geographies. Use before querying when you have a place name rather than raw FIPS codes — state_fips maps to parent_fips and fips_summary maps to geography_fips in downstream tools, and geography_type is itself the geography_level to query at.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Place name (e.g., "King County, WA", "Seattle, WA", "California") or street address (e.g., "1600 Pennsylvania Ave NW, Washington, DC 20500"). Include the state abbreviation to disambiguate places with common names — it narrows a statistical area as well, matching any state the area spans, so "Kansas City, MO" and "Kansas City, KS" both reach the MO-KS metro area. For a statistical area, the name is the full hyphenated one the Census publishes ("Seattle-Tacoma-Bellevue, WA" for the metro area, "Seattle-Tacoma, WA" for the combined one) — a single city name matches it too when only one area contains that city. | |
| county_fips | No | County FIPS code to resolve within — 1 to 3 digits, zero-padded here to the 3 the Census stores. A tract name is unique only inside its county, so a bare tract name matching two counties comes back as ambiguous_name until this is set: take the countyFips of the candidate you want from that error and re-call. Only county and tract sit within a county, so this restricts resolution to those two levels — pairing it with any other geography_type, or with a street address, is a county_scope_unsupported error rather than a scope quietly dropped. census_query_data takes the same code as its own county_fips but pads nothing, so hand it the 3-digit county_fips returned here, not the shorter value. | |
| geography_type | No | Geography level to resolve to, named exactly as census_query_data's geography_level and census_list_geographies name it. Auto-detection covers only state, county, place, and tract: state for a two-letter abbreviation or a spelled-out state name, county when the name contains "County"/"Borough"/"Parish", tract when it contains "Tract", otherwise place with a fallback to county. The other three are never auto-detected and must be set explicitly, because their names overlap city names — "metropolitan statistical area/micropolitan statistical area" covers both metro and micro areas and yields a 5-digit code, "combined statistical area" yields a 3-digit code, and "consolidated city" covers the eight merged city-county governments (Nashville-Davidson, Louisville/Jefferson County, Indianapolis, Athens-Clarke County, Augusta-Richmond County, Butte-Silver Bow, Milford CT, Greeley County KS). Setting it explicitly also overrides auto-detection — "New York" auto-detects as the state, so New York City needs "place". |
Output Schema
| Name | Required | Description |
|---|---|---|
| name | No | Canonical name of the resolved geography. |
| error | No | Present when the call failed. Absent on success. |
| place_fips | No | Place FIPS code when the resolved geography is an incorporated place. |
| state_fips | No | 2-digit state FIPS code. Use as parent_fips in census_query_data for sub-state queries. Absent for a metropolitan/micropolitan or combined statistical area, which can span several states and needs no parent_fips. |
| tract_fips | No | 6-digit census tract FIPS code when the resolved geography is a tract — from a street address, or from a tract name. |
| county_fips | No | 3-digit county FIPS code when the resolved geography is a county or sub-county level. |
| fips_summary | No | Pre-formatted FIPS value ready to use as geography_fips in census_query_data (e.g., "033" for King County with state_fips "53" as parent_fips, "42660" for the Seattle-Tacoma-Bellevue metro area with no parent at all). |
| geography_type | No | Resolved geography level. Pass this straight through as geography_level in census_query_data and census_compare_geographies. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint, the description discloses auto-detection scope (state/county/place/tract only), explicit override behavior, ambiguity errors (ambiguous_name), county_scope_unsupported errors, and Census padding conventions. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The three-sentence description front-loads the purpose, uses concrete examples, and packs downstream routing and auto-detection caveats into a compact, high-density text. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a resolver with an output schema, a read-only annotation, and only one required parameter, the description plus parameter docs cover use cases, disambiguation, error states, and downstream integration. Nothing needed to invoke it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema already documents all three parameters at 100% coverage, so the baseline is 3. The description adds downstream field mapping (state_fips to parent_fips, fips_summary to geography_fips), but this mostly concerns output values rather than input parameter semantics, so it does not push beyond the schema's substantial parameter descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb-resource pair: 'Resolve a place name or street address to Census FIPS identifiers.' It provides concrete examples and explicitly ties the output codes to census_query_data and census_compare_geographies, which distinguishes it from all sibling data-retrieval tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It directly says 'Use before querying when you have a place name rather than raw FIPS codes,' giving an explicit precondition and routing decision. It also explains how the returned fields map into downstream tools, so an agent knows exactly when this tool is the necessary first step.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
census_search_variablesSearch Census VariablesARead-onlyInspect
Search Census variables by keyword across variable labels and concept groups. Returns variable codes with human-readable labels — use this to go from a concept like "median household income" to the variable code B19013_001E needed for data queries. On ACS datasets it returns both estimate (E suffix) and margin-of-error (M suffix) codes so you can request both; other dataset families publish no margins of error. Also use it to find the predicate codes a dataset filters on, such as NAICS2017 in cbp. When total_matches exceeds the limit, narrow the query to see more specific results.
| Name | Required | Description | Default |
|---|---|---|---|
| year | No | Vintage year to search (default: latest available for the dataset). | |
| limit | No | Maximum results to return (default: 20, max: 100). Increase if total_matches greatly exceeds the limit. | |
| query | Yes | Keyword to search (e.g., "median household income", "poverty", "bachelor's degree"). Multi-word queries search for all terms. | |
| dataset | No | Dataset to search within (default: "acs/acs5"). Use census_list_datasets to discover options. |
Output Schema
| Name | Required | Description |
|---|---|---|
| cap | No | The limit that was applied. |
| year | No | Vintage year that was searched. |
| error | No | Present when the call failed. Absent on success. |
| shown | No | Number of variables returned after the limit. |
| notice | No | Guidance when no variables matched, or when results were truncated — suggests broader keywords, a narrower query, or a higher limit. |
| dataset | No | Dataset that was searched. |
| truncated | No | True when total_matches exceeded the limit and results were cut off. |
| variables | No | Matching variables sorted by relevance. On ACS datasets, codes ending in E are estimates and M are their margins of error; on other datasets the suffix carries no such meaning. |
| totalMatches | No | Total variables matching the query before the limit was applied. |
| effectiveQuery | No | Query as the server parsed it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Even with readOnlyHint=true, the description adds substantial behavioral detail: ACS returns both E-suffix and M-suffix codes while other dataset families do not, it can locate predicate codes, and it explains how to respond when total_matches exceeds the limit. These are meaningful behaviors not visible from annotations alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-organized, front-loads the core purpose, and each sentence contributes either a use case, a dataset nuance, or search guidance. There is no filler or repetition of schema details.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the output schema exists and the tool is a straightforward search operation, the description covers everything an agent needs: what to search for, what results look like, dataset-specific behavior, special predicate-code usage, and how to handle overly broad results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds value by clarifying that queries search across labels and concept groups, providing realistic example queries, and explaining dataset-specific output behavior, which enriches the meaning of the query and dataset parameters beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb (search), resource (Census variables), and scope (keyword across variable labels and concept groups). It goes further by explaining the practical transformation from a concept like 'median household income' to a variable code, clearly differentiating it from sibling tools like census_get_variable or census_query_data.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear, actionable context: use this to convert concepts to variable codes, to get both estimates and margins of error on ACS, and to find predicate codes like NAICS2017. It does not explicitly state when not to use it or name alternatives, but the examples strongly imply its niche among the sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
8 tool updates
- First observed
census_compare_geographies - First observed
census_get_variable - First observed
census_list_datasets - First observed
census_list_geographies - First observed
census_list_predicate_values - First observed
census_query_data - First observed
census_resolve_geography - First observed
census_search_variables
Related MCP Connectors
Query US Census Bureau data: demographics, economics, and housing statistics.
Query US Treasury national debt, interest rates, exchange rates, and fiscal datasets via MCP.
Census MCP — U.S. Census Bureau housing-relevant APIs.
Access U.S. congressional data - bills, votes, members, committees - via MCP.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceQuery SEC EDGAR filings, XBRL financials, and company data through MCP.698 npm10Apache 2.0
- AlicenseNot gradedqualityAmaintenanceSearch and query government open-data portals (Socrata SODA API) via MCP.81 npm3Apache 2.0
- FlicenseNot gradedqualityCmaintenanceA production-grade MCP server for querying U.S. Census Bureau data (ACS 5-Year and Decennial) with tools for geographic fuzzy matching, variable search, and batched data retrieval, backed by a PostgreSQL cache for performance.-
- AlicenseNot gradedqualityAmaintenanceAccess FCC broadband availability, coverage analysis, and digital divide data for US geographies and census blocks via MCP.82 npm1Apache 2.0
Glama MCP Gateway
Add one secure layer between your agents and this server.