About cdc-health-mcp-server
An MCP server for exploring CDC public health data, aimed at researchers and analysts. It combines searchable Socrata datasets and queryable CDC WONDER mortality statistics with schema and catalog tools.
Key features
- 01Search CDC dataset catalog
- 02Inspect dataset schemas and metadata
- 03Query datasets with SoQL
- 04Retrieve CDC WONDER mortality statistics
- 05Access catalog resources and analysis prompt
Use cases
- 01Find datasets on vaccination or surveillance
- 02Analyze mortality by year and demographic group
- 03Filter and aggregate CDC dataset records
MCP support
Exposes five tools, two resources, and an analysis prompt to MCP clients. It supports STDIO and Streamable HTTP, including a public hosted endpoint.
Configuration
Remote servers need no local install. Pick your client, then copy the config or install in one click.
https://cdc.caseyjhand.com/mcpclaude mcp add --transport http cyanheads-cdc-health https://cdc.caseyjhand.com/mcp{"mcpServers":{"cyanheads-cdc-health":{"type":"http","url":"https://cdc.caseyjhand.com/mcp"}}}Open in Cursor{"servers":{"cyanheads-cdc-health":{"type":"http","url":"https://cdc.caseyjhand.com/mcp"}}}Open in VS Code{"mcpServers":{"cyanheads-cdc-health":{"type":"http","url":"https://cdc.caseyjhand.com/mcp"}}}{"mcpServers":{"cyanheads-cdc-health":{"serverUrl":"https://cdc.caseyjhand.com/mcp"}}}Run commands in a terminal; paste JSON into the client’s MCP config and restart. Servers that need sign-in will prompt on first connect.
Tools
5| Parameter | Type | Required | Description |
|---|---|---|---|
| tags | array | No | Filter by domain tags (e.g., ["covid19", "surveillance"]). Tags widen the search instead of narrowing it — a dataset matches when it carries any one of them, so every tag added returns more results, and an unrecognized tag matches nothing and leaves the result set unchanged. Values match the catalog's own tag vocabulary, case-insensitively; call cdc_list_catalog_vocabulary for the values in use with their entry counts, or read the tags field on any result. To narrow, combine tags with query or category, which intersect with the tag set. |
| limit | integer | No | Results to return (default 10). One call returns at most 100: a larger limit, up to 10,000, is capped at 100, and the response says so and gives the offset to continue from while more datasets match. offset plus limit must not exceed 10000 once limit is capped. |
| order | enum | No | Result ordering. "dataset_id" (default) sorts deterministically by each dataset's unique catalog ID — required for stable offset pagination, since consecutive pages form a gap-free, duplicate-free traversal. "relevance" returns best-match ranking for keyword search but is not stably paginable across pages, so walking offsets can skip or repeat datasets.dataset_id | relevance |
| query | string | No | Full-text search across dataset names and descriptions (e.g., "diabetes mortality", "lead exposure children"). |
| domain | enum | No | CDC Socrata host to search. "data.cdc.gov" (default) and "chronicdata.cdc.gov" front the same catalog and return the same entries, so switching hosts neither widens nor narrows a search — chronic-disease and small-area collections such as PLACES, the Heart Disease & Stroke Atlas, and Environmental Public Health Tracking are found from either.data.cdc.gov | chronicdata.cdc.gov |
| offset | integer | No | Pagination offset for browsing beyond first page (max 9999). offset plus limit must not exceed 10000 once limit is capped; both CDC portals hold well under two thousand entries, so offsets near that ceiling page past the end of the catalog. |
| category | string | No | Filter by domain category (e.g., "NNDSS", "Vaccinations", "Behavioral Risk Factors"). Values come from the catalog's own vocabulary and are matched exactly, including case — "Vaccinations" matches 89 entries while "Vaccination" matches none. Call cdc_list_catalog_vocabulary for every value with its entry count rather than guessing at one. |
Details
From the project README@cyanheads/cdc-health-mcp-server
Search and query CDC public health data — mortality, vaccinations, surveillance, behavioral risk (Socrata SODA API) via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://cdc.caseyjhand.com/mcp
Overview
CDC public health data from the CDC Open Data portal and CDC WONDER. Search 1,000+ datasets on vaccination, surveillance, and behavioral risk, inspect their schemas and query them with SoQL, or pull national mortality statistics by year, age, sex, and race.
Tools
| Tool | Description |
|---|---|
cdc_discover_datasets | Search the CDC dataset catalog by keyword, category, or tag |
cdc_get_dataset_schema | Fetch column schema, row count, and metadata for a dataset |
cdc_list_catalog_vocabulary | List the catalog's category and tag values with their entry counts |
cdc_query_dataset | Execute SoQL queries — filter, aggregate, sort, full-text search, select fields |
cdc_query_wonder | Query CDC WONDER for national mortality, population, and death rates across five databases |
Resources
| Resource | Description |
|---|---|
cdc://datasets | Top 50 most-viewed catalog entries, for orientation |
cdc://datasets/{datasetId} | Dataset metadata and the first 100 columns for a specific dataset |
Both resources mirror data also reachable via cdc_discover_datasets and cdc_get_dataset_schema, for clients that surface resources but not tools.
Prompts
| Prompt | Description |
|---|---|
analyze_health_trend | Guided workflow for investigating a public health question across CDC data |
Capability reference
cdc_discover_datasets tool
- Filters:
query,category, andtags. Up to 100 results per page (default 10);offset + limitis capped at 10,000 (page_out_of_rangepast it).orderisdataset_id(default, stable paging) orrelevance - Results carry
assetType,columnCount(0 marks a chart, map, or other non-tabular asset), an 8-namecolumnSample, and a description cut to 300 characters, plustotalCountandappliedFilters - When a
categoryortagsvalue matches nothing, thenoticenames the closest catalog values with their entry counts, e.g.Vaccination→"Vaccinations" (89 datasets)
cdc_get_dataset_schema tool
- Takes a four-by-four
datasetId(e.g.bi63-dtpu).column_limit(default 100) andcolumn_offsetpage wide schemas, reportingtotalCount,truncated, andnextOffset - The dataset
descriptionis capped at 4,000 characters by default (descriptionTruncatedanddescriptionLengthare set when cut);full_description: truereturns the whole text - Columns carry
fieldName,dataType, anddescription.rowCountSourcesays whetherrowCountisliveor Socrata'scachedfigure. A non-tabular asset fails withnot_queryable
cdc_list_catalog_vocabulary tool
- Every category comes back whole; the ~1,600 tags are ranked by entry count and paged with
tag_limit(default 50) andtag_offset.filter(3+ characters) narrows both by word match, not fuzzy matching - Each value carries
datasetCount; the response also reportsvocabularySize,categoryCount,tagCount,truncated,nextOffset, andtruncationCeiling(an upper bound on the counts of the tags after the page)
cdc_query_dataset tool
- Clauses:
select,where,group,having,order, and full-textsearch. Up to 5,000 rows per call (default 100),offsetup to 1,000,000 - For stable offset paging, set an
orderending in a unique tie-breaker (:idon ungrouped rows, the grouped columns on a grouped query); the notice says so when it is missing effectiveQueryechoes the SoQL clauses as sent;truncatedandnextOffsetreport paging, with nototalCount. A chart or map ID fails withnot_queryable
cdc_query_wonder tool
database | CDC database | Years | Race groups | mcd_icd10 |
|---|---|---|---|---|
underlying_1999_2020 (default) | D76 — Underlying Cause of Death | 1999–2020 | 4 bridged | — |
provisional | D176 — Provisional Mortality Statistics | 2018 → current year | 6 single-race | yes |
underlying_2018_2024 | D158 — Underlying Cause of Death, Single Race | 2018–2024 | 6 single-race | — |
multiple_1999_2020 | D77 — Multiple Cause of Death | 1999–2020 | 4 bridged | yes |
multiple_2018_2024 | D157 — Multiple Cause of Death, Single Race | 2018–2024 | 6 single-race | yes |
group_bytakes 1–4 ofyear,age_group,sex,race(national only).cause_icd10andmcd_icd10take an ICD-10 code, a range (X40-X49), or a list of up to 50.age_groupstakesNS(age not stated) alone or with all eleven known groups.limitandoffset(max 10,000) page the table- Rows carry
deaths,population,crude_rate, andage_adjusted_ratewhere age can be standardized. Cells CDC returns asSuppressed,Unreliable, orNot Applicablereadnulland are listed incellNotes;messagesreports rows CDC hid, andtotalCountis exact - CDC rejects requests less than 15 seconds apart, so calls are queued one at a time and spaced 16 seconds apart
cdc://datasets resource
- Top 50 catalog entries by popularity, each with
id,name,assetType,category,columnCount, andupdatedAt, plus the catalogtotalCount columnCount: 0marks a non-tabular entry;cdc_discover_datasetssearches and pages the full catalog
cdc://datasets/{datasetId} resource
- Dataset metadata plus the first 100 columns as
application/json, withcolumnCountandtruncated; the description is capped at 4,000 characters - For more columns or the full description, use
cdc_get_dataset_schemawithcolumn_offsetorfull_description: true
analyze_health_trend prompt
- Arguments:
topicrequired;timeRangeandgeographyoptional - Returns one user message that routes the question to
cdc_query_wonder(national mortality) or the Socrata tools (everything else), then walks discover → inspect → baseline → compare → synthesize
Features
Built 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.
CDC-specific:
- The Socrata SODA API v2.1 over the CDC Open Data portal (~1,080 datasets); no auth required, and an optional app token raises rate limits
- CDC WONDER mortality data from five databases covering 1999 through the current year, reached over its XML request API
- The three Socrata tools take a
domaininput, limited todata.cdc.gov(default) andchronicdata.cdc.gov; both serve the same catalog categoryandtagsmust match the catalog's own values (categories case-sensitively, tags case-insensitively);cdc_list_catalog_vocabularylists them- Socrata returns every value as a string, and a column like
yearis numeric in some datasets and text in others. Match WHERE literals to the column'sdataType: numbers bare, strings single-quoted
Agent-friendly output:
- Paging is reported explicitly:
totalCount,truncated,nextOffset, andshown/cap. Both query tools cap each page at 200,000 characters, so a wide page can end short oflimit - Typed errors with a recovery hint on every declared reason, such as
not_queryable,page_out_of_range, andinvalid_query - Upstream gaps stay visible: WONDER status tokens in
cellNotes, hidden-row notices inmessages, androwCountSourceon schema row counts effectiveQueryechoes the SoQL clauses or a WONDER query summary, so a result can be reproduced
Getting started
Public Hosted Instance
A public instance is available at https://cdc.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"cdc-health-mcp-server": {
"type": "streamable-http",
"url": "https://cdc.caseyjhand.com/mcp"
}
}
}
Self-Hosted / Local
Add the following to your MCP client configuration file.
{
"mcpServers": {
"cdc-health-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/cdc-health-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"cdc-health-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/cdc-health-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"cdc-health-mcp-server": {
"type": "stdio",
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/cdc-health-mcp-server:latest"]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
Prerequisites
- Bun v1.4.0 or higher (or Node.js v24+).
- Optional: a Socrata app token for higher rate limits.
Installation
- Clone the repository:
git clone https://github.com/cyanheads/cdc-health-mcp-server.git
- Navigate into the directory:
cd cdc-health-mcp-server
- Install dependencies:
bun install
- Configure environment:
cp .env.example .env
# optionally set CDC_APP_TOKEN
Configuration
| Variable | Description | Default |
|---|---|---|
CDC_APP_TOKEN | Socrata app token; raises rate limits. | none |
CDC_BASE_URL | SODA host for requests with no domain (the cdc://datasets/{datasetId} resource). | https://data.cdc.gov |
CDC_CATALOG_URL | Socrata Discovery API base URL. | https://api.us.socrata.com/api/catalog/v1 |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_SESSION_MODE | HTTP session mode: stateless, stateful, or auto. | stateless |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
OTEL_ENABLED | Enable OpenTelemetry. | false |
See .env.example for the full list of optional overrides.
Running the server
Local development
-
Build and run the production version:
# One-time build bun run rebuild # Run the built server bun run start:http # or bun run start:stdio -
Run checks and tests:
bun run devcheck # Lints, formats, type-checks, and more bun run test # Runs the test suite
Docker
docker build -t cdc-health-mcp-server .
docker run --rm -p 3010:3010 cdc-health-mcp-server
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/cdc-health-mcp-server. OpenTelemetry peer dependencies are installed by default; build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools/resources/prompts and inits services. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). Four Socrata tools and one WONDER tool. |
src/mcp-server/resources | Resource definitions. Catalog overview and dataset detail. |
src/mcp-server/prompts | Prompt definitions. Health trend analysis workflow. |
src/services/socrata | Socrata service — catalog search, vocabularies, metadata, SoQL queries. |
src/services/wonder | CDC WONDER service — XML request builder, response parser, request spacing. |
src/utils | Shared helpers — HTML-to-text, table-cell escaping, vocabulary matching, response budget. |
tests/ | Unit and integration tests mirroring src/. |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catchin tool logic - Use
ctx.logfor logging,ctx.statefor storage - Register new tools and resources in the
createApp()arrays insrc/index.ts - Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
License
Apache-2.0 — see LICENSE for details.
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.