usgs-water-mcp-server

v0.2.2 pre-1.0

Query real-time and historical water data from ~8,000 USGS stream gages and groundwater wells via MCP. STDIO or Streamable HTTP.

usgs-water.caseyjhand.com/mcp
claude mcp add --transport http usgs-water-mcp-server https://usgs-water.caseyjhand.com/mcp
codex mcp add usgs-water-mcp-server --url https://usgs-water.caseyjhand.com/mcp
{
  "mcpServers": {
    "usgs-water-mcp-server": {
      "url": "https://usgs-water.caseyjhand.com/mcp"
    }
  }
}
gemini mcp add --transport http usgs-water-mcp-server https://usgs-water.caseyjhand.com/mcp
{
  "mcpServers": {
    "usgs-water-mcp-server": {
      "command": "bunx",
      "args": [
        "mcp-remote",
        "https://usgs-water.caseyjhand.com/mcp"
      ]
    }
  }
}
{
  "mcpServers": {
    "usgs-water-mcp-server": {
      "type": "http",
      "url": "https://usgs-water.caseyjhand.com/mcp"
    }
  }
}
curl -X POST https://usgs-water.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

water_list_parameters

List well-known USGS parameter codes with human-readable names, units, and thematic domain — a static, built-in catalog. Use this first to discover that 00060 = "Discharge" (ft³/s), 00065 = "Gage height" (ft), 00010 = "Temperature, water" (°C), 72019 = "Depth to water level" (ft), etc. Filter by group to narrow results.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "water_list_parameters",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "group": {
      "default": "all",
      "description": "Filter by thematic domain: \"streamflow\", \"groundwater\", \"temperature\", \"meteorological\", \"water-quality\", or \"all\" (default) for the full catalog.",
      "type": "string",
      "enum": [
        "streamflow",
        "groundwater",
        "temperature",
        "meteorological",
        "water-quality",
        "all"
      ]
    }
  },
  "required": [
    "group"
  ],
  "additionalProperties": false
}
view source ↗

water_find_sites

open-world

Find USGS water monitoring sites by bounding box, state, county, or HUC watershed code, filtered by site type and parameter availability. Returns site numbers, names, coordinates, types, altitude, and (in expanded mode) drainage area. Call this first — water_get_readings, water_get_series, and water_get_conditions all require a site number. Capped at 500 sites inline; when truncated=true, upstreamTotal holds the full count and, if DataCanvas is enabled, the complete match set stages to a canvas (canvas_id/table_name) for retrieval via water_dataframe_query — otherwise narrow the filters to get all matches.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "water_find_sites",
    "arguments": {}
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "bbox": {
      "description": "Bounding box as \"west,south,east,north\" in decimal degrees (e.g. \"-77.5,38.5,-76.5,39.5\" for the DC metro area). Mutually exclusive with stateCd/countyCd/huc.",
      "type": "string",
      "pattern": "^-?\\d+(\\.\\d+)?(,-?\\d+(\\.\\d+)?){3}$"
    },
    "stateCd": {
      "description": "2-character US state abbreviation (e.g. \"VA\", \"WA\"). Returns all sites in the state for the given filters.",
      "type": "string",
      "pattern": "^[A-Za-z]{2}$"
    },
    "countyCd": {
      "description": "FIPS county code(s) as bare 5-digit numbers — state and county digits concatenated, no separator (e.g. \"51013\" for Arlington, VA). Comma-separate up to 20 (e.g. \"51059,51061\"). Use with stateCd for clarity.",
      "type": "string",
      "pattern": "^\\d{5}(,\\d{5}){0,19}$"
    },
    "huc": {
      "description": "Hydrologic Unit Code (HUC) scoping results to a watershed. Either a 2-digit major HUC (e.g. \"02\" for the Mid-Atlantic region) or an 8-digit minor HUC (e.g. \"02070008\" for the Middle Potomac). NWIS accepts no other lengths.",
      "type": "string",
      "pattern": "^(\\d{2}|\\d{8})$"
    },
    "siteType": {
      "description": "Site type filter. Common codes: \"ST\" (stream), \"GW\" (groundwater well), \"LK\" (lake/reservoir), \"SP\" (spring), \"AT\" (atmosphere), \"OC\" (ocean), \"ES\" (estuary). Comma-separate multiple types (e.g. \"ST,GW\").",
      "type": "string"
    },
    "parameterCd": {
      "description": "5-digit parameter code to require at each returned site (e.g. \"00060\" for discharge). Use water_list_parameters to discover codes. Comma-separate multiple codes with no spaces (e.g. \"00060,00065\").",
      "type": "string",
      "pattern": "^\\d{5}(,\\d{5})*$"
    },
    "hasDataTypeCd": {
      "description": "Require sites with data of this type. Common values: \"iv\" (real-time/instantaneous), \"dv\" (daily values), \"gw\" (groundwater). Comma-separate multiple types.",
      "type": "string"
    },
    "siteOutput": {
      "default": "basic",
      "description": "\"basic\" returns core identification fields. \"expanded\" adds drainage area, altitude, contributing area, and other metadata.",
      "type": "string",
      "enum": [
        "basic",
        "expanded"
      ]
    },
    "canvas_id": {
      "description": "Canvas ID from a prior call to stage the full match set into an existing canvas rather than creating a new one. Applies only when the result is truncated and DataCanvas is enabled. Omit to start a fresh canvas.",
      "type": "string"
    }
  },
  "required": [
    "siteOutput"
  ],
  "additionalProperties": false
}
view source ↗

water_get_readings

open-world

Get the latest instantaneous (~15-min, real-time) values for up to 100 USGS sites in one call — per-site, per-parameter records with timestamp, value, unit, and provisional/approved qualifiers. Each series returns only its 10 most recent records (totalValues reports the true count; truncated=true if any were capped); use water_get_series for a full date-range series. Sites NWIS returns nothing for are listed in missingSites, not dropped silently. Use water_find_sites first to discover site numbers and available parameters.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "water_get_readings",
    "arguments": {
      "sites": "<sites>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "sites": {
      "minItems": 1,
      "maxItems": 100,
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^\\d{8,15}$",
        "description": "A USGS site number (8–15 digits, e.g. \"01646500\")."
      },
      "description": "One or more USGS site numbers to query. Maximum 100 per call."
    },
    "parameterCd": {
      "description": "Parameter codes to return. Omit to get all parameters available at each site. Use water_list_parameters to discover codes.",
      "type": "array",
      "items": {
        "type": "string",
        "pattern": "^\\d{5}$",
        "description": "A 5-digit USGS parameter code (e.g. \"00060\" for discharge)."
      }
    },
    "period": {
      "default": "PT2H",
      "description": "ISO 8601 duration for the lookback period (e.g. \"PT2H\" = last 2 hours, \"P1D\" = last 1 day, \"P7D\" = last 7 days). Default: \"PT2H\" (last 2 hours of readings). Widening it raises totalValues, but each series still returns only its 10 most recent records — use water_get_series to retrieve a full series.",
      "type": "string",
      "pattern": "^P(?!$)(\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)?(T(?=\\d)(\\d+H)?(\\d+M)?(\\d+(\\.\\d+)?S)?)?$"
    }
  },
  "required": [
    "sites",
    "period"
  ],
  "additionalProperties": false
}
view source ↗

water_get_series

open-world

Get a daily or instantaneous time series for one USGS site and parameter over a date range, as time-ordered value records. Large sets (>500 records) return the most recent 500 with truncated=true; with DataCanvas enabled they instead spill to a canvas (canvas_id/table_name) for SQL via water_dataframe_query. Use water_find_sites and water_list_parameters to resolve inputs.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "water_get_series",
    "arguments": {
      "site": "<site>",
      "parameterCd": "<parameterCd>",
      "startDate": "<startDate>",
      "endDate": "<endDate>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "site": {
      "type": "string",
      "pattern": "^\\d{8,15}$",
      "description": "USGS site number (8–15 digits, e.g. \"01646500\" for Potomac River at Little Falls). Use water_find_sites to discover valid site numbers."
    },
    "parameterCd": {
      "type": "string",
      "pattern": "^\\d{5}$",
      "description": "A single 5-digit USGS parameter code (e.g. \"00060\" for discharge, \"00065\" for gage height). One code per call — this tool returns one series. Use water_list_parameters to discover available codes."
    },
    "startDate": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "Start date in YYYY-MM-DD format (e.g. \"2024-01-01\")."
    },
    "endDate": {
      "type": "string",
      "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
      "description": "End date in YYYY-MM-DD format (e.g. \"2024-12-31\")."
    },
    "seriesType": {
      "default": "daily",
      "description": "\"daily\" returns one value per day (DV service, typically mean/max/min). \"instantaneous\" returns ~15-minute readings (IV service). Default: \"daily\". Use \"instantaneous\" for high-resolution analysis.",
      "type": "string",
      "enum": [
        "daily",
        "instantaneous"
      ]
    },
    "canvas_id": {
      "description": "Canvas ID from a prior water_get_series call to append data to an existing canvas rather than creating a new one. Omit to start a fresh canvas.",
      "type": "string"
    }
  },
  "required": [
    "site",
    "parameterCd",
    "startDate",
    "endDate",
    "seriesType"
  ],
  "additionalProperties": false
}
view source ↗

water_get_conditions

open-world

Get a USGS site's current reading ranked against its full period-of-record daily-mean percentiles for the same calendar day — a "how unusual is this" percentileClass (record-high to record-low), not a flood-stage or drought determination (this tool fetches no authoritative thresholds). The reading is instantaneous but the percentiles are daily-mean, so the ranking is approximate (see historicalContext.comparisonBasis). When the record is too short to rank, returns the reading with historicalContext=null instead of an error. Use water_find_sites and water_list_parameters to resolve inputs.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "water_get_conditions",
    "arguments": {
      "site": "<site>",
      "parameterCd": "<parameterCd>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "site": {
      "type": "string",
      "pattern": "^\\d{8,15}$",
      "description": "USGS site number (8–15 digits, e.g. \"01646500\" for Potomac River at Little Falls). Use water_find_sites to discover valid site numbers."
    },
    "parameterCd": {
      "type": "string",
      "pattern": "^\\d{5}$",
      "description": "5-digit USGS parameter code (e.g. \"00060\" for discharge, \"00065\" for gage height). Use water_list_parameters to discover codes."
    }
  },
  "required": [
    "site",
    "parameterCd"
  ],
  "additionalProperties": false
}
view source ↗

water_dataframe_query

Run a read-only SQL SELECT against water data tables staged on a DataCanvas by water_get_series or water_find_sites. Workflow: run water_get_series or water_find_sites (get canvas_id + table_name) → water_dataframe_describe (confirm the table and its columns) → water_dataframe_query (SQL analysis). Only SELECT statements are permitted. At most 10,000 rows are returned; a query matching more is capped and the response sets truncated=true — scope with WHERE/LIMIT, and use SELECT COUNT(*) or water_dataframe_describe to learn the true match count. Requires DataCanvas to be enabled on this server instance. Returns an error if DataCanvas is not available.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "water_dataframe_query",
    "arguments": {
      "canvas_id": "<canvas_id>",
      "sql": "<sql>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "canvas_id": {
      "type": "string",
      "description": "Canvas ID returned by water_get_series or water_find_sites. Identifies the canvas holding the data."
    },
    "sql": {
      "type": "string",
      "description": "Read-only SELECT statement. Reference the table by the table_name from water_get_series or water_find_sites; columns vary by source table, so run water_dataframe_describe first for the exact schema. Example: SELECT date_time, value FROM water_series_01646500_00060 ORDER BY date_time DESC LIMIT 10"
    }
  },
  "required": [
    "canvas_id",
    "sql"
  ],
  "additionalProperties": false
}
view source ↗

water_dataframe_describe

List tables and columns staged on a DataCanvas by water_get_series or water_find_sites. Call this after water_get_series or water_find_sites returns a canvas_id to discover the exact table name and column types before writing a query. Then pass the table name to water_dataframe_query. Requires DataCanvas to be enabled on this server instance. Returns an error if DataCanvas is not available.

read
invocation
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "water_dataframe_describe",
    "arguments": {
      "canvas_id": "<canvas_id>"
    }
  }
}
schema
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "canvas_id": {
      "type": "string",
      "description": "Canvas ID returned by water_get_series or water_find_sites. Identifies the canvas to describe."
    }
  },
  "required": [
    "canvas_id"
  ],
  "additionalProperties": false
}
view source ↗

Resources

2

Site metadata for a USGS monitoring site: name, coordinates, type, HUC watershed code, state, county, drainage area, and altitude. Use water_find_sites to discover site numbers.

uri usgs-water://site/{siteId} mime application/json

Full USGS parameter code catalog — injectable context for clients that support resources. Lists well-known parameter codes with human-readable names, units, and thematic domain. The same data is also available via the water_list_parameters tool.

uri usgs-water://parameters mime application/json