io.github.cyanheads/usgs-water-mcp-server
Query real-time and historical USGS water data from ~8,000 stream gages and groundwater wells.
ماذا يمكنك أن تفعل مع io.github.cyanheads/usgs-water-mcp-server؟
- 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 require `CANVA... - No-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 c... - Groundwater depth reads through the standard IV service via parameter
72019— the legacygwlevelsendpoint was decommissioned November 2025 - Percentile classification:
water_get_conditionsreturns apercentileClasscallers can act on directly, paired with apercentileLabelthat states the threshold in plain language - Partial 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)...
التثبيت
أضف هذا إلى إعدادات MCP في عميلك (Claude Desktop: claude_desktop_config.json، وCursor: .cursor/mcp.json، ويستخدم VS Code وغيره الصيغة نفسها «mcpServers»).
claude mcp add --transport http usgs-water-mcp-server 'https://usgs-water.caseyjhand.com/mcp'
{
"mcpServers": {
"usgs-water-mcp-server": {
"type": "http",
"url": "https://usgs-water.caseyjhand.com/mcp"
}
}
}
@cyanheads/usgs-water-mcp-serverclaude mcp add usgs-water-mcp-server -e 'USGS_USER_AGENT=<USGS_USER_AGENT>' -e 'USGS_REQUEST_TIMEOUT_MS=30000' -e 'WATER_DATAFRAME_DROP_ENABLED=false' -e 'MCP_LOG_LEVEL=info' -- npx -y @cyanheads/usgs-water-mcp-server@0.5.0
{
"mcpServers": {
"usgs-water-mcp-server": {
"command": "npx",
"args": [
"-y",
"@cyanheads/usgs-water-mcp-server@0.5.0"
],
"env": {
"USGS_USER_AGENT": "<USGS_USER_AGENT>",
"USGS_REQUEST_TIMEOUT_MS": "30000",
"WATER_DATAFRAME_DROP_ENABLED": "false",
"MCP_LOG_LEVEL": "info"
}
}
}
}
متغيرات البيئة
USGS_USER_AGENTCustom User-Agent string for USGS NWIS and OGC discovery endpoints. Defaults to a descriptive identifier including the repo URL.USGS_REQUEST_TIMEOUT_MSHTTP request timeout in milliseconds for USGS NWIS and OGC calls. Site-series discovery also has a shared 45-second deadline. Default: 30000.WATER_DATAFRAME_DROP_ENABLEDSet to true to make water_dataframe_drop callable — it removes one staged table from a DataCanvas. Off by default, which lists the tool as disabled. Needs CANVAS_PROVIDER_TYPE=duckdb to have anything to drop.MCP_LOG_LEVELSets the minimum log level for output (e.g., 'debug', 'info', 'warn').
@cyanheads/usgs-water-mcp-serverclaude mcp add usgs-water-mcp-server -e 'MCP_TRANSPORT_TYPE=<MCP_TRANSPORT_TYPE>' -e 'USGS_USER_AGENT=<USGS_USER_AGENT>' -e 'USGS_REQUEST_TIMEOUT_MS=30000' -e 'WATER_DATAFRAME_DROP_ENABLED=false' -e 'MCP_HTTP_HOST=127.0.0.1' -e 'MCP_HTTP_PORT=3010' -e 'MCP_HTTP_ENDPOINT_PATH=/mcp' -e 'MCP_AUTH_MODE=none' -e 'MCP_LOG_LEVEL=info' -- npx -y @cyanheads/usgs-water-mcp-server@0.5.0
{
"mcpServers": {
"usgs-water-mcp-server": {
"command": "npx",
"args": [
"-y",
"@cyanheads/usgs-water-mcp-server@0.5.0"
],
"env": {
"MCP_TRANSPORT_TYPE": "<MCP_TRANSPORT_TYPE>",
"USGS_USER_AGENT": "<USGS_USER_AGENT>",
"USGS_REQUEST_TIMEOUT_MS": "30000",
"WATER_DATAFRAME_DROP_ENABLED": "false",
"MCP_HTTP_HOST": "127.0.0.1",
"MCP_HTTP_PORT": "3010",
"MCP_HTTP_ENDPOINT_PATH": "/mcp",
"MCP_AUTH_MODE": "none",
"MCP_LOG_LEVEL": "info"
}
}
}
}
متغيرات البيئة
MCP_TRANSPORT_TYPESelects the HTTP transport.USGS_USER_AGENTCustom User-Agent string for USGS NWIS and OGC discovery endpoints. Defaults to a descriptive identifier including the repo URL.USGS_REQUEST_TIMEOUT_MSHTTP request timeout in milliseconds for USGS NWIS and OGC calls. Site-series discovery also has a shared 45-second deadline. Default: 30000.WATER_DATAFRAME_DROP_ENABLEDSet to true to make water_dataframe_drop callable — it removes one staged table from a DataCanvas. Off by default, which lists the tool as disabled. Needs CANVAS_PROVIDER_TYPE=duckdb to have anything to drop.MCP_HTTP_HOSTThe hostname for the HTTP server.MCP_HTTP_PORTThe port to run the HTTP server on.MCP_HTTP_ENDPOINT_PATHThe endpoint path for the MCP server.MCP_AUTH_MODEAuthentication mode to use: 'none', 'jwt', or 'oauth'.MCP_LOG_LEVELSets the minimum log level for output (e.g., 'debug', 'info', 'warn').
التوثيق
مأخوذ من ملف README للمشروع على GitHub ويُحدَّث أسبوعيًا. تفتح الروابط على GitHub.
المحتويات
@cyanheads/usgs-water-mcp-server
Query real-time and historical water data from ~8,000 USGS stream gages and groundwater wells via MCP. STDIO or Streamable HTTP.
9 Tools • 2 Resources
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 |
|---|---|
water_list_parameters | 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. |
water_find_sites | 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. |
water_get_readings | Get the latest instantaneous values (~15 min real-time) for up to 100 USGS sites. |
water_get_series | Get a time series of daily or instantaneous values for a site over a date range. Large ranges spill to DataCanvas. |
water_get_site_data_catalog | Discover a site's parameters, sensors, statistics, and full UTC record bounds, with cursor pagination. |
water_get_conditions | Get current hydrologic conditions ranked against the full period-of-record percentile statistics. |
water_dataframe_describe | List tables and columns staged on a DataCanvas by water_get_series or water_find_sites. |
water_dataframe_query | Run a read-only SQL SELECT against the time-series and site tables staged by water_get_series and water_find_sites. |
water_dataframe_drop | Drop one staged table from a DataCanvas, leaving the canvas and its other tables in place. Opt-in via WATER_DATAFRAME_DROP_ENABLED. |
Resources
| Resource | Description |
|---|---|
usgs-water://site/{siteId} | Site metadata: name, coordinates, type, HUC, state, county, drainage area, and altitude |
usgs-water://parameters | Curated well-known parameter codes (the list water_list_parameters returns without a query) |
All resource data is also reachable via tools. Use water_find_sites for geographic site discovery.
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 thanall - The 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 call limit(1–500, default 500) andoffsetpage through the match set, withtruncatedandupstreamTotaldescribing what lies outside the window;siteOutput: expandedadds drainage and contributing area- With
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 back - Every 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 rest - Returns 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 count - With
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 nullseriesType - For
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 unknown - Site 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 determination - When 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 names row_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 restrictions - Capped 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_tables - A name not staged on the canvas fails as
table_not_foundand changes nothing, so a repeated drop is safe - Opt-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 altitude siteIdis 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 query - Takes 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=true - No-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 canvas - Groundwater 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 language - Partial 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 error - Truncation 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 SQL - Structured content and rendered text agree — every cap and count a tool applies appears identically in
structuredContentand in the markdown
Getting started
Public Hosted Instance
A public instance is available at https://usgs-water.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"usgs-water-mcp-server": {
"type": "streamable-http",
"url": "https://usgs-water.caseyjhand.com/mcp"
}
}
}
Self-Hosted / Local
Add the following to your MCP client configuration file.
{
"mcpServers": {
"usgs-water-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/usgs-water-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"usgs-water-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/usgs-water-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"usgs-water-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"ghcr.io/cyanheads/usgs-water-mcp-server:latest"
]
}
}
}
DataCanvas is enabled by default for SQL analytics over large result sets. Set CANVAS_PROVIDER_TYPE=none to return inline previews only. The portable Claude Desktop bundle disables DataCanvas because it omits platform-specific DuckDB binaries.
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
Prerequisites
- Bun v1.4.0 or higher (or Node.js v24+).
- No API key required — USGS NWIS is a free, public API.
Installation
- Clone the repository:
git clone https://github.com/cyanheads/usgs-water-mcp-server.git
- Navigate into the directory:
cd usgs-water-mcp-server
- Install dependencies:
bun install
- Configure environment:
cp .env.example .env
# Edit .env to set any optional overrides
Configuration
| Variable | Description | Default |
|---|---|---|
CANVAS_PROVIDER_TYPE | DataCanvas spillover for large results from water_get_series and water_find_sites; set to none to disable. The portable desktop bundle sets none. | duckdb |
USGS_USER_AGENT | Custom User-Agent string sent to USGS NWIS and OGC discovery endpoints. USGS requests a descriptive User-Agent per their terms. | usgs-water-mcp-server/0.4.0 (contact: https://github.com/cyanheads/usgs-water-mcp-server) |
USGS_REQUEST_TIMEOUT_MS | HTTP request timeout in milliseconds for USGS NWIS and OGC calls. Site-series discovery also has a shared 45-second deadline. | 30000 |
WATER_DATAFRAME_DROP_ENABLED | Set to true to make water_dataframe_drop callable. Off, the tool is listed as disabled. Needs CANVAS_PROVIDER_TYPE=duckdb to have anything to drop. | false |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | Port for HTTP server. | 3010 |
MCP_SESSION_MODE | HTTP session mode. The server declares stateless in code, matching .env.example and the Docker runtime; setting this overrides that, and auto resolves to stateful. | stateless |
MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (RFC 5424). | info |
LOGS_DIR | Directory for log files (Node.js only). | <project-root>/logs |
LOG_TOOL_FAILURE_PAYLOADS | Log each failed tool call's arguments and result, redacted by key name and capped at LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES (default 16384). A secret inside a free-form value is not redacted. | false |
OTEL_ENABLED | Enable OpenTelemetry instrumentation (spans, metrics, completion logs). | false |
See .env.example for the full list of optional overrides.
Running the server
Local development
- Build and run:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:http - Run 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 build -t usgs-water-mcp-server .
docker run --rm -p 3010:3010 usgs-water-mcp-server
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/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 |
|---|---|
src/index.ts | createApp() entry point — registers tools/resources and inits services. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). |
src/mcp-server/resources | Resource definitions (*.resource.ts). |
src/services/nwis | NWIS HTTP client — IV, DV, site, and stat endpoints with HTML error detection. |
src/services/waterdata | USGS Water Data site-series discovery, parameter-code catalog reader (24-hour cache), and curated parameter table. |
src/services/canvas | DataCanvas accessor, caller-supplied canvas_id resolution, and deterministic table names for DuckDB-backed spillover. |
tests/ | Unit and integration tests mirroring src/. |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catchin tool logic - Use
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storage - Register new tools and resources via the barrels in
src/mcp-server/*/definitions/index.ts - Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
License
Apache-2.0 — see LICENSE for details.
الأسئلة الشائعة
ما هو io.github.cyanheads/usgs-water-mcp-server؟
Query real-time and historical USGS water data from ~8,000 stream gages and groundwater wells.
كيف أثبّت io.github.cyanheads/usgs-water-mcp-server؟
يعمل كخادم بعيد. في Claude Code: claude mcp add --transport http usgs-water-mcp-server 'https://usgs-water.caseyjhand.com/mcp'. وفي العملاء الآخرين، الصق إعداد JSON من هذه الصفحة في إعدادات MCP الخاصة بهم.
هل يحتاج io.github.cyanheads/usgs-water-mcp-server إلى مفتاح API؟
لا يذكر إدراجه في السجل أي مفتاح أو سر إلزامي. قد تتطلب بعض الميزات حسابًا لدى الخدمة التي يتصل بها.
هل io.github.cyanheads/usgs-water-mcp-server مفتوح المصدر؟
نعم. شيفرته متاحة على GitHub بترخيص Apache-2.0.
من يصون io.github.cyanheads/usgs-water-mcp-server؟
ينشره cyanheads. يدرجه AI.tn لكنه لا يصونه ولا يوصي به.
من سجل MCP الرسمي. الإدراج ليس توصية: راجع شيفرة الخادم وصلاحياته قبل منحه الوصول إلى بياناتك.