Skip to main content
Glama
hjlrosales

EPANET MCP Server

by hjlrosales
README.md
# 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

A4.1/5.0

Scored across 33 tools

Disambiguation5/5

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.

Naming Consistency4/5

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.

Tool Count4/5

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.

Completeness5/5

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues