Skip to main content
Glama

πŸ“Š IPUMS MCP

License: MIT

A Model Context Protocol (MCP) server that exposes the IPUMS API as tools for LLM clients. Supports IPUMS microdata collections (USA, CPS, IPUMSI, etc.) and NHGIS aggregate/GIS data. Includes tools for browsing metadata, submitting and downloading extracts, generating reproducible R/Python code, and producing formatted citations.

πŸ“‹ Prerequisites

  • Node.js 18+

  • An IPUMS API key

  • Any MCP-compatible client (Claude Desktop, Claude Code, Cursor, Windsurf, etc.)

Related MCP server: census-mcp-server

πŸ› οΈ Installation

1. Clone and build

git clone https://github.com/hesscl/ipums-mcp
cd ipums-mcp
npm install
npm run build

2. Configure your MCP client

All MCP clients use the same server entry point β€” only the config file location differs. Set IPUMS_API_KEY as an environment variable; never hard-code it.

The server command is always:

node /absolute/path/to/ipums-mcp/dist/index.js

Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "ipums": {
      "command": "node",
      "args": ["/absolute/path/to/ipums-mcp/dist/index.js"],
      "env": { "IPUMS_API_KEY": "your-key-here" }
    }
  }
}

Restart Claude Desktop after saving.

Claude Code (CLI)

claude mcp add ipums node /absolute/path/to/ipums-mcp/dist/index.js \
  -e IPUMS_API_KEY=your-key-here

Or add manually to .claude/settings.json:

{
  "mcpServers": {
    "ipums": {
      "command": "node",
      "args": ["/absolute/path/to/ipums-mcp/dist/index.js"],
      "env": { "IPUMS_API_KEY": "your-key-here" }
    }
  }
}

Cursor / Windsurf

Add to your editor's MCP settings (typically under Settings β†’ MCP or a mcp.json file):

{
  "mcpServers": {
    "ipums": {
      "command": "node",
      "args": ["/absolute/path/to/ipums-mcp/dist/index.js"],
      "env": { "IPUMS_API_KEY": "your-key-here" }
    }
  }
}

Other MCP clients

Any client that supports the MCP stdio transport works. Provide:

  • Command: node

  • Args: ["/absolute/path/to/ipums-mcp/dist/index.js"]

  • Env: IPUMS_API_KEY=your-key-here

πŸ”§ Tools

🧬 Microdata (USA, CPS, IPUMSI, ACS, NHIS, MEPS, …)

Tool

Description

microdata_search_variables

Search the offline variable database by name, label, group, or sample (no API key needed)

microdata_list_samples

List available samples for a collection (use to find sample IDs by year/survey)

microdata_list_extracts

List recent extracts for a collection

microdata_get_extract

Get status and download links for an extract

microdata_create_extract

Submit a new extract request

microdata_wait_for_extract

Poll until extract completes, with automatic bail-out for large extracts

microdata_download_extract

Download completed extract files to disk, with SHA-256 verification

microdata_extract_to_code

Generate reproducible R (ipumsr) or Python (ipumspy) code for an extract

microdata_search_variables queries a local database of 827 harmonized IPUMS USA variables scraped from usa.ipums.org β€” no API key or network request needed.

Each result includes:

  • name β€” variable mnemonic (e.g. INCTOT)

  • label β€” short description (e.g. "Total personal income")

  • type β€” "H" (household-level) or "P" (person-level)

  • groups β€” thematic category (e.g. "Person: Income")

  • samples β€” representative IPUMS USA sample IDs where the variable is available (e.g. ["us2024a", "us2023a", ..., "us1850a"])

Filter by any combination of query, type, group, and sample:

microdata_search_variables({ query: "income", type: "P", sample: "us2024a" })
microdata_search_variables({ group: "Education" })
microdata_search_variables({ query: "veteran", sample: "us1990a" })

The samples field lists the representative display columns from usa.ipums.org β€” a subset of all available samples. Cross-reference with microdata_list_samples for the complete list.

Create extract

Samples and variables use array syntax:

{
  "collection": "usa",
  "dataFormat": "csv",
  "dataStructure": { "rectangular": { "on": "P" } },
  "description": "2022 ACS veterans",
  "samples": [{ "id": "us2022a" }],
  "variables": [
    { "name": "VETSTAT" },
    { "name": "AGE" },
    { "name": "SEX" },
    { "name": "STATEFIP" }
  ]
}

The server converts these to the keyed-object format the IPUMS API v2 requires.

The response includes a size advisory based on samples Γ— variables to help decide whether to poll immediately or check back later:

Complexity

Advisory

≀ 200

Small β€” microdata_wait_for_extract is fine (1–3 min)

≀ 2,000

Medium β€” consider asking the user (3–15 min)

> 2,000

Large β€” recommend checking back with microdata_get_extract (20–60+ min)

Wait for extract

microdata_wait_for_extract defaults to a 90-second timeout. If the extract is still processing when the timeout is reached, the tool returns early with:

{
  "status": "still_processing",
  "message": "Extract 42 is still queued after 90s (6 check(s)). Ask the user if they would like to wait longer, or tell them to check back later using: microdata_get_extract({ collection: \"usa\", extractNumber: 42 })",
  "extract": { ... }
}

This keeps the conversation responsive β€” the user gets the extract number and can ask to resume polling or check back manually at their convenience. Pass a higher timeoutSeconds only if the user explicitly agrees to wait.

Download a completed extract

{
  "collection": "usa",
  "extractNumber": 42,
  "outputDir": "/data/ipums",
  "fileTypes": ["data", "ddiCodebook"]
}

Available fileTypes: data, ddiCodebook, basicCodebook, rCommandFile, spssCommandFile, stataCommandFile, stsCommandFile, sasCommandFile.

Note: The IPUMS API returns stsCommandFile (not stataCommandFile) for Stata syntax files in some collections. Include both if unsure.

Returns { downloaded: [...], errors: [...] }. Each entry includes localPath, bytes, and sha256Verified.

πŸ—ΊοΈ NHGIS (Aggregate & GIS Data)

Tool

Description

nhgis_search_datasets

Search all datasets by keyword (name, census group, description)

nhgis_search_data_tables

Search data tables by keyword, optionally scoped to a dataset

nhgis_search_time_series_tables

Search all time series tables by keyword

nhgis_list_datasets

Browse available datasets (paginated)

nhgis_get_dataset

Get tables and geographic levels for a dataset

nhgis_list_data_tables

List all data tables (paginated)

nhgis_get_data_table

Get variables in a data table

nhgis_list_time_series_tables

List time series tables (paginated)

nhgis_get_time_series_table

Get detail for a time series table

nhgis_list_shapefiles

List available shapefiles

nhgis_list_extracts

List recent NHGIS extracts

nhgis_get_extract

Get NHGIS extract status and download links

nhgis_create_extract

Submit a new NHGIS extract

nhgis_extract_to_code

Generate reproducible R (ipumsr) or Python (ipumspy) code for an NHGIS extract

Three tools enable natural-language discovery of NHGIS data, complementing the nhgis_list_* and nhgis_get_* tools:

nhgis_search_datasets β€” fetches all ~253 datasets in one call and returns those matching the keyword in their name, group (e.g. "2020 Census", "2019 ACS"), or description. Best first step when you know a topic or census year.

nhgis_search_time_series_tables β€” fetches all ~400 time series tables (which span multiple census years with consistent geographic definitions) and filters by keyword in name or description. Ideal for longitudinal analysis.

nhgis_search_data_tables β€” searches source data tables by keyword against description and universe:

  • With dataset parameter: fetches all tables for that dataset comprehensively.

  • Without dataset: scans the first 2,500 tables across all datasets (limited coverage β€” see note in response).

Recommended workflow:

nhgis_search_datasets("poverty")
  β†’ pick a dataset (e.g. "2019_ACS5a")
nhgis_get_dataset("2019_ACS5a")
  β†’ confirm available tables and geographic levels
nhgis_search_data_tables("poverty", dataset="2019_ACS5a")
  β†’ pick a table (e.g. "B17001")
nhgis_create_extract(...)
nhgis_extract_to_code(...)
  β†’ generate R/Python code to reproduce the extract

The NHGIS public API does not support server-side filtering. The search tools fetch full metadata pages and filter client-side.

πŸ“ Reproducibility & Citation

Three tools close the loop between "I pulled this data via AI" and "here's the exact code to pull it again."

Tool

Description

microdata_extract_to_code

Generate R or Python code to reproduce a microdata extract

nhgis_extract_to_code

Generate R or Python code to reproduce an NHGIS extract

generate_ipums_citation

Return a formatted citation template with authors, DOI, and official citation link

Code generation

After submitting or retrieving an extract, call microdata_extract_to_code (or nhgis_extract_to_code) to get ready-to-run code:

microdata_extract_to_code({ collection: "usa", extractNumber: 42, language: "both" })

R output (ipumsr):

library(ipumsr)

extract <- define_extract_usa(
  samples = c("us2022a"),
  variables = c("VETSTAT", "AGE", "SEX", "STATEFIP"),
  data_format = "csv",
  data_structure = "rectangular"
)

extract <- submit_extract(extract)
extract <- wait_for_extract(extract)
path    <- download_extract(extract)

ddi  <- read_ipums_ddi(path$ddi)
data <- read_ipums_micro(ddi)

Python output (ipumspy):

from ipumspy import IpumsApiClient, UsaExtract
import os

client = IpumsApiClient(api_key=os.environ["IPUMS_API_KEY"])

extract = UsaExtract(
    samples=["us2022a"],
    variables=["VETSTAT", "AGE", "SEX", "STATEFIP"],
    data_format="csv"
)

extract = client.submit_extract(extract)
client.wait_for_extract(extract)
client.download_extract(extract, download_dir="./downloads")

The language parameter accepts "r", "python", or "both" (default). For variables with case selections or data quality flags, R output uses var_spec() automatically. NHGIS output uses ds_spec()/tst_spec() for datasets and time series tables, with camelCase API keys converted to snake_case for both languages.

Citation

generate_ipums_citation returns the standard citation for any IPUMS collection β€” no API call needed:

generate_ipums_citation({ collection: "nhgis" })

Returns the full author list, DOI, and a direct link to the official citation page where the current version number is always listed. Replace [YEAR] and VX with the current year and version before publishing.

πŸš€ Workflow: Jupyter MCP + ipumsr

This server pairs naturally with a Jupyter MCP server to form a complete data pipeline β€” from browsing IPUMS metadata to live analysis in a notebook β€” without leaving your LLM client.

Full pipeline example

Step 1 β€” Browse and submit (via MCP tools in Claude) πŸ€–

1. nhgis_search_datasets        β†’ find datasets by keyword (topic, year, census program)
2. nhgis_search_data_tables     β†’ narrow to specific tables (scope to dataset for full coverage)
3. nhgis_get_dataset            β†’ confirm tables and geo levels
4. nhgis_create_extract         β†’ submit the extract
5. nhgis_get_extract            β†’ poll until status = "completed"
6. nhgis_extract_to_code        β†’ generate R/Python code to reproduce the extract

Or for microdata:

1. microdata_search_variables     β†’ find variable names by keyword or group
2. microdata_list_samples         β†’ confirm the right sample ID ("2022 ACS" β†’ us2022a)
3. microdata_create_extract       β†’ submit (size advisory tells you whether to wait or check back)
4. microdata_wait_for_extract     β†’ poll up to 90s; bail out gracefully for large extracts
5. microdata_download_extract     β†’ download data + DDI codebook
6. microdata_extract_to_code      β†’ generate R/Python code to reproduce the extract

πŸ’‘ Variable discovery: Use microdata_search_variables to look up mnemonics from natural language β€” search "income", "veteran", "race" β€” rather than relying on memorized variable names. Filter by sample to confirm a variable is available in your target year. For NHGIS, use nhgis_search_datasets and nhgis_search_data_tables to find tables by keyword, then nhgis_get_data_table to see all variables within a table.

Step 2 β€” Analyze in Jupyter with ipumsr (R kernel) πŸ““

Once microdata_download_extract has saved files locally, use the Jupyter MCP server to run R in a notebook:

library(ipumsr)

# Point at the DDI codebook downloaded by microdata_download_extract
ddi  <- read_ipums_ddi("/data/ipums/usa_00042.xml")
data <- read_ipums_micro(ddi)

# Variable labels and value labels are attached automatically
head(data)
attr(data$VETSTAT, "labels")

For Python notebooks 🐍:

import pandas as pd

# CSV data file downloaded by microdata_download_extract
df = pd.read_csv("/data/ipums/usa_00042.csv.gz")
df.head()

For NHGIS data (already CSV, no DDI needed):

library(ipumsr)

# read_nhgis reads the CSV and attaches codebook metadata
tbl <- read_nhgis("/data/ipums/nhgis0007_csv.zip", data_layer = 1)

Suggested config with Jupyter MCP

Add both servers to your MCP client config:

{
  "mcpServers": {
    "ipums": {
      "command": "node",
      "args": ["/absolute/path/to/ipums-mcp/dist/index.js"],
      "env": { "IPUMS_API_KEY": "your-key-here" }
    },
    "jupyter": {
      "command": "uvx",
      "args": ["jupyter-mcp-server"],
      "env": { "JUPYTER_TOKEN": "your-jupyter-token" }
    }
  }
}

With both servers running you can prompt your LLM client: "Submit a 2022 ACS extract for VETSTAT, AGE, SEX, STATEFIP, download it when ready, then open a Jupyter notebook and plot veteran counts by state with ggplot2." 🎯

πŸ”’ Security Notes

  • API key: Pass via IPUMS_API_KEY env var only. Never hard-code it or commit it to source control. For local development, store it in a .env file (gitignored).

  • Download URL validation: microdata_download_extract validates that each download URL uses HTTPS and resolves to an *.ipums.org host before sending your API key. URLs that fail this check are skipped and reported in errors.

  • Path traversal protection: Downloaded filenames are resolved against outputDir and rejected if the resolved path would escape that directory.

  • SHA-256 verification: Every downloaded file is verified against the checksum provided by the API. A mismatch is reported in errors but the file is kept so you can inspect it.

  • Error message sanitization: API error responses from 4xx errors are HTML-stripped and truncated before being surfaced; 5xx server error bodies are suppressed entirely.

  • Generated code safety: R code emitted by microdata_extract_to_code and nhgis_extract_to_code escapes all string values to prevent code injection from API-returned identifiers.

πŸ’» Development

Create a .env file in the project root (already gitignored):

IPUMS_API_KEY=your-key-here

Then run:

export $(cat .env | xargs) && npm run dev   # load key + run via tsx
npm run build                                # compile TypeScript β†’ dist/

Source layout:

src/
  index.ts          entry point
  client.ts         shared HTTP client (ipumsRequest, getApiKey)
  types.ts          Zod schemas
  data/
    usa-variables.ts  offline DB β€” 827 harmonized IPUMS USA variables
  tools/
    microdata.ts    microdata tools (search vars, list, get, create, wait, download)
    nhgis.ts        NHGIS tools (search datasets/tables/TSTs + list/get/create/extract)
    codegen.ts      reproducibility tools (extract→code for R/Python, citation lookup)

πŸ“„ License

MIT β€” Β© 2026 hesscl

Available Tools

23 tools
generate_ipums_citationA

Return a formatted citation template for an IPUMS dataset. Includes the standard author list, DOI, and a direct link to the official citation page where the current version number is always listed. The version number also appears on your extract page and in the README included with each download.

Inputs: collection (required).

microdata_create_extractA

Submit a new IPUMS microdata extract request. Specify the collection, samples, variables, and output format. Returns the new extract number and initial status. Once submitted, use microdata_extract_to_code to generate reproducible R or Python code for the extract.

Inputs: collection (required), samples, version, variables, +4 more.

microdata_download_extractA

Download completed IPUMS microdata extract files to a local directory. Verifies SHA-256 checksums. Returns local file paths and verification status.

Inputs: outputDir (required), collection (required), extractNumber (required), version, +1 more.

microdata_extract_to_codeA

Generate ready-to-run R (ipumsr) and/or Python (ipumspy) code to reproduce a microdata extract. Fetches the extract definition by number and emits code that re-submits the same request. Use this after microdata_create_extract to capture a reproducible workflow.

Inputs: collection (required), extractNumber (required), language.

microdata_get_extractA

Get the status and details of a specific IPUMS microdata extract by its number. Includes download links when the extract is complete.

Inputs: collection (required), extractNumber (required), version.

microdata_list_extractsA

List recent extracts for an IPUMS microdata collection (e.g. usa, cps, ipumsi). Returns extract numbers, status, and basic metadata.

Inputs: collection (required), version, pageSize, pageNumber.

microdata_list_samplesA

List available samples for an IPUMS microdata collection (e.g. all ACS, CPS, or Census years). Use this to find the correct sample ID before creating an extract.

Inputs: collection (required), version, pageSize, pageNumber.

microdata_search_variablesA

Search IPUMS USA harmonized variable mnemonics by name or label. Returns matching variables with their label, record type (H=household / P=person), thematic group, and available sample IDs. Use this to discover variable names before building an extract. The samples field lists representative IPUMS USA sample IDs (e.g. 'us2024a') where the variable is available β€” cross-reference with microdata_list_samples. Database covers 827 harmonized variables scraped from usa.ipums.org.

Inputs: type, group, limit, query, +1 more.

microdata_wait_for_extractA

Poll a microdata extract until it completes (or fails). Returns the final extract object including downloadLinks when done. Default timeout is 90 seconds β€” if the extract is still running at that point the tool returns early with the current status and a command to check back later, so the conversation is not blocked for large extracts.

Inputs: collection (required), extractNumber (required), version, timeoutSeconds, +1 more.

nhgis_create_extractA

Submit a new NHGIS extract request. Specify datasets (with data tables and geographic levels), time series tables, shapefiles, and output format. Returns the new extract number and initial status. Once submitted, use nhgis_extract_to_code to generate reproducible R or Python code for the extract.

Inputs: datasets, dataFormat, shapefiles, description, +2 more.

nhgis_extract_to_codeA

Generate ready-to-run R (ipumsr) and/or Python (ipumspy) code to reproduce an NHGIS extract. Fetches the extract definition by number and emits code that re-submits the same request. Use this after nhgis_create_extract to capture a reproducible workflow.

Inputs: extractNumber (required), language.

nhgis_get_datasetA

Get full details for a specific NHGIS dataset including available data tables, geographic levels, breakdown values, and years.

Inputs: dataset (required).

nhgis_get_data_tableA

Get detailed information about a specific NHGIS data table within a dataset, including all variable descriptions.

Inputs: dataset (required), dataTable (required).

nhgis_get_extractA

Get the status and details of a specific NHGIS extract by its number. Includes download links when the extract is complete.

Inputs: extractNumber (required).

nhgis_get_time_series_tableA

Get full details for a specific NHGIS time series table including available years, geographic levels, and variable descriptions.

Inputs: table (required).

nhgis_list_datasetsA

Browse available NHGIS datasets. Returns paginated list of dataset identifiers, names, years, and geographic levels available.

Inputs: pageSize, pageNumber.

nhgis_list_data_tablesA

List all available NHGIS data tables across all datasets. Returns paginated list of table identifiers, descriptions, and universe.

Inputs: pageSize, pageNumber.

nhgis_list_extractsB

List recent NHGIS extract requests. Returns extract numbers, status, and submission timestamps.

Inputs: pageSize, pageNumber.

nhgis_list_shapefilesA

List all available NHGIS shapefiles (boundary files). Returns identifiers for census geographies that can be included in extracts.

Inputs: pageSize, pageNumber.

nhgis_list_time_series_tablesA

List all available NHGIS time series tables. These span multiple census years with consistent geographic definitions.

Inputs: pageSize, pageNumber.

nhgis_search_datasetsA

Search NHGIS datasets by keyword. Fetches all available datasets and returns those whose name, group (e.g. '2020 Census'), or description match the keyword. Use this to find which datasets cover a topic or census year before drilling into specific data tables.

Inputs: keyword (required).

nhgis_search_data_tablesA

Search NHGIS data tables by keyword. If a dataset is specified, searches all tables within that dataset (comprehensive). Without a dataset, scans up to 2,500 tables across all datasets β€” recommend using nhgis_search_datasets first to find a relevant dataset, then scoping the search here. Matches against table description and universe.

Inputs: keyword (required), dataset.

nhgis_search_time_series_tablesA

Search NHGIS time series tables by keyword. Fetches all ~400 time series tables (which span multiple census years with consistent geographic definitions) and returns those matching the keyword in their name or description.

Inputs: keyword (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.9/5.0

    Scored across 23 tools

    Disambiguation4/5

    Most tools have clearly distinct resources and actions, with a consistent list/get/search/create pattern. A few pairs like search_ vs list_ for datasets/tables could be confused, but descriptions clarify the distinction. Overall, only one or two tools have overlapping boundaries.

    Naming Consistency5/5

    All tools follow a consistent prefix (nhgis_ or microdata_) with a verb_noun pattern (list_, get_, search_, create_, download_, wait_for_). Minor exceptions like extract_to_code and generate_ipums_citation are still clear and do not break the overall consistency.

    Tool Count4/5

    23 tools is on the heavier side but justified by covering two substantial domains (NHGIS and IPUMS microdata) with browse, search, extract creation, status, and code generation. The count is slightly over the ideal range but each tool has a clear purpose.

    Completeness3/5

    The microdata workflow is complete: search variables/list samples, create extract, wait, download, generate code. However, the NHGIS side lacks a dedicated download tool (nhgis_get_extract only provides links) and a wait/poll tool analogous to microdata_wait_for_extract, leaving a significant gap for agents that need to fetch NHGIS files programmatically.

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      MCP server to query U.S. Census Bureau data, variables, and geography through 7 tools supporting dataset discovery, variable search, geography resolution, and data queries with suppression code decoding.
      363 npm
      3
      Apache 2.0
    • A
      license
      A
      quality
      D
      maintenance
      An MCP server that puts open data on tap, enabling LLM clients to search, inspect, and query 30,000+ civic datasets from Socrata portals with built-in guardrails like row caps and actionable error messages.
      4
      1
      MIT
    • F
      license
      A
      quality
      D
      maintenance
      An MCP server that exposes the U.S. Energy Information Administration (EIA) Open Data API, enabling LLMs to browse and query energy data across 17 datasets with generic, composable tools.
      4
      -