mcp-usgs-water-data
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-usgs-water-dataWhat's the flow of the Chattahoochee in Atlanta?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-usgs-water-data
An MCP server that exposes the USGS Instantaneous Values (IV) web service as tools — real-time and historical streamflow, gage height, water temperature, and related measurements from USGS gauges across the United States.
It is built for a language model to use safely. The USGS API is designed for browsers and bulk downloads; feeding its responses straight to a model goes wrong in ways that are easy to miss and hard to notice — a state-wide query returns megabytes of JSON, a "latest value" can be seven years old, and a river with no gauge returns an empty result the model will invent a reason for. This server exists to turn those traps into structured, self-describing answers.
What it does
Three tools:
Tool | Purpose |
| Resolve a place or river name to USGS station numbers. Start here when you know the river but not its 8- or 15-digit site number. |
| Fetch readings for one or more sites. The main tool. |
| Look up the 5-digit USGS code for a measurement (e.g. streamflow = |
The usual flow is find_sites → get_instantaneous_values. A model asking "what's the flow
of the Chattahoochee in Atlanta?" resolves the name to site 02336000, then reads it.
Related MCP server: Colorado DWR MCP Server
Requirements
Node.js 22+ (uses native
fetchwith transparent gzip; no HTTP dependencies).
Install
npm install
npm run build # compiles TypeScript to dist/
npm test # 189 tests, no network access requiredConfigure your MCP client
The server speaks MCP over stdio. Point your client at the built entry file.
Claude Code (project-scoped, committable):
claude mcp add usgs-water --scope project -- node /absolute/path/to/dist/src/index.jsThis writes .mcp.json. Project-scoped servers are never auto-trusted — restart claude
in the project directory and approve the server once when prompted.
Any MCP client, directly:
{
"mcpServers": {
"usgs-water": {
"type": "stdio",
"command": "node",
"args": ["/absolute/path/to/dist/src/index.js"]
}
}
}The config runs
dist/, not the TypeScript source. Runnpm run buildafter any code change, or the server will keep running the old build.
Tools
get_instantaneous_values
Exactly one major filter is required to scope the query — providing zero or more than one is rejected before any network call:
Filter | Type | Notes |
|
| Station numbers, e.g. |
|
| Two-letter state code, e.g. |
|
| 5-digit FIPS codes, e.g. |
|
| Hydrologic Unit Codes: one 2-digit region, or up to ten 8-digit sub-basins. |
|
|
|
Optional narrowing: parameterCd (which measurements), siteType, siteStatus,
modifiedSince, agencyCd.
Time window — pick one, they are mutually exclusive:
(none) — the single latest value per sensor.
period— an ISO-8601 duration ending now, e.g."P7D"(last 7 days),"PT6H"(last 6 hours).startDT/endDT— an absolute range, e.g."2024-01-01"or"2024-01-01T12:00".endDTrequiresstartDT. Data begins 2007-10-01.
Output:
mode—"values"(default; individual readings) or"summary"(count, min, max, mean, first, last). Use"summary"for any window longer than a day — a single site over one year is ~35,000 readings.maxValues— cap on readings per series in"values"mode (default 200, max 1000).
find_sites
Same one-major-filter rule (sites / stateCd / countyCd / huc / bBox), plus:
nameContains— case-insensitive substring match on the station name. Applied client-side after fetching by the major filter, so it narrows the results, not the request. AstateCdquery can return hundreds of sites before filtering.siteType,siteStatus(default"active"),hasDataTypeCd(default"iv"— only sites that report instantaneous values),maxSites(default 50, max 500).
list_common_parameter_codes
No input. Returns the table below.
Code | Measurement | Unit |
| Discharge (streamflow) | ft³/s |
| Gage height | ft |
| Water temperature | °C |
| Specific conductance | µS/cm @25°C |
| Dissolved oxygen | mg/L |
| pH | std units |
| Turbidity | FNU |
| Precipitation | in |
| Depth to water level | ft |
| Lake/reservoir elevation (NGVD 1929) | ft |
| Lake/reservoir elevation (NAVD 1988) | ft |
| Air temperature | °C |
| Barometric pressure | mm Hg |
| Wind speed | mph |
| Wind direction | degrees |
| Relative humidity | % |
| Stream velocity (point) | ft/s |
Reading the output
The response has a top-level series array. Each series carries the site, the variable, the
sensor method, qualifier codes, and either readings or a summary. A few fields exist
specifically to prevent a confidently wrong answer, and a consumer should check them.
On each series:
stale(true/false/null) —truemeans the latest reading is more than 48 hours behind the query's reference time. USGS returns decommissioned sensors in a latest-value query, frozen at their final reading — a temperature sensor switched off in 2019 will otherwise look current.nullmeans staleness could not be evaluated. Check this before reporting any value as current.discontinued— the sensor is marked decommissioned in its metadata. A hint that explains why a series is stale; not itself a freshness signal.truncated/totalValues— set whenmaxValuescapped a series. You are seeing the most recent N, not the whole record.qualifiers — most recent USGS data is provisional (code
P) and subject to revision. These are never stripped.
On the response (not the series):
missingParameterCodes— present when you requested aparameterCda site does not measure. Series are not positionally aligned with the codes you asked for; match on each series'variable.code.note— a plain-language explanation attached to empty results, stale series, or dropped parameters, so an empty or partial answer is never silently ambiguous.
When a response would exceed 256 KB even after shaping, the server returns an error naming
summary mode rather than truncating silently.
Rate limiting
USGS publishes no numeric rate limit but blocks IP addresses it judges to be "seriously impacting others." A single client session can fan out — subagents issuing many concurrent tool calls all route through this one server process on one IP — so the server gates its own outbound traffic: a shared limiter with a concurrency cap, minimum spacing between requests, and a load-shedding valve that returns a "retry shortly" error rather than hanging when a burst overwhelms it. There is deliberately no retry-on-failure loop — retrying into a throttling server is how you earn the block.
Three environment variables, read once at startup:
Variable | Default | Meaning |
|
| Requests in flight at once. |
|
| Minimum gap between request starts (~13/s ceiling). |
|
| Shed a request that would wait longer than this. |
Tune concurrency and spacing up for a purely interactive deployment, down for a
scheduled batch collector. Negative and non-numeric values fall back to the default. Zero
falls back for concurrency and queue-wait (both are degenerate at 0 — a zero concurrency
cap deadlocks, a zero queue-wait sheds every request), but 0 is a valid "no added spacing"
setting for the interval.
The upstream base URLs are not currently configurable; they are constants in src/usgs.ts
and src/sites.ts.
Scope and limits
No site lookup by coordinates-you-assert.
find_sitesresolves names and geographic filters; a bounding box you get wrong will still return an honest (possibly empty) result.Groundwater-only filters (aquifer codes, well/hole depth) are not exposed. Additive if needed.
503is not retried. The gate prevents the burst that causes throttling; it does not retry after one occurs.One documented staleness corner: a future
endDTcombined with a payload missing its request timestamp marks live data as stale. Unobserved in practice, errs toward caution.
Design rationale for every one of these decisions — and the live-service behavior that drove
them — is in DESIGN.md.
Development
npm run build # tsc -> dist/
npm test # build, then node --test (no network)Tests are deterministic and hermetic: no live network calls, no wall-clock or timezone
dependence (the suite passes identically under any TZ). Fixtures under test/fixtures/ are
real captured USGS responses, not synthesized.
License
MIT. USGS water data is in the public domain; this license covers the server code only.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/higherpass/mcp-usgs-water-data'
If you have feedback or need assistance with the MCP directory API, please join our Discord server