cdc-health-mcp-server
An MCP server that allows agents to search, query, and analyze CDC public health datasets including mortality, vaccinations, and behavioral risk data via the Socrata SODA API and CDC WONDER.
Endpoint and access
No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.
https://cdc.caseyjhand.com/mcpWhat its tools do
5 tools, grouped by the hints the server advertises. Hints are self-declared, not verified behavior.
Checks and signals
Each line is separate evidence, not a trust score.
- Passed Endpoint answered the live check ·
- Availability 1 of 1 checks passed in 30 days
What you can do with it
- Search the CDC dataset catalog by keyword or category
- Fetch column schemas and metadata for specific datasets
- Execute SoQL queries to filter and aggregate CDC data
- Query CDC WONDER for national mortality and death rates
Source and maintenance
- Maintenance checked
Protocol and capabilities
- Server
- cdc-health-mcp-server 0.11.0
- MCP protocol negotiated in check
- 2025-11-25
- Tools
- 5 advertised
- Resources
- 4 advertised
- Resource templates
- 2 advertised
- Prompts
- 1 advertised
{
"tools": {
"listChanged": true
},
"logging": {},
"prompts": {
"listChanged": true
},
"resources": {
"subscribe": true,
"listChanged": true
},
"extensions": {
"io.modelcontextprotocol/skills": {
"directoryRead": true
}
}
}Server-provided instructions
Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.
Tools
5 advertised
cdc_discover_datasets — Cdc Discover Datasets
Search the CDC dataset catalog by keyword, category, tag, or asset type. Returns IDs, names, truncated descriptions, asset types, column counts, update timestamps, and portal links. The catalog also holds charts, maps, stories, files, and links; an entry whose columnCount is 0 is one of those and yields no data from the other tools — reach a file or link through its permalink or accessPoints, and a chart or map through the dataset its parentIds name. Use cdc_get_dataset_schema for the full column list of a chosen dataset.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"tags": {
"type": "array",
"items": {
"type": "string",
"description": "Tag value"
},
"description": "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": {
"type": "integer",
"default": 10,
"maximum": 10000,
"minimum": 1,
"description": "Results to return (default 10, max 10,000). offset plus limit must not exceed 10000."
},
"order": {
"enum": [
"dataset_id",
"relevance"
],
"type": "string",
"description": "Result ordering. Defaults to \"relevance\" when query is set and to \"dataset_id\" otherwise, at every offset; an explicit value always wins. \"relevance\" ranks the best matches for the query first, but its order varies between calls, so paging a relevance search by offset can repeat or skip entries. \"dataset_id\" sorts by each entry's catalog ID — a stable order whose offset pages walk every match exactly once."
},
"query": {
"type": "string",
"description": "Full-text search across dataset names and descriptions (e.g., \"diabetes mortality\", \"lead exposure children\")."
},
"domain": {
"enum": [
"data.cdc.gov",
"chronicdata.cdc.gov"
],
"type": "string",
"default": "data.cdc.gov",
"description": "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."
},
"offset": {
"type": "integer",
"default": 0,
"maximum": 9999,
"minimum": 0,
"description": "Pagination offset for browsing beyond first page (max 9999). offset plus limit must not exceed 10000; both CDC portals hold well under two thousand entries, so offsets near that ceiling page past the end of the catalog."
},
"category": {
"type": "string",
"description": "Filter by domain category (e.g., \"NNDSS\", \"Vaccinations\", \"Behavioral Risk Factors\"). Values come from the catalog's own vocabulary and are matched exactly, ignoring case — \"Vaccinations\" and \"vaccinations\" each match 89 entries while \"Vaccination\" matches none. Call cdc_list_catalog_vocabulary for every value with its entry count rather than guessing at one."
},
"asset_types": {
"type": "array",
"items": {
"enum": [
"dataset",
"filter",
"chart",
"map",
"story",
"file",
"href"
],
"type": "string"
},
"description": "Limit the search to these catalog asset types, spelled as assetType reports them. An entry matches when it is any one of them, and totalCount counts only those; [\"dataset\", \"filter\"] returns just the entries with rows to query. Omitted or [] searches every type."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"datasets",
"totalCount",
"appliedFilters"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"rate_limited",
"dataset_not_found",
"access_denied",
"upstream_error",
"page_out_of_range",
"invalid_query"
],
"description": "Machine-readable failure mode. Declared by this tool: `rate_limited`: Socrata API returns 429 Too Many Requests. `dataset_not_found`: Socrata returned 404 for the catalog endpoint itself — the Discovery API address is wrong or the service moved. `access_denied`: Socrata returned 403 — the catalog refused this request rather than failing to serve it. `upstream_error`: Socrata catalog API returned a 5xx server error, could not be reached, did not finish answering within 20 seconds, or answered with a body that is not the JSON it serves. `page_out_of_range`: offset is above 0 and offset plus limit exceeds 10000, which Socrata's catalog rejects outright. `invalid_query`: Catalog API returned 400 — typically a malformed query or invalid filter value. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"notice": {
"type": "string",
"description": "Guidance when the page came back empty — the catalog values closest to a category or tag filter that matched nothing, each with its entry count; how to broaden a search when no value is close; or the size of the result set when the offset ran past its end. On a relevance-ordered page with matches remaining past it, also that relevance order varies between calls, so offset paging can repeat or skip entries, and that order: \"dataset_id\" walks every match."
},
"datasets": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"name"
],
"properties": {
"id": {
"type": "string",
"description": "Four-by-four dataset identifier (e.g., \"bi63-dtpu\")."
},
"name": {
"type": "string",
"description": "Dataset display name from the catalog (e.g., \"Provisional COVID-19 Deaths by Sex and Age\")."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Domain tags when provided."
},
"category": {
"type": "string",
"description": "Domain category when provided."
},
"assetType": {
"type": "string",
"description": "Catalog asset type as Socrata reports it — \"dataset\", \"filter\", \"chart\", \"map\", \"story\", \"file\", or \"href\" — and the value asset_types takes. Descriptive only: \"filter\" entries carry real columns and query normally, while \"chart\" and \"map\" entries do not. Read columnCount, not this field, to decide whether an entry is queryable."
},
"pageViews": {
"type": "number",
"description": "Total page views when provided."
},
"parentIds": {
"type": "array",
"items": {
"type": "string"
},
"description": "Four-by-four IDs of the datasets this entry is built on — every chart, map, and filter names one, and a story can name several. A chart or map has no rows of its own; cdc_get_dataset_schema and cdc_query_dataset read its parent. Absent on an entry built on no other dataset."
},
"permalink": {
"type": "string",
"description": "The entry's page on the CDC portal. For a link (href) entry it is a catalog landing page; the destination itself is in accessPoints."
},
"updatedAt": {
"type": "string",
"description": "Last data update timestamp when provided."
},
"columnCount": {
"type": "number",
"description": "Number of columns in the dataset when reported by the catalog. A count of 0 means the entry is not tabular — cdc_get_dataset_schema and cdc_query_dataset return no usable data for it."
},
"description": {
"type": "string",
"description": "Dataset description when provided by the catalog, as plain text — markup stripped and entity references decoded — then truncated to 300 characters, so the limit bounds visible text. Fetch the full text via cdc_get_dataset_schema with full_description: true; without it, that tool caps the description at 4,000 characters."
},
"accessPoints": {
"type": "array",
"items": {
"type": "object",
"required": [
"label",
"url"
],
"properties": {
"url": {
"type": "string",
"description": "The URL."
},
"label": {
"type": "string",
"description": "Label the publisher gave the URL — free text, e.g. \"html\", \"map-server\"."
}
},
"description": "One publisher-labelled URL.",
"additionalProperties": false
},
"description": "The URLs a link (href) entry points to, each with its publisher's label, in catalog order — the only place a link entry's destination appears. Absent when the catalog lists none."
},
"columnSample": {
"type": "array",
"items": {
"type": "string"
},
"description": "First 8 column field names as a preview. Call cdc_get_dataset_schema for the full column list with data types."
}
},
"description": "A single dataset catalog entry.",
"additionalProperties": false
},
"description": "Matching datasets."
},
"totalCount": {
"type": "number",
"description": "Total matching datasets in the catalog (for pagination)."
},
"appliedFilters": {
"type": "object",
"properties": {
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "Tag filters used — a dataset matched when it carried any one of them."
},
"query": {
"type": "string",
"description": "Search query used."
},
"category": {
"type": "string",
"description": "Category filter used."
},
"assetTypes": {
"type": "array",
"items": {
"enum": [
"dataset",
"filter",
"chart",
"map",
"story",
"file",
"href"
],
"type": "string"
},
"description": "Asset types the search was limited to — an entry matched when it was any one of them."
}
},
"description": "Filters applied to this query; absent fields indicate no filter on that dimension. Query, category, tags, and asset types intersect with each other, but multiple tags union, as do multiple asset types.",
"additionalProperties": false
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}cdc_get_dataset_schema — Cdc Get Dataset Schema
Fetch the column schema for a CDC dataset — names, data types, descriptions, row count, last-updated timestamp, and, for a filter, the dataset it is a view of — with the period and area it covers, how often it is updated, its suggested citation, license, and attribution wherever the publisher recorded them. A chart or map has no schema of its own; the failure names the dataset it is built on. Returns the first 100 columns by default; wide datasets continue via column_offset. The dataset description is capped at 4,000 characters by default; pass full_description: true for the whole text. Get dataset IDs from cdc_discover_datasets.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"datasetId"
],
"properties": {
"domain": {
"enum": [
"data.cdc.gov",
"chronicdata.cdc.gov"
],
"type": "string",
"default": "data.cdc.gov",
"description": "CDC Socrata host to fetch the dataset from. \"data.cdc.gov\" (default) and \"chronicdata.cdc.gov\" front the same catalog, so a four-by-four ID resolves on either and the default works whichever host the dataset was found on."
},
"datasetId": {
"type": "string",
"pattern": "^[a-z0-9]{4}-[a-z0-9]{4}$",
"description": "Four-by-four dataset identifier (e.g., \"bi63-dtpu\"). Obtain from cdc_discover_datasets."
},
"column_limit": {
"type": "integer",
"default": 100,
"maximum": 9007199254740991,
"minimum": 1,
"description": "Columns to return in this call (default 100). Every dataset under the default arrives whole; past it the response reports totalCount and a nextOffset to pass back as column_offset. Raise this to pull a wide schema in one call — a value at or above the column count returns every column."
},
"column_offset": {
"type": "integer",
"default": 0,
"maximum": 10000,
"minimum": 0,
"description": "Index of the first column to return, for continuing past a previous call (default 0). Columns keep the order the dataset declares, so column_offset plus column_limit walks the schema without gaps or repeats. An offset at or past the column count returns an empty window rather than an error."
},
"full_description": {
"type": "boolean",
"default": false,
"description": "Return the dataset description whole (default false). By default a description longer than 4,000 characters is cut there, and the response sets descriptionTruncated and descriptionLength. Column descriptions are never shortened either way."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"name",
"columns",
"totalCount"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"cap": {
"type": "number",
"description": "The column_limit that bounded this response."
},
"name": {
"type": "string",
"description": "Dataset display name from the catalog (e.g., \"Provisional COVID-19 Deaths by Sex and Age\")."
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"dataset_not_found",
"not_queryable",
"access_denied",
"invalid_query",
"rate_limited",
"upstream_error"
],
"description": "Machine-readable failure mode. Declared by this tool: `dataset_not_found`: Dataset ID does not exist or has been retired. `not_queryable`: The ID names a catalog asset with no columns — a chart, map, story, file, or external link rather than a tabular dataset. `access_denied`: Socrata returned 403 — the asset is not readable through this endpoint or access is restricted. `invalid_query`: Socrata rejected the metadata request with a 400. `rate_limited`: Socrata API returns 429 Too Many Requests. `upstream_error`: Socrata metadata API returned a 5xx server error, could not be reached, did not finish answering within 20 seconds, or answered with a body that is not the JSON it serves. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"shown": {
"type": "number",
"description": "Number of columns returned in this response."
},
"notice": {
"type": "string",
"description": "Guidance when the response is a subset of the schema — which columns it covers, how to reach the rest, or that column_offset ran past the end."
},
"columns": {
"type": "array",
"items": {
"type": "object",
"required": [
"fieldName",
"dataType"
],
"properties": {
"dataType": {
"type": "string",
"description": "Column data type (text, number, calendar_date, etc.)."
},
"fieldName": {
"type": "string",
"description": "Column field name (use in SoQL queries)."
},
"description": {
"type": "string",
"description": "Column description when provided."
}
},
"description": "A single column in the dataset schema.",
"additionalProperties": false
},
"description": "The requested window of dataset columns, with full types and descriptions. Bounded by column_limit/column_offset; the enrichment fields say how the window sits in the whole schema."
},
"license": {
"type": "string",
"description": "Name of the license the dataset is published under — publisher-written text in no fixed format (\"Public Domain U.S. Government\", \"Open Data Commons Attribution License\"). Absent when the publisher named none."
},
"citation": {
"type": "string",
"description": "How the publisher asks the dataset to be cited — publisher-written text in no fixed format, placeholders such as \"[Last accessed date]\" included. Absent when the publisher recorded none."
},
"parentId": {
"type": "string",
"description": "Four-by-four ID of the dataset this asset is a view of — set on a filter, naming the dataset it was derived from. Absent on a dataset in its own right."
},
"rowCount": {
"type": "number",
"description": "Total rows in the dataset; omitted when neither a live nor a cached figure is available. Read rowCountSource before sizing a pagination walk against it — the cached figure can understate an actively-updated dataset by a third or more."
},
"truncated": {
"type": "boolean",
"description": "True when the returned columns are a subset of the schema. Absent means every column of the dataset is in this response."
},
"updatedAt": {
"type": "string",
"description": "Last data update timestamp when provided."
},
"nextOffset": {
"type": "number",
"description": "Value to pass as column_offset on the next call to continue after the last column returned. Present only when columns remain beyond this window."
},
"totalCount": {
"type": "number",
"description": "Total columns in the dataset schema, before column_limit/column_offset."
},
"attribution": {
"type": "string",
"description": "The office the publisher credits for the dataset — publisher-written text in no fixed format (\"National Center for Health Statistics\"). Absent when the publisher recorded none."
},
"description": {
"type": "string",
"description": "Dataset description when provided, as plain text — markup stripped and entity references decoded. Capped at 4,000 characters by default and ending in \"…\" when cut, with descriptionTruncated and descriptionLength set; pass full_description: true for the whole text. Column descriptions are never shortened."
},
"rowCountSource": {
"enum": [
"live",
"cached"
],
"type": "string",
"description": "Provenance of rowCount. \"live\" is a count(*) run against the dataset for this call. \"cached\" is the figure Socrata stored when it last built its column cache, returned when the count request did not succeed; Socrata does not refresh it as rows land. Absent exactly when rowCount is absent."
},
"updateFrequency": {
"type": "string",
"description": "How often the publisher updates the dataset — publisher-written text in no fixed format (\"R/P1M\", \"Weekly\", \"irregular\", \"No longer updated (dataset archived)\"). Absent when the publisher recorded none; updatedAt is when the data last changed, which alone does not tell a monthly series from a closed one."
},
"temporalCoverage": {
"type": "string",
"description": "Period the data covers — publisher-written text in no fixed format, relayed as written (\"2015-01-01/2026-05-31\", \"1999/2018\", \"2019/present\", \"N/A\"). Absent when the publisher recorded none."
},
"descriptionLength": {
"type": "number",
"description": "Character count of the whole description, present only when description was cut. Call again with full_description: true to read all of it."
},
"geographicCoverage": {
"type": "string",
"description": "Area the data covers — publisher-written text in no fixed format (\"United States\", \"50 states and District of Columbia\"). Absent when the publisher recorded none."
},
"descriptionTruncated": {
"type": "boolean",
"description": "True when description was cut at the 4,000-character cap. Absent means description is the whole text."
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}cdc_list_catalog_vocabulary — List CDC Catalog Vocabulary
List the controlled vocabularies cdc_discover_datasets' category and tags filters are matched against — every domain category and domain tag the CDC catalog publishes, each with the number of entries carrying it. Call it before filtering a search: a value the catalog does not carry matches nothing and returns an empty page, which is indistinguishable from a real value with no results. All categories come back whole; the tag vocabulary runs to roughly 1,600 values, so tags are ranked by entry count and returned one page at a time via tag_limit and tag_offset. Pass filter to narrow both vocabularies to the values whose words contain its words, or that appear whole inside it; a filter under three letters or digits matches whole words only.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"domain": {
"enum": [
"data.cdc.gov",
"chronicdata.cdc.gov"
],
"type": "string",
"default": "data.cdc.gov",
"description": "CDC Socrata host to read the vocabulary from. \"data.cdc.gov\" (default) and \"chronicdata.cdc.gov\" front the same catalog and publish the same vocabulary, so this selects which host answers, never which values exist."
},
"filter": {
"type": "string",
"maxLength": 500,
"description": "Narrow both vocabularies to the values related to this text (at most 500 characters). A value matches when every word of the filter appears inside one of its words (\"vaccin\" reaches \"Vaccinations\" and \"covid-19 vaccination\"), or when the whole value appears in the filter. Matching is not fuzzy — a misspelling returns nothing rather than a guess. A word under three letters or digits matches only an equal word, so a short filter reaches the values carrying it as a whole word (\"tb\" reaches \"tuberculosis (tb)\", never \"outbreak\"); a short filter that reaches none, or one with no letters or digits, is ignored and both vocabularies come back unfiltered."
},
"tag_limit": {
"type": "integer",
"default": 50,
"maximum": 9007199254740991,
"minimum": 1,
"description": "Tags to return in this call (default 50). Tags are ranked by entry count, so the default page is the most-used end of the vocabulary; the response reports how many matched and a nextOffset while more remain. A tag_limit at or above the number of matching tags returns all of them in one call. Categories are never paged — every one arrives whole."
},
"tag_offset": {
"type": "integer",
"default": 0,
"maximum": 10000,
"minimum": 0,
"description": "Index of the first tag to return, for continuing past a previous call (default 0). Ranking is stable, so tag_offset plus tag_limit walks the vocabulary without gaps or repeats. An offset at or past the number of matching tags returns an empty tag list rather than an error."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"domain",
"categories",
"tags",
"vocabularySize",
"categoryCount",
"tagCount"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"cap": {
"type": "number",
"description": "The tag_limit that bounded this response."
},
"tags": {
"type": "array",
"items": {
"type": "object",
"required": [
"value",
"datasetCount"
],
"properties": {
"value": {
"type": "string",
"description": "The value as the catalog spells it — pass it verbatim to cdc_discover_datasets. The catalog matches categories and tags against these values ignoring case, but otherwise exactly."
},
"datasetCount": {
"type": "number",
"description": "Catalog entries carrying this value. Charts, maps, stories, files, and links are counted alongside datasets, so this is an upper bound on the queryable entries a filter returns."
}
},
"description": "One vocabulary value with its catalog entry count.",
"additionalProperties": false
},
"description": "The requested window of domain tags, ranked by entry count. Pass values to cdc_discover_datasets' tags input; tags union there, so each one added widens the result set."
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"rate_limited",
"dataset_not_found",
"access_denied",
"upstream_error",
"invalid_query"
],
"description": "Machine-readable failure mode. Declared by this tool: `rate_limited`: Socrata API returns 429 Too Many Requests. `dataset_not_found`: Socrata returned 404 for the vocabulary endpoint itself — the Discovery API address is wrong or the service moved. `access_denied`: Socrata returned 403 — the catalog refused this request rather than failing to serve it. `upstream_error`: Socrata catalog API returned a 5xx server error, could not be reached, did not finish answering within 20 seconds, or answered with a body that is not the JSON it serves. `invalid_query`: Socrata rejected the vocabulary request with a 400. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"shown": {
"type": "number",
"description": "Number of tags returned in this response."
},
"domain": {
"enum": [
"data.cdc.gov",
"chronicdata.cdc.gov"
],
"type": "string",
"description": "CDC Socrata host this vocabulary was read from."
},
"notice": {
"type": "string",
"description": "Guidance when the response is a subset of the vocabulary, when a filter under three letters or digits was ignored because no value carries it as a whole word, when the filter matched nothing, or when tag_offset ran past the end of the matches."
},
"tagCount": {
"type": "number",
"description": "Tags matching the filter, before tag_limit and tag_offset."
},
"truncated": {
"type": "boolean",
"description": "True when the returned tags are a subset of the matching ones. Absent means every matching tag is in this response."
},
"categories": {
"type": "array",
"items": {
"type": "object",
"required": [
"value",
"datasetCount"
],
"properties": {
"value": {
"type": "string",
"description": "The value as the catalog spells it — pass it verbatim to cdc_discover_datasets. The catalog matches categories and tags against these values ignoring case, but otherwise exactly."
},
"datasetCount": {
"type": "number",
"description": "Catalog entries carrying this value. Charts, maps, stories, files, and links are counted alongside datasets, so this is an upper bound on the queryable entries a filter returns."
}
},
"description": "One vocabulary value with its catalog entry count.",
"additionalProperties": false
},
"description": "Every domain category matching the filter, ranked by entry count. Pass a value to cdc_discover_datasets' category input exactly as spelled here."
},
"nextOffset": {
"type": "number",
"description": "Value to pass as tag_offset on the next call to continue after the last tag returned. Present only while matching tags remain."
},
"categoryCount": {
"type": "number",
"description": "Categories matching the filter. Every one of them is in this response."
},
"vocabularySize": {
"type": "object",
"required": [
"categories",
"tags"
],
"properties": {
"tags": {
"type": "number",
"description": "Domain tags published for this host."
},
"categories": {
"type": "number",
"description": "Domain categories published for this host."
}
},
"description": "Size of each full vocabulary on this host, before any filter was applied.",
"additionalProperties": false
},
"truncationCeiling": {
"type": "number",
"description": "Upper bound on the entry count of every tag after this page — the tags from nextOffset on. Tags are ranked by entry count, so none of those is carried by more entries than the last tag shown. It does not bound the tags before tag_offset: those rank at or above the first tag shown."
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}cdc_query_dataset — Cdc Query Dataset
Execute a SoQL query against any CDC dataset. Supports filtering, aggregation, sorting, full-text search, and field selection. Use cdc_discover_datasets to find dataset IDs and cdc_get_dataset_schema to inspect columns before querying.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"datasetId"
],
"properties": {
"group": {
"type": "string",
"description": "SoQL GROUP BY clause. Requires aggregate functions in select."
},
"limit": {
"type": "integer",
"default": 100,
"maximum": 1000000,
"minimum": 1,
"description": "Rows to return (default 100). One call returns at most 1,000 rows: a larger limit, up to 1,000,000, is capped at 1,000, and the response says so and gives a nextOffset when more rows remain. Up to that cap, a page comes back shorter than limit only at the end of the result set."
},
"order": {
"type": "string",
"description": "SoQL ORDER BY clause. Field name with optional ASC/DESC: \"total_deaths DESC\". Set one from the first page whenever paging with offset, and keep it for the whole walk: SODA does not order results implicitly, so consecutive offsets without a deterministic order, or under a changed one, can skip or repeat rows. End it with a tie-breaker unique to each row: on ungrouped rows, the system field `:id`, present on every dataset (order=\"year DESC, :id\"); on a grouped query, the grouped columns (order=\"total DESC, state\"); on a distinct select, the selected columns. Socrata rejects `:id` once rows are grouped."
},
"where": {
"type": "string",
"description": "SoQL WHERE clause. Strings must be single-quoted: \"state='California' AND year=2020\". If a column name matches a SoQL keyword (group, select, where, order, limit, offset, having, search), wrap it in backticks: \"`group`='By Year'\"."
},
"domain": {
"enum": [
"data.cdc.gov",
"chronicdata.cdc.gov"
],
"type": "string",
"default": "data.cdc.gov",
"description": "CDC Socrata host to query. \"data.cdc.gov\" (default) and \"chronicdata.cdc.gov\" front the same catalog, so a four-by-four ID returns the same rows from either and the default works whichever host the dataset was found on."
},
"having": {
"type": "string",
"description": "SoQL HAVING clause. Filters aggregated results."
},
"offset": {
"type": "integer",
"default": 0,
"maximum": 1000000,
"minimum": 0,
"description": "Row offset for pagination (max 1,000,000). Pair with a deterministic order clause — an offset walk over unordered results can skip or repeat rows."
},
"search": {
"type": "string",
"description": "Full-text search across all text columns. For precise filtering use the where parameter instead."
},
"select": {
"type": "string",
"description": "SoQL SELECT clause — column names, aliases, or aggregates: \"state, sum(deaths) as total_deaths\". Omit for all columns. To enumerate distinct values of a column, set select to \"{column}, count(*) as count\" with group=\"{column}\" and order=\"count DESC, {column}\"."
},
"datasetId": {
"type": "string",
"pattern": "^[a-z0-9]{4}-[a-z0-9]{4}$",
"description": "Four-by-four dataset identifier (e.g., \"bi63-dtpu\"). Obtain from cdc_discover_datasets."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"rows",
"rowCount",
"effectiveQuery"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"cap": {
"type": "number",
"description": "The row limit that bounded this response: the requested limit, or 1,000 when a larger one was capped."
},
"rows": {
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
},
"description": "Result rows with requested fields. Most values are strings (including numbers/dates); geo columns return GeoJSON objects."
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"dataset_not_found",
"no_such_column",
"type_mismatch",
"invalid_query",
"not_queryable",
"access_denied",
"rate_limited",
"upstream_error"
],
"description": "Machine-readable failure mode. Declared by this tool: `dataset_not_found`: Dataset ID does not exist or has been retired. `no_such_column`: WHERE/SELECT/GROUP/ORDER references a column that does not exist on this dataset. `type_mismatch`: Filter value type does not match the column data type (e.g., quoting a number). `invalid_query`: Socrata rejected the SoQL query for other syntax or semantic reasons. `not_queryable`: The ID names a non-tabular asset: the data endpoint answered with rows carrying no fields, and the asset reports no columns. `access_denied`: Socrata returned 403 — typically an ID naming a story, file, or external link rather than a tabular dataset. `rate_limited`: Socrata API returns 429 Too Many Requests. `upstream_error`: Socrata data API returned a 5xx server error, could not be reached, did not finish answering within 20 seconds, or answered with a body that is not the JSON it serves. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"shown": {
"type": "number",
"description": "Number of rows returned in this response."
},
"notice": {
"type": "string",
"description": "Guidance when no rows matched, when offset ran past the end of the result set (or the offset-0 check that tells the two apart could not complete), when every matched row came back with no fields for the projection, or when further rows remain (naming a limit above 1,000 that was capped) — how to verify filters, lower offset, pick columns the matched rows populate, resume paging under the same order or restart it from offset 0 under one that makes the walk gap-free, or broaden the query."
},
"rowCount": {
"type": "number",
"description": "Number of rows returned in this response."
},
"truncated": {
"type": "boolean",
"description": "True when rows exist beyond the ones returned, established by fetching one row more than the limit rather than inferred from the row count. Absent means this response is the complete remainder of the result set."
},
"nextOffset": {
"type": "number",
"description": "Offset to pass on the next call, under the same order, to resume immediately after the last row returned. Present only when further rows exist and the resume point is within the offset ceiling. The walk is gap-free only when that order ends in a tie-breaker unique to each row; when it does not, the notice says to restart from offset 0 with one."
},
"effectiveQuery": {
"type": "string",
"description": "The SoQL clauses sent to Socrata, as `$clause=value` pairs joined by \"&\". Values read exactly as they were supplied — not URL-encoded — so a clause can be copied back into the matching parameter of another call."
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}cdc_query_wonder — Cdc Query Wonder
Query CDC WONDER for national US mortality statistics — deaths, population, and crude/age-adjusted death rates — across its five mortality databases, selected with the database input: final underlying-cause data for 1999–2020 (the default) or 2018–2024, provisional data running from 2018 through the current year (its latest year partial, with a notice saying so), and two multiple-cause databases covering the same two eras. Break results out by year, month, age group, sex, race, Hispanic origin, or underlying cause of death — ICD-10 chapter or sub-chapter, CDC's 113-cause list, its 15 leading causes, or injury intent — and filter by ICD-10 cause of death, sex, age group, or year range; on a multiple-cause database, mcd_icd10 additionally matches a cause listed anywhere on the death certificate rather than only the one certified as underlying. Each database holds a different span of years (1999–2026 across all of them) and a request whose year_range falls outside the selected one's span is rejected with that span named. WONDER is a separate CDC system from the Socrata datasets the other cdc_* tools query. Data is national only — sub-national (state/county) breakdowns are not available through the API (CDC vital-statistics policy). Chapter and sub-chapter rows carry their ICD-10 code range as cause_code, which cause_icd10 accepts as it stands; 113-cause-list rows carry cause_level, their depth in that nested list, whose deeper causes are already counted in the cause containing them; leading_causes ranks the 15 leading causes by deaths and combines with no other grouping, while the filters still apply; month rows carry deaths only — CDC publishes no monthly population, so population and both rates come back "Not Applicable". The values each grouping dimension can take multiply to bound the table — year beside month, or icd_chapter beside icd_subchapter, adds nothing, since the finer level already enumerates the coarser — and a grouping that could produce more than 10,000 rows is refused before any request with the product named, since WONDER takes longer than the request deadline to answer a table that large; narrow year_range, sex or age_groups, or drop a dimension. Some measure cells come back as a CDC status token rather than a number — "Suppressed" (withheld for confidentiality), "Unreliable" (a rate CDC judges statistically unstable and does not show — on D76 and D77 when it is computed from 20 or fewer deaths, and D176, D158, and D157 when its 95% confidence interval is wider than 160% of the rate, whatever the death count), or "Not Applicable" (no population denominator); those cells read null in rows and each one is listed in cellNotes with its token. Each rate comes with the 95% confidence interval CDC publishes for it, as <rate>_lower and <rate>_upper right after the rate — null where CDC published none — so two rates can be compared by their intervals rather than their point values. CDC also drops whole rows before sending the table — strata with zero deaths, and strata whose death count is suppressed — so a stratum can be missing from rows entirely; messages carries CDC's statement whenever that happened. One call returns at most 10,000 rows: with limit omitted it returns the rest of the table up to that ceiling, a larger limit is lowered to it, and a smaller limit takes smaller pages; a page that stops short of the end reports the table's totalCount and a nextOffset to continue from. Paging shapes the response only — every call fetches the whole table from WONDER and returns a slice of it, so the figures, caveats and hidden-row notices are the same on every page and a smaller page saves no time. CDC rejects requests made less than 15 seconds apart across all five databases, so calls are spaced automatically: calls made while another is running wait their turn and run one after another, each queued call adding about 16 seconds plus its own query time before it returns. A request WONDER has not finished answering within 20 seconds fails as a retryable upstream_error, so one call queued behind another returns within about 56 seconds.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"sex": {
"enum": [
"all",
"male",
"female"
],
"type": "string",
"default": "all",
"description": "Filter by sex."
},
"limit": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 1,
"description": "Rows to return from the table CDC sent — any positive integer. Omit to take the rest of the table from offset, up to 10,000 rows. One call returns at most 10,000 rows: a larger limit is lowered to 10,000, and the response says so and gives a nextOffset when rows remain. WONDER's request carries no limit of its own, so this pages a table already fetched in full rather than narrowing the query: the deaths, rates, caveats and hidden-row notices are the same whichever page is read."
},
"offset": {
"type": "integer",
"default": 0,
"maximum": 10000,
"minimum": 0,
"description": "Index of the first row to return, for continuing past a previous call (default 0, max 10,000). Rows keep the order CDC returned them in, which is stable for a given query, so offset plus limit walks the table without gaps or repeats. An offset at or past the row total returns an empty page rather than an error."
},
"database": {
"enum": [
"underlying_1999_2020",
"provisional",
"underlying_2018_2024",
"multiple_1999_2020",
"multiple_2018_2024"
],
"type": "string",
"default": "underlying_1999_2020",
"description": "Which WONDER mortality database to query. \"underlying_1999_2020\" (D76) is final data for 1999–2020 and the default. \"provisional\" (D176) runs 2018 through the current year, updated weekly, and returns the most recent years labelled e.g. \"2025 (provisional)\". Its latest year is partial — deaths only through the data-through date the response names, against a full-year population — so its counts and rates read low next to a finished year, and a notice says so whenever the years queried include it. \"underlying_2018_2024\" (D158) is settled — not provisional — data for 2018–2024. \"multiple_1999_2020\" (D77) and \"multiple_2018_2024\" (D157) record every cause listed on the death certificate; without an mcd_icd10 filter they return the same figures as the underlying-cause database for the same era, so pick one only to use that filter. The two 1999–2020 databases report race in CDC's four bridged groups; the other three use the six single-race groups — figures broken out by race are not comparable between the two families."
},
"group_by": {
"type": "array",
"items": {
"enum": [
"year",
"age_group",
"sex",
"race",
"icd_chapter",
"icd_subchapter",
"cause_113",
"leading_causes",
"injury_intent",
"hispanic_origin",
"month"
],
"type": "string"
},
"default": [
"year"
],
"maxItems": 4,
"minItems": 1,
"description": "Dimensions to break results out by (1–4, each at most once), in output-column order — e.g. [\"year\"], [\"year\",\"sex\"], [\"icd_chapter\",\"year\"]. Results are always national. \"race\" resolves to whichever race vocabulary the selected database uses — four bridged groups (Asian and Pacific Islander combined) on the 1999–2020 databases, six single-race categories on the others, one of them \"More than one race\" — so a race series from one family cannot be spliced onto one from the other. \"hispanic_origin\" splits Hispanic or Latino, Not Hispanic or Latino, and Not Stated. \"month\" groups by calendar month (labels such as \"Jan., 2019\") and returns deaths only: population and both rates are \"Not Applicable\". The rest group by underlying cause: \"icd_chapter\" and \"icd_subchapter\" by node of CDC's ICD-10 tree, each row carrying the node's code range as cause_code (e.g. \"C00-D48\"), which cause_icd10 takes as it stands; \"cause_113\" by CDC's 113-cause list, each row carrying cause_level, its depth in that nested list (1 for a top-level cause) — a deeper cause is already counted in the one containing it; \"leading_causes\" by the 15 leading causes, ranked by deaths, and only on its own — CDC refuses it beside any other dimension, though the filters still apply; \"injury_intent\" by intent (unintentional, suicide, homicide, undetermined, legal intervention, or no injury). The most values the listed dimensions can take, multiplied, must be 10,000 or fewer — e.g. icd_subchapter (177) × year (22 on the 1999–2020 databases) × sex (2) fits, and adding age_group (12) does not — or the call is refused before any request. year beside month, and icd_chapter beside icd_subchapter, add nothing to the product, since each month names its year and each sub-chapter sits inside one chapter."
},
"mcd_icd10": {
"anyOf": [
{
"type": "string",
"const": ""
},
{
"enum": [
"999--999",
"999-999"
],
"type": "string"
},
{
"type": "string",
"pattern": "^[A-Z][0-9]{2}(\\.[0-9]+)?(-[A-Z][0-9]{2}(\\.[0-9]+)?)?$",
"description": "ICD-10 code or range, same form as cause_icd10 — a chapter such as \"S00-T98\" (injury and poisoning, a chapter the underlying-cause finder does not list), a block such as \"J09-J18\" (influenza and pneumonia), or a single code such as \"T40.1\" (heroin)."
},
{
"type": "array",
"items": {
"anyOf": [
{
"enum": [
"999--999",
"999-999"
],
"type": "string"
},
{
"type": "string",
"pattern": "^[A-Z][0-9]{2}(\\.[0-9]+)?(-[A-Z][0-9]{2}(\\.[0-9]+)?)?$",
"description": "ICD-10 code or range, same form as cause_icd10 — a chapter such as \"S00-T98\" (injury and poisoning, a chapter the underlying-cause finder does not list), a block such as \"J09-J18\" (influenza and pneumonia), or a single code such as \"T40.1\" (heroin)."
}
],
"description": "One list entry: an ICD-10 code or range, or the withheld-cause marker."
},
"maxItems": 50,
"description": "A list of up to 50 entries, each in the single-value form, matched as a union: a death counts once when it matches any of them, so a code set such as X40–X44 plus X60–X64 is one series with one set of rates. Repeated entries are ignored. An empty list reads as omitted."
}
],
"description": "Filter to deaths with any of these ICD-10 codes recorded anywhere on the death certificate, whether or not it was the underlying cause — e.g. \"died with a respiratory condition listed\", a population no underlying-cause query can produce. Takes one code or range, or a list of them, e.g. opioid involvement as [\"T40.0\",\"T40.1\",\"T40.2\",\"T40.3\",\"T40.4\",\"T40.6\"]. Valid only when database is \"multiple_1999_2020\", \"multiple_2018_2024\", or \"provisional\"; the other databases record only the underlying cause and reject it. \"999--999\", the withheld-cause marker described under cause_icd10, is offered here too but only by \"provisional\", where the 24-week withholding also covers drug poisoning (T36-T50) as a listed cause. Combines with cause_icd10, which keeps meaning the underlying cause: a death must match both filters. Omit for all causes."
},
"age_groups": {
"type": "array",
"items": {
"enum": [
"1",
"1-4",
"5-14",
"15-24",
"25-34",
"35-44",
"45-54",
"55-64",
"65-74",
"75-84",
"85+",
"NS"
],
"type": "string"
},
"description": "Restrict to deaths in any of the listed age groups — e.g. [\"25-34\",\"35-44\"] covers both; a repeated group counts once. \"1\" is the under-1-year group. \"NS\" is the group CDC puts a death in when the age was not recorded; it is not covered by any of the ten-year groups, so a filter listing all eleven of those still leaves those deaths out and returns fewer deaths than the same query unfiltered. CDC refuses \"NS\" beside only some of the other groups: list it on its own to count those deaths, or list all twelve values — the same as omitting age_groups — to include them. Omit for all ages, which includes them."
},
"year_range": {
"type": "object",
"required": [
"from",
"to"
],
"properties": {
"to": {
"type": "integer",
"maximum": 2026,
"minimum": 1999,
"description": "Last year (1999–2026 across all databases; the selected one holds a narrower span)."
},
"from": {
"type": "integer",
"maximum": 2026,
"minimum": 1999,
"description": "First year (1999–2026 across all databases; the selected one holds a narrower span)."
}
},
"description": "Inclusive year range; from must not be later than to. These bounds span every database (1999–2026); the years the selected one actually holds are narrower, and a range outside them is rejected with that database's span named. Omit for all years the database holds."
},
"cause_icd10": {
"anyOf": [
{
"type": "string",
"const": ""
},
{
"enum": [
"999--999",
"999-999"
],
"type": "string"
},
{
"type": "string",
"pattern": "^[A-Z][0-9]{2}(\\.[0-9]+)?(-[A-Z][0-9]{2}(\\.[0-9]+)?)?$",
"description": "ICD-10 underlying-cause code or range. WONDER takes a node of its ICD-10 tree — a chapter such as \"A00-B99\" (infectious) or \"V01-Y89\" (external causes), a block such as \"X40-X49\" (accidental poisoning) or \"C00-C97\" (malignant neoplasms), or a single code such as \"I21\" — and rejects any other span, e.g. \"X40-X44\", naming it in the error. List the codes (or blocks) to cover a span that is not a tree node."
},
{
"type": "array",
"items": {
"anyOf": [
{
"enum": [
"999--999",
"999-999"
],
"type": "string"
},
{
"type": "string",
"pattern": "^[A-Z][0-9]{2}(\\.[0-9]+)?(-[A-Z][0-9]{2}(\\.[0-9]+)?)?$",
"description": "ICD-10 underlying-cause code or range. WONDER takes a node of its ICD-10 tree — a chapter such as \"A00-B99\" (infectious) or \"V01-Y89\" (external causes), a block such as \"X40-X49\" (accidental poisoning) or \"C00-C97\" (malignant neoplasms), or a single code such as \"I21\" — and rejects any other span, e.g. \"X40-X44\", naming it in the error. List the codes (or blocks) to cover a span that is not a tree node."
}
],
"description": "One list entry: an ICD-10 code or range, or the withheld-cause marker."
},
"maxItems": 50,
"description": "A list of up to 50 entries, each in the single-value form, matched as a union: a death counts once when it matches any of them, so a code set such as X40–X44 plus X60–X64 is one series with one set of rates. Repeated entries are ignored. An empty list reads as omitted."
}
],
"description": "Filter to ICD-10 underlying causes of death — the single condition CDC certified as having started the chain of events leading to death. Takes one code or range, or a list of them for a cause defined as a code set, e.g. drug overdose as [\"X40\",\"X41\",\"X42\",\"X43\",\"X44\",\"X60\",\"X61\",\"X62\",\"X63\",\"X64\",\"X85\",\"Y10\",\"Y11\",\"Y12\",\"Y13\",\"Y14\"]. Omit for all causes. Accepted by every database. \"999--999\" is not an ICD-10 code but CDC's own marker for deaths whose cause it is still withholding under the provisional database's six-month reporting lag; it counts that backlog, and only the \"provisional\" database offers it. \"999-999\", the cause_code a sub-chapter row carries for the same deaths, is accepted too and sent as \"999--999\". On that database CDC withholds external-cause (V01-Y89) and sudden-death (R95, R96) causes for the 24 weeks before its data-through date, so a filter reaching them — a block or chapter containing one included — undercounts those weeks; the notice names the cutoff."
}
},
"additionalProperties": false
}{
"type": "object",
"anyOf": [
{
"not": {
"required": [
"error"
]
},
"required": [
"rows",
"rowCount",
"database",
"databaseTitle",
"caveats",
"cellNotes",
"messages",
"suppressedCount",
"effectiveQuery",
"totalCount"
]
},
{
"required": [
"error"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"cap": {
"type": "number",
"description": "Page size that ended this response — limit, or 10,000 (the most one call returns) when limit was omitted or set higher. Present only when rows remain past the page."
},
"rows": {
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"type": [
"string",
"number",
"null"
]
}
},
"description": "Result rows. Each carries the requested group-by dimensions, then cause_code when grouped by icd_chapter or icd_subchapter — CDC's code range for the row's ICD-10 node (the sub-chapter's when both are grouped), null where CDC sent none; the provisional database's withheld-cause row reads \"999--999\" by chapter and \"999-999\" by sub-chapter, and cause_icd10 takes either as it stands — then cause_level when grouped by cause_113, the row's depth in that nested list as an integer, then deaths, population, crude_rate, and age_adjusted_rate (per 100,000) when age standardization is possible — it is omitted when age_group is a grouping dimension or age_groups selects a single group, and it is the rate to compare across groups, since crude_rate moves with each group's age mix. Each rate is followed by its 95% confidence interval: crude_rate_lower and crude_rate_upper, then age_adjusted_rate_lower and age_adjusted_rate_upper when the row has age_adjusted_rate. The bounds are CDC's own published figures, never computed here, and are null wherever CDC sent no interval (a \"Not Applicable\" rate, for one); an \"Unreliable\" rate reads null but keeps the interval CDC published with it. Dimension values are CDC's own labels with only surrounding whitespace removed, so the same year keys identically across databases; nothing inside a label is changed, and on the provisional database a year reads \"2025 (provisional)\" or \"2026 (provisional and partial)\" rather than a bare year. A measure cell CDC returned as a status token instead of a number is null here; cellNotes names the cell and the token. These are one page of the table CDC sent, in its order, bounded by limit and by the 10,000-row per-call ceiling; totalCount says how many rows the whole table holds."
},
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "JSON-RPC error code for this failure."
},
"data": {
"type": "object",
"properties": {
"reason": {
"type": "string",
"examples": [
"invalid_query",
"rate_limited",
"upstream_error"
],
"description": "Machine-readable failure mode. Declared by this tool: `invalid_query`: The request contradicts itself — a year_range whose from is later than its to, or a group_by dimension listed twice — or asks for a combination CDC refuses — age_groups holding \"NS\" beside only some of the known age groups, or leading_causes beside another group_by value — or groups by dimensions whose values could make a table of more than 10,000 rows, or does not fit the selected database — a year_range outside the years it holds, mcd_icd10 against a database that records only the underlying cause, or the withheld-cause marker against one that keeps no withheld backlog — or WONDER itself rejected it, e.g. an ICD-10 code or range its tree does not hold, or a filter/grouping combination it does not allow. `rate_limited`: A request reached WONDER less than 15 seconds after the previous response to the same source IP finished, and WONDER returned 429. This server queues its own calls, so the earlier request usually came from another client sharing that IP. `upstream_error`: WONDER returned an unexpected response, was unreachable, or did not finish answering a request within 20 seconds. Other values are possible when a failure originates below the handler."
},
"recovery": {
"type": "object",
"required": [
"hint"
],
"properties": {
"hint": {
"type": "string"
}
},
"description": "Actionable next step for the caller.",
"additionalProperties": {}
},
"retryable": {
"type": "boolean",
"description": "Whether retrying may succeed."
}
},
"additionalProperties": {}
},
"message": {
"type": "string",
"description": "Human-readable description of what went wrong."
}
},
"description": "Present when the call failed. Absent on success.",
"additionalProperties": {}
},
"shown": {
"type": "number",
"description": "Number of rows returned in this response."
},
"notice": {
"type": "string",
"description": "Guidance when no rows matched or CDC recorded no deaths for the query, when the returned rows are a page of a larger table — naming whether limit or the 10,000-row per-call ceiling ended it — or the offset ran past it, whether the rows CDC withheld leave the deaths in rows complete or partial, a note when CDC returned a status token in place of a measure value, that rows grouped by cause_113 nest and do not sum across levels, on a multiple-cause database queried without mcd_icd10, that its figures match the underlying-cause database for the same years, and on the provisional database, when the years queried include its partial latest year (naming the partial month too when grouped by month) or a cause filter or cause grouping reaches causes CDC is still withholding under its 24-week reporting lag."
},
"caveats": {
"type": "array",
"items": {
"type": "string"
},
"description": "CDC-provided caveats and footnotes: data revisions, population-estimate sources, suppression and rate-reliability rules. CDC's links to its methodology pages are kept as Markdown links, e.g. \"[More information.](https://wonder.cdc.gov/wonder/help/ucd-expanded.html#Confidence-Intervals)\". They describe the whole table CDC assembled, so they come back complete on every page rather than scoped to the rows returned."
},
"database": {
"type": "string",
"description": "WONDER dataset code the rows came from — e.g. \"D76\", \"D176\", \"D157\"."
},
"messages": {
"type": "array",
"items": {
"type": "string"
},
"description": "Notices CDC attached to this table, verbatim apart from links, which are kept as Markdown links. The ones that matter say rows were withheld before the table was sent — \"Rows with zero Deaths are hidden.\" and \"Rows with suppressed Deaths are hidden.\" A withheld row is absent from rows entirely, with nothing in the table marking the gap. A stratum hidden for zero deaths recorded none, so deaths summed from rows stay complete; while the suppressed-deaths message is present, a stratum missing from rows may have been dropped rather than unobserved, and any count, ranking, or completeness claim drawn from rows is partial. The notice says which reading applies. These describe the whole table, so they come back complete on every page. Empty when CDC withheld no rows."
},
"rowCount": {
"type": "number",
"description": "Number of rows returned in this response — the page size whenever it falls short of totalCount."
},
"cellNotes": {
"type": "array",
"items": {
"type": "object",
"required": [
"row",
"column",
"token"
],
"properties": {
"row": {
"type": "number",
"description": "Zero-based index into rows — the rows in this response, so it is relative to the page rather than to the whole table."
},
"token": {
"type": "string",
"description": "Token CDC returned in place of a number: \"Suppressed\" (withheld for confidentiality, fewer than 10 persons), \"Unreliable\" (a rate CDC judges statistically unstable and replaces with this token — on D76 and D77 when it is computed from 20 or fewer deaths, and D176, D158, and D157 when its 95% confidence interval is wider than 160% of the rate, whatever the death count; the row's deaths and population stand), or \"Not Applicable\" (no population denominator)."
},
"column": {
"type": "string",
"description": "Measure column whose numeric value the token replaced."
}
},
"description": "One flagged measure cell: where it is and what CDC put there.",
"additionalProperties": false
},
"description": "One entry per measure cell CDC returned as a status token rather than a number, covering the rows in this response only. Those cells read null in rows, so this is what tells a withheld value apart from an unreliable one or a genuinely absent one."
},
"truncated": {
"type": "boolean",
"description": "True when rows remain past the ones returned. Absent means this response runs to the end of the table, which is also the case for an offset past it."
},
"nextOffset": {
"type": "number",
"description": "Offset to pass on the next call to resume immediately after the last row returned. Present only when further rows remain."
},
"totalCount": {
"type": "number",
"description": "Rows in the whole table CDC returned, before any page was taken. Exact rather than estimated — the table is parsed in full before a page is taken from it."
},
"databaseTitle": {
"type": "string",
"description": "CDC's own title for that database, e.g. \"Underlying Cause of Death, 1999-2020\". Names the era and record type the rows describe, so a result read on its own is self-describing."
},
"effectiveQuery": {
"type": "string",
"description": "Human-readable summary of the grouping and filters sent to WONDER."
},
"suppressedCount": {
"type": "number",
"description": "How many cellNotes carry the \"Suppressed\" token — cells CDC withheld for confidentiality. Counted over the rows in this response, so it tracks the page rather than the whole table."
}
},
"additionalProperties": false
}{
"readOnlyHint": true
}Prompts
1 advertised
analyze_health_trend
Structured workflow for investigating a public health question across CDC data. Picks between the two systems this server reads — CDC WONDER for national mortality (1999–2026, final and provisional), the Socrata catalog for everything else — then guides through: discover relevant datasets, inspect schemas, query for baseline data, compare across time/geography/demographics, and synthesize findings.
- topic
- The health topic or question to investigate (e.g., "diabetes mortality trends by state", "childhood vaccination coverage over time"). (required)
- timeRange
- Time period of interest (e.g., "2015-2023", "last 10 years"). Defaults to all available years.
- geography
- Geographic scope — "national", a specific state name (e.g., "California"), or "all states" for comparison. Defaults to national.
Resources
4 advertised
CDC Dataset Catalog — CDC Dataset Catalog
Top 50 CDC catalog entries by popularity with names, categories, asset types, column counts, and update timestamps. Provides an overview of the CDC data landscape for orientation; an entry whose columnCount is 0 is not tabular and yields no data from cdc_get_dataset_schema or cdc_query_dataset. Use cdc_discover_datasets for full catalog search with filtering and pagination.
cdc://datasetsNCHS - Leading Causes of Death: United States — CDC Dataset Detail
Dataset metadata and column schema for a specific CDC dataset, addressable by URI. Carries the first 100 columns, the dataset's total column count, and a truncation flag; call cdc_get_dataset_schema with column_offset for the columns beyond that window. The dataset description is capped at 4,000 characters, with descriptionTruncated and descriptionLength set when cut; call cdc_get_dataset_schema with full_description: true for the whole text. The row count and its rowCountSource ("live" or "cached") match what cdc_get_dataset_schema reports for the same ID. A filter carries parentId, the dataset it is a view of; a chart or map has no schema of its own, and the failure names the dataset it is built on. Where the publisher recorded them, temporalCoverage, updateFrequency, geographicCoverage, citation, license, and attribution carry its own text in no fixed format, as on cdc_get_dataset_schema.
cdc://datasets/bi63-dtpuProvisional COVID-19 Deaths by Sex and Age — CDC Dataset Detail
Dataset metadata and column schema for a specific CDC dataset, addressable by URI. Carries the first 100 columns, the dataset's total column count, and a truncation flag; call cdc_get_dataset_schema with column_offset for the columns beyond that window. The dataset description is capped at 4,000 characters, with descriptionTruncated and descriptionLength set when cut; call cdc_get_dataset_schema with full_description: true for the whole text. The row count and its rowCountSource ("live" or "cached") match what cdc_get_dataset_schema reports for the same ID. A filter carries parentId, the dataset it is a view of; a chart or map has no schema of its own, and the failure names the dataset it is built on. Where the publisher recorded them, temporalCoverage, updateFrequency, geographicCoverage, citation, license, and attribution carry its own text in no fixed format, as on cdc_get_dataset_schema.
cdc://datasets/9bhg-hckucdc-health-guide
Gates for comparing, ranking, and trending cdc_* results — which CDC WONDER rate to compare, race beside Hispanic origin, the provisional database's partial year and withheld causes, finding a peak month, splicing WONDER databases, and reading stratified Socrata tables. Use before saying a CDC mortality or health figure rose, fell, peaks, or differs between groups or places.
skill://cdc-health-guide/SKILL.mdCDC Dataset Detail — CDC Dataset Detail
Dataset metadata and column schema for a specific CDC dataset, addressable by URI. Carries the first 100 columns, the dataset's total column count, and a truncation flag; call cdc_get_dataset_schema with column_offset for the columns beyond that window. The dataset description is capped at 4,000 characters, with descriptionTruncated and descriptionLength set when cut; call cdc_get_dataset_schema with full_description: true for the whole text. The row count and its rowCountSource ("live" or "cached") match what cdc_get_dataset_schema reports for the same ID. A filter carries parentId, the dataset it is a view of; a chart or map has no schema of its own, and the failure names the dataset it is built on. Where the publisher recorded them, temporalCoverage, updateFrequency, geographicCoverage, citation, license, and attribution carry its own text in no fixed format, as on cdc_get_dataset_schema.
cdc://datasets/{datasetId}io.modelcontextprotocol/skills
skill://{+path}90-day uptime
A lightweight connection check about every six hours, sampled on hosts with many listings. It shows the server answered, not that its tools work.
- 2026-11-07: 1 of 1 checks answered
30-day availability
Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.
- Successful checks
- 1 of 1 (100%)
- Average latency
- 542 ms
- 95th percentile latency
- 542 ms
Maintenance history
Public check and source-refresh events. Editorial and ownership records remain private.
- · probe · passed · 542 ms · metadata changed · 5 tool(s), 4 resource(s), 2 resource template(s), 1 prompt(s)
Connect from your client
Each snippet registers the same endpoint; pick the client you use.
Claude Code
claude mcp add --transport http cdc-health-mcp-server-caseyjhand https://cdc.caseyjhand.com/mcpCodex
codex mcp add cdc-health-mcp-server-caseyjhand --url https://cdc.caseyjhand.com/mcpCursor
{
"mcpServers": {
"cdc-health-mcp-server-caseyjhand": {
"url": "https://cdc.caseyjhand.com/mcp"
}
}
}Cline
{
"mcpServers": {
"cdc-health-mcp-server-caseyjhand": {
"type": "streamableHttp",
"url": "https://cdc.caseyjhand.com/mcp",
"disabled": false
}
}
}VS Code
{
"servers": {
"cdc-health-mcp-server-caseyjhand": {
"type": "http",
"url": "https://cdc.caseyjhand.com/mcp"
}
}
}Gemini CLI
gemini mcp add --transport http cdc-health-mcp-server-caseyjhand https://cdc.caseyjhand.com/mcpClaude Desktop
https://cdc.caseyjhand.com/mcpWindsurf
{
"mcpServers": {
"cdc-health-mcp-server-caseyjhand": {
"serverUrl": "https://cdc.caseyjhand.com/mcp"
}
}
}Let your agent find its tools
Find servers like cdc-health-mcp-server from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.