EPANET MCP Server
# EPANET MCP Server
An [MCP](https://modelcontextprotocol.io) server that puts the [EPANET](https://www.epa.gov/water-research/epanet) hydraulic/water-quality engine in front of AI assistants (Claude Desktop, Cursor, VS Code Copilot, Claude Code, …) over stdio — no cloud round-trip, no browser.
It lets an AI assistant load an EPANET `.inp`/`.net` model, inspect it, run simulations, read per-node/per-link results, and make validated edits, all locally.
## Tools
- **Network I/O** — `load_network`, `save_network`, `list_networks`
- **Inspection** — `get_network_summary`, `get_nodes`, `get_links`, `get_coordinates`
- **Simulation** — `run_simulation`, `get_node_results`, `get_link_results`
- **Mutation** (engine-validated, results-invalidating) — `set_pipe_diameter`, `set_junction_demand`, `set_pump_speed`, `set_valve_setting`, `set_node_elevation`, `set_demand_pattern`, `add_tank`, `remove_tank`, `add_valve`
- **Model building** — `create_network`, `assign_demands`, `sample_elevations`, `fetch_road_network`, `generate_network_from_bbox`
- **Design helpers** — `lookup_pipe_diameters`, `recommend_diameter`, `friction_loss`, `calculate_minor_loss`, `pipe_sizing_wizard`, `pump_selection`, `list_fitting_kfactors`
- **Optimization** — `optimize_network`, `run_candidate`
`generate_network_from_bbox` auto-lays out a junction/pipe skeleton from real OpenStreetMap road data for a bounding box, with a reservoir placed at the nearest waterway and ground elevations sampled from a bundled IFSAR 10 m DEM.
## Prerequisites
| Requirement | Version |
|---|---|
| Node.js | 20.x or 21.x |
| OS | Windows, macOS, or Linux — transport is stdio, no network ports opened |
## Install
```sh
npm install
```
## Run locally
The data directory the server reads from (`load_network`) and writes to (`save_network`) is **path-contained** — the server refuses any file outside it.
```sh
mkdir -p data
cp your-model.inp data/
export EPANET_DATA_DIR=$(pwd)/data
npm start
```
The server boots and waits for a client on stdio.
## Wire it to an AI app
### Claude Desktop
Edit `claude_desktop_config.json` (`%APPDATA%\Claude\claude_desktop_config.json` on Windows, `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
```json
{
"mcpServers": {
"epanet": {
"command": "npm",
"args": ["start"],
"cwd": "<absolute path to this repo>",
"env": {
"EPANET_DATA_DIR": "<absolute path to this repo>/data"
}
}
}
}
```
### Cursor / VS Code
Same shape works in Cursor (`%APPDATA%\Cursor\User\globalStorage\mcp.json`) or VS Code (`%APPDATA%\Code\User\mcp.json`) — `command: "npm"`, same `args`/`env`.
## Configuration
The server reads these environment variables:
| Env var | Default | Purpose |
|---|---|---|
| `EPANET_DATA_DIR` | cwd | Folder `load_network`/`save_network` may read/write |
| `EPANET_DEM_DIR` | unset | Folder the DEM/elevation tools read from |
| `EPANET_AUTH_PUBLIC_KEY` | unset | RS256 public key for optional token-mode auth |
| `EPANET_AUTH_VALIDATE_URL` | unset | Loopback URL for token validation |
| `EPANET_AUTH_ISSUER` | unset | Expected JWT `iss` |
| `EPANET_AUTH_AUDIENCE` | unset | Expected JWT `aud` |
| `EPANET_AUTH_TOKEN` | unset | Static bearer token (CI use) |
| `EPANET_AUTH_TOKEN_FILE` | unset | Path to a file containing the bearer token |
Auth is off by default; set the `EPANET_AUTH_*` variables to gate the server behind an RS256 bearer token.
## Tests
```sh
npm test
```
Unit tests drive the in-memory MCP transport against committed `.inp` fixtures — they don't require a running server.
## Build a packaged installer
```sh
npm run package:win # or package:mac / package:linux
```
Produces a self-contained installer (vendored Node runtime, no system Node required) under `dist/`.
## License
MIT — see [LICENSE](./LICENSE).
TDQS
Scored across 33 tools
Each tool has a clearly distinct purpose, even within the design/sizing cluster (e.g., recommend_diameter vs. pipe_sizing_wizard, friction_loss vs. calculate_minor_loss). The set_* tools target different properties, and generate_network_from_bbox vs. fetch_road_network are separate phases. No two tools appear to do the same thing.
The majority of tools follow the verb_noun snake_case pattern (load_network, run_simulation, set_pipe_diameter). A few tools break this pattern with noun-first names (friction_loss, pipe_sizing_wizard, pump_selection), but the overall style is consistent and readable.
33 tools is on the high side, but the server covers a comprehensive range: network lifecycle, simulation, editing, optimization, GIS integration, and hydraulic design. Each tool is justified by the domain's breadth, so the count feels appropriate rather than excessive.
The tool surface is remarkably complete: create/load/save/list networks, run simulations, inspect and edit all key elements, add/remove tanks/valves, optimize, and a full set of design tools (diameter, friction, pump sizing, minor losses, GIS sampling). No obvious dead ends; only a delete_network is missing, which is a minor gap.