@cyanheads/noaa-cdo-mcp-server

v0.1.9 pre-1.0

Search NOAA CDO stations and datasets, fetch historical weather observations via MCP. STDIO or Streamable HTTP.

@cyanheads/noaa-cdo-mcp-server
claude mcp add --transport http noaa-cdo-mcp-server https://noaa-cdo.caseyjhand.com/mcp
codex mcp add noaa-cdo-mcp-server --url https://noaa-cdo.caseyjhand.com/mcp
{
  "mcpServers": {
    "noaa-cdo-mcp-server": {
      "url": "https://noaa-cdo.caseyjhand.com/mcp"
    }
  }
}
gemini mcp add --transport http noaa-cdo-mcp-server https://noaa-cdo.caseyjhand.com/mcp
{
  "mcpServers": {
    "noaa-cdo-mcp-server": {
      "command": "bunx",
      "args": [
        "@cyanheads/noaa-cdo-mcp-server@latest"
      ]
    }
  }
}
{
  "mcpServers": {
    "noaa-cdo-mcp-server": {
      "type": "http",
      "url": "https://noaa-cdo.caseyjhand.com/mcp"
    }
  }
}
curl -X POST https://noaa-cdo.caseyjhand.com/mcp \
  -H "Content-Type: application/json" \
  -H "MCP-Protocol-Version: 2025-11-25" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.0"}}}'

Tools

7

noaa_list_datasets

List available NOAA CDO datasets with their IDs, names, and temporal coverage. Returns all ~11 datasets by default (no required parameters). Optionally filter to datasets that contain a specific data type, cover a location or station, or overlap a date range. Common datasets: GHCND (daily observations, 1763–present), GSOM (monthly summaries), GSOY (annual summaries), NORMAL_DLY/MLY/ANN/HLY (1981–2010 climate normals). Use this first to discover available datasets before calling noaa_fetch_data.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "noaa_list_datasets",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "datatypeId": {
      "description": "Filter to datasets containing these data type IDs (e.g., [\"TMAX\", \"PRCP\"]). Optional.",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "locationId": {
      "description": "Filter to datasets covering this location ID (e.g., \"FIPS:37\" for NC). Optional.",
      "type": "string"
    },
    "stationId": {
      "description": "Filter to datasets covering this station ID (e.g., \"GHCND:USC00450974\"). Optional.",
      "type": "string"
    },
    "startDate": {
      "description": "Filter to datasets with data on or after this ISO date (YYYY-MM-DD). Optional.",
      "type": "string"
    },
    "endDate": {
      "description": "Filter to datasets with data on or before this ISO date (YYYY-MM-DD). Optional.",
      "type": "string"
    },
    "sortField": {
      "description": "Sort results by this field. Optional.",
      "type": "string",
      "enum": [
        "id",
        "name",
        "mindate",
        "maxdate",
        "datacoverage"
      ]
    },
    "sortOrder": {
      "description": "Sort direction. Optional; defaults to asc.",
      "type": "string",
      "enum": [
        "asc",
        "desc"
      ]
    },
    "limit": {
      "default": 25,
      "description": "Maximum number of results to return (1–1000). Defaults to 25.",
      "type": "integer",
      "minimum": 1,
      "maximum": 1000
    },
    "offset": {
      "default": 0,
      "description": "Zero-based index of the first result to return for pagination. Defaults to 0.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "limit",
    "offset"
  ],
  "additionalProperties": false
}
view source ↗

noaa_list_data_categories

List data categories that group related data types — Temperature, Precipitation, Wind, Pressure, Sunshine, Sky cover, Weather Type, and more. Use to discover what types of measurements are available before calling noaa_list_data_types. Optionally filter by dataset, location, station, or date range. There are ~41 categories in total.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "noaa_list_data_categories",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "datasetId": {
      "description": "Filter to categories available in this dataset (e.g., \"GHCND\", \"GSOM\"). Optional.",
      "type": "string"
    },
    "locationId": {
      "description": "Filter to categories available at this location ID. Optional.",
      "type": "string"
    },
    "stationId": {
      "description": "Filter to categories available at this station ID. Optional.",
      "type": "string"
    },
    "startDate": {
      "description": "Filter to categories with data on or after this ISO date (YYYY-MM-DD). Optional.",
      "type": "string"
    },
    "endDate": {
      "description": "Filter to categories with data on or before this ISO date (YYYY-MM-DD). Optional.",
      "type": "string"
    },
    "sortField": {
      "description": "Sort results by this field. Optional.",
      "type": "string",
      "enum": [
        "id",
        "name"
      ]
    },
    "sortOrder": {
      "description": "Sort direction. Optional; defaults to asc.",
      "type": "string",
      "enum": [
        "asc",
        "desc"
      ]
    },
    "limit": {
      "default": 25,
      "description": "Maximum number of results to return (1–1000). Defaults to 25.",
      "type": "integer",
      "minimum": 1,
      "maximum": 1000
    },
    "offset": {
      "default": 0,
      "description": "Zero-based index of the first result to return for pagination. Defaults to 0.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "limit",
    "offset"
  ],
  "additionalProperties": false
}
view source ↗

noaa_list_data_types

List available data types (measurement labels like TMAX, TMIN, PRCP, SNOW) for a given dataset or category. Pass a datasetId to see what is measured in that dataset, or a datacategoryId (e.g., "TEMP") to see all temperature-related types. Hundreds of types exist across all datasets. Use this before calling noaa_fetch_data when the data type IDs are unknown. Common GHCND types: TMAX (max temperature), TMIN (min temperature), PRCP (precipitation), SNOW (snowfall), SNWD (snow depth), AWND (average wind speed).

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "noaa_list_data_types",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "datasetId": {
      "description": "Filter to data types available in this dataset (e.g., \"GHCND\", \"GSOM\"). Optional.",
      "type": "string"
    },
    "datacategoryId": {
      "description": "Filter to data types in this category (e.g., \"TEMP\" for temperature types, \"PRCP\" for precipitation). Optional.",
      "type": "string"
    },
    "locationId": {
      "description": "Filter to data types available at this location ID. Optional.",
      "type": "string"
    },
    "stationId": {
      "description": "Filter to data types available at this station ID. Optional.",
      "type": "string"
    },
    "startDate": {
      "description": "Filter to data types with data on or after this ISO date (YYYY-MM-DD). Optional.",
      "type": "string"
    },
    "endDate": {
      "description": "Filter to data types with data on or before this ISO date (YYYY-MM-DD). Optional.",
      "type": "string"
    },
    "sortField": {
      "description": "Sort results by this field. Optional.",
      "type": "string",
      "enum": [
        "id",
        "name",
        "mindate",
        "maxdate",
        "datacoverage"
      ]
    },
    "sortOrder": {
      "description": "Sort direction. Optional; defaults to asc.",
      "type": "string",
      "enum": [
        "asc",
        "desc"
      ]
    },
    "limit": {
      "default": 25,
      "description": "Maximum number of results to return (1–1000). Defaults to 25.",
      "type": "integer",
      "minimum": 1,
      "maximum": 1000
    },
    "offset": {
      "default": 0,
      "description": "Zero-based index of the first result to return for pagination. Defaults to 0.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "limit",
    "offset"
  ],
  "additionalProperties": false
}
view source ↗

noaa_find_locations

open-world

Search for geographic locations by category (CITY, ST, CNTY, CNTRY, ZIP, CLIM_REG, etc.). Returns location IDs used in station search and data queries. Without locationCategoryId, returns all location types. Use locationCategoryId=ST to list US states (~52 entries — small enough to retrieve completely). Use locationCategoryId=CITY for cities (thousands of entries — use pagination and sortField=name to navigate alphabetically). The CDO API has no name-search parameter; to find a specific city, sort alphabetically with sortField=name and page through results. Location IDs: states as FIPS:37 (NC), cities as CITY:US530031, zip codes as ZIP:98101, countries as CNTRY:US. Obtain location IDs here, then pass them to noaa_find_stations or noaa_fetch_data.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "noaa_find_locations",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "locationCategoryId": {
      "description": "Category filter. Use ST for states (~52 entries), CNTY for counties, CITY for cities (large set — thousands of entries), CNTRY for countries, ZIP for zip codes, CLIM_REG for NOAA climate regions, CLIM_DIV for climate divisions, HYD_ACC/HYD_CAT/HYD_REG/HYD_SUB for hydrological categories. Optional — omit to return all location types.",
      "type": "string"
    },
    "datasetId": {
      "description": "Filter to locations covered by this dataset (e.g., \"GHCND\"). Optional.",
      "type": "string"
    },
    "datacategoryId": {
      "description": "Filter to locations with this data category (e.g., \"TEMP\"). Optional.",
      "type": "string"
    },
    "startDate": {
      "description": "Filter to locations with data on or after this ISO date (YYYY-MM-DD). Optional.",
      "type": "string"
    },
    "endDate": {
      "description": "Filter to locations with data on or before this ISO date (YYYY-MM-DD). Optional.",
      "type": "string"
    },
    "sortField": {
      "description": "Sort results by this field. Use name with sortOrder=asc to browse alphabetically when searching for a specific city or location name. Optional.",
      "type": "string",
      "enum": [
        "id",
        "name",
        "mindate",
        "maxdate",
        "datacoverage"
      ]
    },
    "sortOrder": {
      "description": "Sort direction. Optional; defaults to asc.",
      "type": "string",
      "enum": [
        "asc",
        "desc"
      ]
    },
    "limit": {
      "default": 25,
      "description": "Maximum number of results to return (1–1000). Defaults to 25.",
      "type": "integer",
      "minimum": 1,
      "maximum": 1000
    },
    "offset": {
      "default": 0,
      "description": "Zero-based index of the first result to return for pagination. Defaults to 0.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "limit",
    "offset"
  ],
  "additionalProperties": false
}
view source ↗

noaa_find_stations

open-world

Search for weather observation stations by location, bounding box, dataset, and data type. Returns station IDs, names, coordinates, elevation, and data coverage dates. Filter by locationId (e.g., "FIPS:37" for all NC stations), extent (lat/lon bounding box), datasetId, datatypeId, and date range. Station IDs returned here are used as stationId in noaa_fetch_data. A station must have data for the dataset and date range you want — filter by datasetId and startDate/endDate to ensure compatibility. Common station ID formats: GHCND:USC00450974, COOP:010008.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "noaa_find_stations",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "locationId": {
      "description": "Filter to stations within this location ID (e.g., \"FIPS:37\" for NC, \"CITY:US530031\" for Seattle). Obtain from noaa_find_locations. Optional.",
      "type": "string"
    },
    "extent": {
      "description": "Bounding box filter as \"minLat,minLon,maxLat,maxLon\" (e.g., \"47.5,-122.4,47.7,-122.1\" for central Seattle). Optional.",
      "type": "string"
    },
    "datasetId": {
      "description": "Filter to stations that have data in this dataset (e.g., \"GHCND\" for daily observations). Optional.",
      "type": "string"
    },
    "datatypeId": {
      "description": "Filter to stations that record these data types (e.g., [\"TMAX\", \"TMIN\", \"PRCP\"]). Optional.",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "datacategoryId": {
      "description": "Filter to stations with data in this category (e.g., \"TEMP\"). Optional.",
      "type": "string"
    },
    "startDate": {
      "description": "Filter to stations with data on or after this ISO date (YYYY-MM-DD). Optional.",
      "type": "string"
    },
    "endDate": {
      "description": "Filter to stations with data on or before this ISO date (YYYY-MM-DD). Optional.",
      "type": "string"
    },
    "sortField": {
      "description": "Sort results by this field. Optional.",
      "type": "string",
      "enum": [
        "id",
        "name",
        "mindate",
        "maxdate",
        "datacoverage"
      ]
    },
    "sortOrder": {
      "description": "Sort direction. Optional; defaults to asc.",
      "type": "string",
      "enum": [
        "asc",
        "desc"
      ]
    },
    "limit": {
      "default": 25,
      "description": "Maximum number of results to return (1–1000). Defaults to 25.",
      "type": "integer",
      "minimum": 1,
      "maximum": 1000
    },
    "offset": {
      "default": 0,
      "description": "Zero-based index of the first result to return for pagination. Defaults to 0.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "limit",
    "offset"
  ],
  "additionalProperties": false
}
view source ↗

noaa_get_station

Fetch full metadata for a single weather station by its ID (e.g., "GHCND:USC00450974", "COOP:010008"). Returns name, coordinates, elevation, and the full date range for which data is available. Use when you have a station ID from noaa_find_stations and want its complete details, or to verify a station before querying data.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "noaa_get_station",
    "arguments": {
      "stationId": "<stationId>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "stationId": {
      "type": "string",
      "minLength": 1,
      "description": "Station ID to fetch (e.g., \"GHCND:USC00450974\", \"COOP:010008\"). Obtain from noaa_find_stations."
    }
  },
  "required": [
    "stationId"
  ],
  "additionalProperties": false
}
view source ↗

noaa_fetch_data

open-world

Fetch historical observation records from a NOAA CDO dataset for a given date range. Requires datasetId (e.g., GHCND for daily, GSOM for monthly), startDate, and endDate. Optionally scope to specific stations, locations, and data types. Date range limits per request: sub-daily and daily datasets (GHCND, PRECIP_15, PRECIP_HLY, NORMAL_DLY, NORMAL_HLY) are limited to 1 year; monthly and annual datasets (GSOM, GSOY, NORMAL_MLY, NORMAL_ANN) are limited to 10 years. For climate normals (NORMAL_*), use startDate=2010-01-01 and endDate=2010-12-31 — that is the API proxy year regardless of which 30-year period is being described. Returns flat tuples of { date, datatype, station, value, attributes }. Strongly recommended: pass units=metric or units=standard — without it, GHCND values are raw tenths-of-unit integers (TMAX=256 = 25.6°C, PRCP=12 = 1.2mm). GSOM/GSOY are already scaled.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "noaa_fetch_data",
    "arguments": {
      "datasetId": "<datasetId>",
      "startDate": "<startDate>",
      "endDate": "<endDate>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "datasetId": {
      "type": "string",
      "minLength": 1,
      "description": "Dataset ID to query (e.g., GHCND for daily data, GSOM for monthly, GSOY for annual, NORMAL_DLY/MLY/ANN/HLY for 1981–2010 climate normals). Determines date range limit: GHCND/PRECIP_*/NORMAL_DLY/NORMAL_HLY allow 1-year max per request; GSOM/GSOY/NORMAL_MLY/NORMAL_ANN allow 10-year max."
    },
    "startDate": {
      "type": "string",
      "description": "Start date for observations (YYYY-MM-DD). For NORMAL_* datasets use 2010-01-01 regardless of the years being analyzed — 2010 is the API proxy year for all normals."
    },
    "endDate": {
      "type": "string",
      "description": "End date for observations (YYYY-MM-DD). Must be within 1 year of startDate for sub-daily/daily datasets (GHCND, PRECIP_15, PRECIP_HLY, NORMAL_DLY, NORMAL_HLY) or within 10 years for monthly/annual datasets (GSOM, GSOY, NORMAL_MLY, NORMAL_ANN). For any NORMAL_* dataset use 2010-12-31."
    },
    "stationId": {
      "description": "One or more station IDs to filter by (e.g., [\"GHCND:USC00450974\"]). Obtain from noaa_find_stations. Multiple IDs return comparative readings across stations. Optional.",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "locationId": {
      "description": "One or more location IDs to filter by (e.g., [\"FIPS:37\", \"ZIP:98101\"]). Broader than stationId — returns data from all stations within the location. Optional.",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "datatypeId": {
      "description": "One or more data type IDs to include (e.g., [\"TMAX\", \"TMIN\", \"PRCP\"]). Without this, all data types for the dataset are returned. Use noaa_list_data_types to discover valid IDs. Optional.",
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "units": {
      "description": "Unit system for returned values. Without this parameter, GHCND returns raw tenths-of-unit integers (TMAX=256 = 25.6°C). Strongly recommended: pass metric (SI units) or standard (Fahrenheit/inches). Optional.",
      "type": "string",
      "enum": [
        "standard",
        "metric"
      ]
    },
    "includemetadata": {
      "default": true,
      "description": "Include pagination metadata in the response. Defaults to true.",
      "type": "boolean"
    },
    "sortField": {
      "description": "Sort results by this field. Optional.",
      "type": "string",
      "enum": [
        "datatype",
        "date",
        "station"
      ]
    },
    "sortOrder": {
      "description": "Sort direction. Optional; defaults to asc.",
      "type": "string",
      "enum": [
        "asc",
        "desc"
      ]
    },
    "limit": {
      "default": 25,
      "description": "Maximum number of records to return (1–1000). Defaults to 25.",
      "type": "integer",
      "minimum": 1,
      "maximum": 1000
    },
    "offset": {
      "default": 0,
      "description": "Zero-based index of the first record to return for pagination. Defaults to 0.",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    }
  },
  "required": [
    "datasetId",
    "startDate",
    "endDate",
    "includemetadata",
    "limit",
    "offset"
  ],
  "additionalProperties": false
}
view source ↗

Resources

2

All available NOAA CDO datasets with IDs, names, and temporal coverage. Use as injectable context to orient an agent on which datasets exist before querying data. Returns a small stable list (~11–13 datasets). Equivalent to calling noaa_list_datasets with no filters and a high limit.

uri noaa://datasets

Station metadata by ID — name, coordinates, elevation, and the full date range for which data is available. Mirrors noaa_get_station as an injectable resource. URI format: noaa://stations/GHCND:USC00450974.

uri noaa://stations/{stationId}