@cyanheads/usgs-water-mcp-server
This server has a remotely hosted endpoint. Open its connector listing for connection details, authentication requirements, and setup instructions:
Public Hosted Server: https://usgs-water.caseyjhand.com/mcp
Overview
Real-time and historical water data from ~8,000 active USGS stream gages and groundwater wells across the US and territories. Find monitoring sites, pull the latest readings or a historical series, and see how today's flow compares with decades of records.
Tools
Tool | Description |
| List well-known USGS parameter codes with names, units, and domain (no network call), or search the full ~19,600-code USGS catalog by name or description. |
| Find USGS monitoring sites by bounding box, state, county, or HUC watershed. Filter by site type and parameter availability. Large match sets spill to DataCanvas. |
| Get the latest instantaneous values (~15 min real-time) for up to 100 USGS sites. |
| Get a time series of daily or instantaneous values for a site over a date range. Large ranges spill to DataCanvas. |
| Discover a site's parameters, sensors, statistics, and full UTC record bounds, with cursor pagination. |
| Get current hydrologic conditions ranked against the full period-of-record percentile statistics. |
| List tables and columns staged on a DataCanvas by |
| Run a read-only SQL SELECT against the time-series and site tables staged by |
| Drop one staged table from a DataCanvas, leaving the canvas and its other tables in place. Opt-in via |
Resources
Resource | Description |
| Site metadata: name, coordinates, type, HUC, state, county, drainage area, and altitude |
| Curated well-known parameter codes (the list |
All resource data is also reachable via tools. Use water_find_sites for geographic site discovery.
Skill
Skill | Description |
| Gates and workflows for agents calling the tools: telling live gages from discontinued ones, reading depth-to-water rankings (they run inverted), finding the parameter code a site reports, time zones and data lag, and SQL over staged series |
Ships in skills/ for plugin installs, the npm package, the Docker image, and the .mcpb bundle, and is served over MCP as skill://usgs-water-guide/SKILL.md.
Related MCP server: nws-weather-usgs-water-mcp
Capability reference
water_list_parameters tool
Without
query: the curated, built-in list — no network call — of codes like00060(Discharge, ft³/s),00065(Gage height, ft), and72019(Depth to water level, ft), filtered bygroup(streamflow,groundwater,temperature,meteorological,water-quality, orall, the default)With
query: searches the full ~19,600-code USGS catalog by name or description (a bare 5-digit code returns that code) and returns at most 25 entries, each naming itssource(curatedorusgs-catalog), withtotalandtruncatedcovering the full match set.querycan't be combined with agroupother thanallThe catalog is fetched on the first query and cached for 24 hours; a failed fetch fails as a retryable
upstream_error
water_find_sites tool
Exactly one geographic scope per call —
bbox("west,south,east,north"), 2-letterstateCd, up to 20 comma-separated 5-digit FIPScountyCdcodes, or a 2- or 8-digithuc— narrowed bysiteType(ST,GW,LK,SP, …),parameterCd, andhasDataTypeCd(iv/dv/gw). A missing or doubled scope fails asmissing_major_filter/conflicting_major_filtersbefore any NWIS calllimit(1–500, default 500) andoffsetpage through the match set, withtruncatedandupstreamTotaldescribing what lies outside the window;siteOutput: expandedadds drainage and contributing areaWith
CANVAS_PROVIDER_TYPE=duckdb, a match set over 500 sites also stages in full to a canvas (canvas_id/table_name) forwater_dataframe_query
water_get_readings tool
Up to 100
sitesper call, an optionalparameterCdfilter, and aperiodlookback (ISO 8601, defaultPT2H); returns one series per site, parameter, and sensor method (methodId/methodDescription), capped at 100 series per call (kept round-robin across sites) and the 10 most recent records per series, withtotalSeries,totalValues, andtruncatedreporting what the caps held backEvery value carries its qualifier codes (
Pprovisional,Aapproved), and sites NWIS returned nothing for are named inmissingSites
water_get_series tool
One
siteandparameterCdper call over astartDate–endDaterange;seriesTypeisdaily(one value per day, the default) orinstantaneous(~15 min). The most recent 500 records return inline, withtotalRecordsandtruncatedreporting the restReturns one named series — by default the daily mean and the sensor method with the most records;
statCd(00003mean,00001maximum,00002minimum; daily only) andmethodIdselect another, andotherSerieslists every alternative with its record countWith
CANVAS_PROVIDER_TYPE=duckdb, a range over 500 records spills the complete series to a canvas (canvas_id/table_name); pass a priorcanvas_idto add the table to an existing canvas
water_get_site_data_catalog tool
One
site, optionalparameterCdandseriesType, andlimit(1–1000; default 100). Reuse the same filters and limit withnextCursoruntil it is null. An unknown site fails asnot_found; a known site with no matching metadata returnsseries: []Each row preserves its OGC
timeSeriesId, parameter name/unit, nullable statistic, sensor description, raw computation fields, and full UTCbeginDate/endDate.Dailymaps todaily;PointswithInstantaneousmaps toinstantaneous; other computations have nullseriesTypeFor
water_get_series, take YYYY-MM-DD from the bounds, passstatCdonly for daily rows, and selectmethodIdfrom the series response, never an OGC ID. Bounds may contain gaps and lag observations; null bounds remain unknownSite identity and metadata share a 45-second deadline including retries. Malformed or rejected continuation cursors fail as
invalid_cursor; restart without cursor
water_get_conditions tool
One
siteandparameterCd; ranks the current instantaneous reading against the period-of-record daily-mean percentiles for its calendar day as apercentileClass—record-high(≥p95),above-normal,normal,below-normal,low,record-low(<p05), orunknownfor a no-data reading — withpercentileLabelstating the threshold in plain language andcomparisonBasisnoting that the ranking is approximate, not a flood-stage or drought determinationWhen percentiles can't be applied, the reading still returns with
historicalContext: nulland ahistoricalContextStatusofno_record,no_matching_day,no_matching_method, orunavailable(a transient stat-service failure); on a multi-sensor site,methodIdandmethodMatchedname the sensor read and whether the percentiles came from its own series
water_dataframe_describe tool
Lists the tables staged on a canvas by
water_get_seriesorwater_find_sites, with each column's name, DuckDB type, and nullability — call it beforewater_dataframe_queryorwater_dataframe_dropto confirm the exact namesrow_countis a DuckDB estimate and may differ from the exact count
water_dataframe_query tool
A single read-only
SELECTagainst the staged tables; other statements, system-catalog access (information_schema,pg_catalog,duckdb_*), engine-setting functions, dynamic SQL, and file references are rejected.query()accepts a single SQL string literal, checked under the same restrictionsCapped at 10,000 rows per query;
truncated: truesignals more matched — narrow withWHERE/LIMIT, or runSELECT COUNT(*)for the true total
water_dataframe_drop tool
Removes one staged table by
table_name(matched case-insensitively) from the canvas named bycanvas_id; the canvas and its other tables stay, and the response reports the dropped table'srow_countand theremaining_tablesA name not staged on the canvas fails as
table_not_foundand changes nothing, so a repeated drop is safeOpt-in: callable only with
WATER_DATAFRAME_DROP_ENABLED=true; otherwise it appears on the landing page as a disabled tool and is absent fromtools/list. Like the other dataframe tools, it needsCANVAS_PROVIDER_TYPE=duckdb
usgs-water://site/{siteId} resource
Returns
application/jsonsite metadata: name, coordinates, type, HUC watershed code, state, county, drainage area, and altitudesiteIdis an 8–15 digit USGS site number — discover one viawater_find_sites
usgs-water://parameters resource
The curated table of well-known parameter codes as
application/json— the listwater_list_parametersreturns without a queryTakes no parameters; a curated subset of the USGS catalog — search the rest with
water_list_parametersquery
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
USGS Water-specific:
Wraps NWIS IV (instantaneous), DV (daily), site, and stat endpoints plus USGS Water Data parameter codes, monitoring locations, and time-series metadata — no API key required, fully public
Input formats are checked at the edge against what NWIS actually accepts — site numbers, parameter codes, ISO 8601 periods, HUC, FIPS county, state, and bbox each carry a validated pattern advertised in the tool's JSON Schema
HTML error detection: NWIS returns 400 with an HTML body for bad input, and the service layer extracts NWIS's own message and maps it to a typed failure
DataCanvas spillover:
water_get_series(long date ranges) andwater_find_sites(match sets past the 500-site cap) stage the full result as a DuckDB-backed table, queryable viawater_dataframe_query; the spillover and the dataframe tools requireCANVAS_PROVIDER_TYPE=duckdb, andwater_dataframe_dropalso needsWATER_DATAFRAME_DROP_ENABLED=trueNo-data readings stay no-data: a value NWIS reports as
-999999(a seasonal, discontinued, dry, or malfunctioning gage) returns as an emptyvaluewith qualifiers naming why (Ssn,Dis,Dry,Eqp) — never as a magnitude — and stages asNULLon a canvasGroundwater depth reads through the standard IV service via parameter
72019— the legacygwlevelsendpoint was decommissioned November 2025
Agent-friendly output:
Percentile classification:
water_get_conditionsreturns apercentileClasscallers can act on directly, paired with apercentileLabelthat states the threshold in plain languagePartial success over hard failure:
water_get_readingsreturns the series it got and names the rest inmissingSites;water_get_conditionsseparates an empty stat table (no_record/no_matching_day) from a failed stat call (unavailable, transient) instead of collapsing both into one errorTruncation signals: every capped response (
water_get_series,water_find_sites,water_get_readings) reports its own count and atruncatedflag, pluscanvas_id/table_namewhen the rest is retrievable via SQLStructured content and rendered text agree — every cap and count a tool applies appears identically in
structuredContentand in the markdown
Getting started
Connect to the hosted instance, no install:
claude mcp add --transport http usgs-water-mcp-server https://usgs-water.caseyjhand.com/mcpOr run it locally:
claude mcp add usgs-water-mcp-server -- npx -y @cyanheads/usgs-water-mcp-serverFor other clients, add this to the MCP config file:
{
"mcpServers": {
"usgs-water-mcp-server": {
"command": "npx",
"args": ["-y", "@cyanheads/usgs-water-mcp-server"]
}
}
}DataCanvas is enabled by default for SQL analytics over large result sets; set CANVAS_PROVIDER_TYPE=none to return inline previews only. No API key is required — USGS NWIS is a free, public API. For Docker or a source build, see Running the server.
Configuration
Variable | Description | Default |
| DataCanvas spillover for large results from |
|
| Custom User-Agent string sent to USGS NWIS and OGC discovery endpoints. USGS requests a descriptive User-Agent per their terms. |
|
| HTTP request timeout in milliseconds for USGS NWIS and OGC calls. Site-series discovery also has a shared 45-second deadline. |
|
| Set to |
|
| Transport: |
|
| Port for HTTP server. |
|
| HTTP session mode. The server declares |
|
| Auth mode: |
|
| Log level (RFC 5424). |
|
| Directory for log files (Node.js only). |
|
| Log each failed tool call's arguments and result, redacted by key name and capped at |
|
| Enable OpenTelemetry instrumentation (spans, metrics, completion logs). |
|
See .env.example for the full list of optional overrides.
Running the server
Local development
Requires Bun v1.4.0 or higher (or Node.js v24+).
Clone and install:
git clone https://github.com/cyanheads/usgs-water-mcp-server.git cd usgs-water-mcp-server && bun install cp .env.example .env # optional overridesBuild and run:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:httpRun checks and tests:
bun run devcheck # Lint, format, typecheck, security bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
docker run --rm -p 3010:3010 ghcr.io/cyanheads/usgs-water-mcp-server:latestThe image defaults to HTTP transport, stateless session mode, and logs to /var/log/usgs-water-mcp-server. To run it over stdio from an MCP client, use "command": "docker" with "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/usgs-water-mcp-server:latest"]. To build locally, docker build -t usgs-water-mcp-server .; OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
Directory | Purpose |
|
|
| Server-specific environment variable parsing and validation with Zod. |
| Tool definitions ( |
| Resource definitions ( |
| NWIS HTTP client — IV, DV, site, and stat endpoints with HTML error detection. |
| USGS Water Data site-series discovery, parameter-code catalog reader (24-hour cache), and curated parameter table. |
| DataCanvas accessor, caller-supplied |
| Unit and integration tests mirroring |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
Handlers throw, framework catches — no
try/catchin tool logicUse
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storageRegister new tools and resources via the barrels in
src/mcp-server/*/definitions/index.tsWrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run testLicense
Apache-2.0 — see LICENSE for details.
This server cannot be deployed
Maintenance
Related MCP Connectors
USGS Water Services (NWIS) MCP.
USGS Water MCP — wraps USGS National Water Information System (NWIS) REST services (free, no auth)
Real-time water levels and flow rates from USGS stream gauges
Water Quality Portal (WQP) MCP — US ambient water-chemistry monitoring
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceProvides access to real-time water data from the USGS Water Services API, allowing users to fetch instantaneous measurements like stream flow, gage height, temperature, and water quality parameters from thousands of monitoring stations across the US.3-
- AlicenseAqualityAmaintenanceCombines National Weather Service alerts and forecasts with modern USGS water data, enabling weather and hydrology queries through MCP. No API key required, with persistent local caching.29Creative Commons Zero v1.0 Universal
- AlicenseAqualityCmaintenanceEnables querying USGS water data including real-time and historical streamflow, gage height, and water temperature from USGS gauges across the United States.3MIT
- AlicenseAqualityCmaintenanceProvides AI agents live access to USGS stream flow, gage height, and water temperature data, along with EPA water quality results ranked against compliance thresholds, and composite basin health scores.3MIT