river-watershed-mcp
# river-watershed-mcp
An MCP (Model Context Protocol) server that gives AI agents **live** access to
bioregional river and watershed data, pulled directly from public U.S.
federal APIs:
- **USGS NWIS** (National Water Information System) — real-time stream flow, gage height, water temperature
- **EPA WQP** (Water Quality Portal) — water-quality results ranked against EPA-style compliance thresholds
Built for the "Encrypt/access the river data" bioregional sensor bounty (#450).
## Tools exposed
| Tool | What it does |
|---|---|
| `get_stream_flow` | Live discharge (cfs), gage height (ft), and water temp for a USGS site number |
| `get_water_quality` | Recent DO, pH, turbidity, temperature, E. coli, nitrate, phosphorus for a WQP site, each ranked `good` / `fair` / `poor` |
| `get_basin_health` | Composite 0–100 score across multiple sites in a basin (70% water-quality compliance, 30% live-flow gauge coverage) |
All three return a common `SensorReading` JSON shape (see `src/schema.ts`):
```ts
{
sensor_id: string;
sensor_type: "stream_flow" | "water_quality" | "basin_health";
location: { lat: number; lon: number; name?: string; huc_code?: string };
parameters: Record<string, { value: number; unit: string; compliance?: "good"|"fair"|"poor"|"unknown" }>;
observed_at: string; // ISO-8601
source: "USGS-NWIS" | "EPA-WQP" | "COMPOSITE";
}
```
**On the "bounty #450 sensor schema":** I wasn't able to retrieve the exact
spec text for that field layout from this build environment's network
access. The schema above follows the common convention for bioregional
sensor-network payloads (typed location + parameters map + provenance +
timestamp). If the bounty spec differs, `toSensorReading` logic lives
entirely in `src/usgs.ts`, `src/epaWqp.ts`, and `src/basinHealth.ts` — remap
field names there.
## Setup
```bash
npm install
npm run build
```
Then point any MCP-compatible client (Claude Desktop, Claude Code, etc.) at:
```json
{
"mcpServers": {
"river-watershed": {
"command": "node",
"args": ["/absolute/path/to/river-watershed-mcp/dist/index.js"]
}
}
}
```
## Live demo
```bash
npm run build
npm run demo
```
`src/demo.ts` acts as an AI agent: it spawns the server over real MCP stdio
transport, lists its tools, then calls all three against real Potomac River
basin sites (USGS 01646500, 01651800).
**Verified vs. not yet verified:** the MCP protocol wiring (tool
registration, JSON-RPC over stdio, request/response shape) was verified live
in this repo's build environment — `listTools()` and `callTool()` both work
end-to-end against the compiled server. The underlying USGS/EPA HTTP calls
were **not** verified from that same environment, because its network
allowlist doesn't include `waterservices.usgs.gov` or
`www.waterqualitydata.us` (confirmed: a live call returns a clean `fetch
failed` `isError: true` result — the server handles it correctly, but no
real river data crossed the wire). Run `npm run demo` on a machine with
normal internet access to see live values.
## Example site numbers to try
- `01646500` — Potomac River near Washington, DC
- `01651800` — Northeast Branch Anacostia River at Riverdale, MD
- `01474500` — Schuylkill River at Philadelphia, PA
Find any USGS site at https://waterdata.usgs.gov/nwis/inventory, and its
matching WQP ID is usually `USGS-<site number>`.
## License
MIT
TDQS
Scored across 3 tools
Each tool targets a distinct data domain: stream flow is live hydrologic measurements, water quality is compliance-ranked sample results, and basin health is a composite score aggregating both. There is no overlap or ambiguity in purpose.
All three tools follow the consistent verb_noun pattern: get_stream_flow, get_water_quality, get_basin_health. The naming is predictable and clearly indicates the resource being accessed.
Three tools is well-scoped for a river watershed monitoring server. Each tool covers a major data category without unnecessary bloat or redundancy.
The core surface covers live flow, water quality, and an integrated health score, which addresses the primary needs of watershed monitoring. Minor gaps exist, such as no dedicated site metadata or historical data retrieval, but these are workarounds via external links and not critical to the main purpose.