Skip to main content
Glama
hjlrosales

EPANET MCP Server

by hjlrosales

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault

No arguments

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": true
}

Tools

Functions exposed to the LLM to take actions

NameDescription
load_networkA

Load an EPANET INP model from the server's data directory into a named session. Returns the network id and counts.

create_networkA

Build a new hydraulic model from scratch — no file needed. Supply junctions, reservoirs (e.g. a river intake) and optional tanks with elevations, plus pipes with lengths and diameters; optional x/y coordinates per node are written into a [COORDINATES] section so the model has real geometry. The spec is validated (unique ids, real pipe endpoints, realistic bounds) and the generated INP is re-opened through the EPANET engine: it must parse with exactly the requested node/link counts. Solvability is reported (solves: true/false) but not enforced, so a skeleton network can be created and filled in later. Creates a normal session: run_simulation, set_* edits, optimize_network and save_network all work on it immediately. This is the first step of the GIS workflow; for an auto-layout from real streets use generate_network_from_bbox (Phase 4) instead — see mcp_gis_roadmap.md.

generate_network_from_bboxA

Build a water network automatically from real streets — the one-call GIS layout (roadmap Phase 4, native tool). Fetch OSM highway+waterway ways for a bbox (or reuse a saved extract via osmExtract/osmFile), derive the junction/pipe skeleton: the connected component containing the road nearest the river, dead-end spurs pruned (minDeadEndM, default 30 m), roads decimated to ~spacingM (default 150 m) junction spacing, and pipe diameters from the road class as HDPE (PE100) INTERNAL bores at SDR 11 — e.g. trunk 290, primary 258, secondary 205, tertiary 164, residential 90, service 74 mm — with the default pipe material HDPE (Hazen-Williams C = 130). The reservoir is placed at the nearest OSM waterway point to river and connected to the nearest junction by an intake pipe; ground elevations are sampled from the bundled IFSAR 10 m DEM (never guessed, same fail-closed contract as create_network elevationSource). The resulting model is engine-validated and becomes a normal session (networkId): run_simulation, assign_demands, optimize_network and save_network all work on it immediately. The bbox mode calls the public Overpass API (fixed endpoint, 30 s timeout, 25 MB response cap); pass osmExtract or osmFile instead to work from a saved extract with no network. Provide exactly one of bbox / osmExtract / osmFile, plus river and a positive reservoirHead (the intake/pump discharge head — a design input, never guessed).

save_networkA

Write the current state of a loaded network (including any set_* edits applied so far) to a file in the server's data directory: EPANET INP text (format 'inp', default) or the app's .net container (format 'net', which embeds the INP text — load_network reads both). The model is re-validated through the EPANET engine before writing, and existing files are not overwritten unless overwrite: true. Use this to persist edits: after saving, load_network can read the file back on a fresh session or server restart.

get_network_summaryB

Return a summary of a loaded network: counts, simulation timing, units, quality type, and node/link id previews.

list_networksA

List the networks currently loaded in this server session (id, source file, whether results exist). Use to recover a network id after losing it.

run_simulationA

Run the EPANET hydraulic simulation for a loaded network and return aggregate results (min/max/average pressure and flow, pump energy, warnings). Results are stored so get_node_results / get_link_results can query them.

get_nodesA

Inspect static properties of network nodes (junctions: elevation/demand; reservoirs: head; tanks: levels/diameter). Pass specific node ids to keep the response small; omit to get a capped listing.

get_linksA

Inspect static properties of network links (pipes: length/diameter/roughness; pumps: speed/power/head curve; valves: diameter/setting). Pass specific link ids to keep the response small; omit to get a capped listing.

get_coordinatesA

Read the model's node coordinates (the [COORDINATES] section) without saving a file — pure read, nothing is modified. Returns each requested node's stored x/y (the model's own coordinate system) plus, when coordinates is given, its WGS84 lat/lon (coordinates: 'wgs84' means x is longitude/y is latitude; {utmZone} means x/y are UTM easting/northing meters in that zone). Nodes without stored coordinates come back in missingIds — never invented. Pass specific node ids to keep the response small, or page with offset + the 250-item cap. This is the geometry half of the GIS workflow: get_coordinates to inspect where nodes are, then sample_elevations for real IFSAR ground elevations at those points.

get_node_resultsA

Query simulation results for nodes. Defaults to the last timestep and includes min/max/mean over time unless timesteps are given. Node ids are capped at 250, timesteps at 100. Pass the runId returned by run_simulation to pin results to a specific run; omit for the latest run. Units and a qualityAnalyzed flag are included in every response. Demand for tanks/reservoirs is net inflow; tank pressure is 0 by definition.

get_link_resultsA

Query simulation results for links (flow, velocity, headloss, unitHeadloss, quality, status, setting, head). Defaults to the last timestep and includes min/max/mean over time unless timesteps are given. Link ids are capped at 250, timesteps at 100. Pass the runId returned by run_simulation to pin results to a specific run; omit for the latest run. headloss is TOTAL headloss (unit headloss x length/1000 for pipes; valves report the raw column value). unitHeadloss is the raw per-1000-unit value. Pumps report head (head added, positive when pumping normally); their headloss/unitHeadloss are null. status is a raw EPANET status code (legend in units.status); statusLabel/statusWarning are decoded per link type. Units and a qualityAnalyzed flag are included in every response.

set_pipe_diameterA

Change the diameter of a pipe in a loaded network. The edit is validated against the EPANET engine before it is applied: the pipe must exist and be a pipe, the value must be within realistic bounds for the model's units, and the edited model must re-parse with the requested diameter. Previous simulation results for the network are invalidated (they describe the old model) — re-run run_simulation afterwards.

set_junction_demandA

Change the base demand of a junction in a loaded network. The edit is validated against the EPANET engine before it is applied: the node must exist and be a junction, the value must be within realistic bounds, and the edited model must re-parse with the requested demand. Junctions whose demand is overridden by the [DEMANDS] section are rejected. Previous simulation results for the network are invalidated (they describe the old model) — re-run run_simulation afterwards.

set_demand_patternA

Add or replace a demand pattern ([PATTERNS]) and attach it to one or more junctions, so base demands vary over the simulation (e.g. an hourly diurnal curve with morning and evening peaks). The edit is validated against the EPANET engine before it is applied: the pattern id must be valid, each multiplier must be between 0 and 10 (and not all zero), every listed junction must exist and be a junction, and the edited model must re-parse with the pattern present at exactly the requested length and values, attached to each junction's demand, with node/link counts unchanged. A previously-solvable model must still solve. An existing pattern with the same id is replaced in place; a junction already using a pattern gets this one instead. Previous simulation results are invalidated — re-run run_simulation afterwards (get_node_results with timesteps returns the hourly variation).

set_pump_speedA

Change the relative speed setting of a pump in a loaded network. The edit is validated against the EPANET engine before it is applied: the link must exist and be a pump, the value must be within realistic bounds, and the edited model must re-parse with the requested speed. If the pump has no SPEED property yet, one is appended to its [PUMPS] line. Previous simulation results for the network are invalidated (they describe the old model) — re-run run_simulation afterwards.

set_valve_settingA

Change the setting of a valve (PRV/PSV/PBV pressure, FCV flow, TCV loss coefficient) in a loaded network. The edit is validated against the EPANET engine before it is applied: the link must exist and be a valve, the value must be within realistic bounds for the valve type and the model's units, and the edited model must re-parse with the requested setting. GPV valves are rejected (their setting is a headloss curve id). Previous simulation results for the network are invalidated (they describe the old model) — re-run run_simulation afterwards.

set_node_elevationA

Change the elevation of a junction, reservoir (its fixed head), or tank (its bottom elevation) in a loaded network. The edit is validated against the EPANET engine before it is applied: the node must exist and be a junction/reservoir/tank, the value must be within realistic bounds for the model's units, and the edited model must re-parse with the requested elevation. Previous simulation results for the network are invalidated (they describe the old model) — re-run run_simulation afterwards.

add_tankA

Add a storage tank at a strategic location: a new tank node plus a connector pipe attaching it to an existing node — a junction (e.g. a low-pressure junction that needs pressure support) or, optionally, a reservoir. The edit is validated against the EPANET engine before it is applied — unique ids, realistic geometry, the edited model must re-parse with the tank and pipe present, node/link counts must increase by exactly one each, and the edited model must still solve hydraulically if the base model did. Previous simulation results are invalidated; re-run run_simulation to see the effect.

add_valveA

Insert a pressure-reducing valve (PRV) in series with an existing pipe: the pipe is split — a new junction is created at a fraction along it, the original pipe is shortened to that junction, and a new PRV link runs from the junction to the pipe's downstream node, capping pressure there at the requested setting (e.g. to protect low-lying tap stands from excess static head). The edit is validated against the EPANET engine before it is applied — unique ids, realistic bounds, the edited model must re-parse with the junction as a junction, the valve as a PRV with the requested setting, and the pipe shortened as requested, node/link counts must increase by exactly one each, and the edited model must still solve hydraulically if the base model did. Previous simulation results are invalidated; re-run run_simulation to see the effect.

remove_tankA

Remove a storage tank and every pipe attached to it (the inverse of add_tank). The edit is validated against the EPANET engine before it is applied — unique id, the edited model must re-parse with the tank and its connector pipes gone, node count must decrease by exactly one and link count by exactly the removed pipes, and a previously-solvable network must still solve. If controls or rules reference the tank, the engine rejects the edit (fail-closed) rather than leaving a dangling reference.

run_candidateA

Evaluate a scenario: apply a batch of edits (pipe diameters, junction demands, pump speeds) to a COPY of a loaded network, validate the copy against the EPANET engine, run a simulation, and return the aggregate results (same shape as run_simulation). The base model and its stored results are NEVER modified, so candidates can be compared freely and previous runs stay valid.

optimize_networkA

Search for the best combination of edits by evaluating an explicit grid of candidate choices on COPIES of a loaded network. For each variable (pipe diameter, junction demand, pump speed, or add_tank placement) you list the choices to try — for add_tank, the nodes to attach a tank to (junctions or reservoirs; plus, by default, the baseline with no tank, so 'try a tank at J2 vs R1 vs none' is one variable); every combination is validated against the EPANET engine and simulated, and the combination that minimizes/maximizes the chosen metric is returned with its full aggregate results. The base model and its stored results are NEVER modified. The search space (product of all choice lists) must fit within maxEvaluations (default 20, up to 100).

lookup_pipe_diametersA

Look up standard pipe inner diameters for common water-distribution materials. Returns a table of standard sizes with nominal diameter, outer diameter, wall thickness, and the resulting inner diameter — the value EPANET uses for hydraulic modeling. Materials: HDPE (ISO 4427, PE100 SDR 11), PVC (AWWA C900 DR 18), ductile iron (ISO 2531 class K9), carbon steel in three variants: steel-medium (ISO 4200 medium series), steel-sch40 (ANSI B36.10 Schedule 40/STD), steel-sch80 (ANSI B36.10 Schedule 80/XS). Filter by material, minimum/maximum nominal size, and display units (mm, inches, or both). Use this when sizing pipes, choosing a standard diameter for set_pipe_diameter, or comparing materials for a given nominal size.

recommend_diameterA

Recommend standard pipe diameters for a given flow rate and target velocity range. Given a design flow (in any EPANET flow unit) and an acceptable velocity window, computes the ideal inner diameter using Q = v × πd²/4, then returns every standard pipe size (from HDPE, PVC, ductile iron, or steel in medium/Sch 40/Sch 80 variants) whose inner diameter produces a velocity within the range. Each recommendation includes the resulting velocity, an approximate Reynolds number (for turbulence check), and the standard it comes from. Use presets (distribution, transmission, service, suction) for typical velocity ranges, or specify custom min/max. Pipe diameters returned are the inner-diameter values to use with set_pipe_diameter.

friction_lossA

Calculate friction loss (head loss and pressure drop) in a full-bore water pipe. Supports two methods: Hazen-Williams (empirical, uses C-factor, water-only) and Darcy-Weisbach (physics-based, uses absolute roughness ε, universally applicable). Returns head loss per metre, total head loss over the pipe length, pressure drop, flow velocity, Reynolds number, and the Darcy friction factor. Roughness presets are available for common pipe materials (hdpe, pvc, di-cement, steel-new, steel-aged, etc.) or supply custom values. Use this to validate pipe sizing from recommend_diameter or to compare friction losses across materials/ diameters.

pipe_sizing_wizardA

End-to-end pipe sizing wizard: given a flow rate, pipe length, and material, finds every standard pipe size that satisfies velocity AND friction-loss constraints, then ranks them by suitability. Chains velocity-based diameter recommendation with Hazen-Williams or Darcy-Weisbach friction-loss validation in a single call. Supports velocity presets (distribution/transmission/service/suction), roughness presets (hdpe/pvc/di-cement/steel-new etc.), and optional max head-loss or max pressure-drop filters. Returns the top N candidates ranked by a composite score (60% velocity closeness + 40% head-loss efficiency). Each candidate includes: nominal size, inner diameter, velocity, head loss per metre, total head loss, pressure drop, Reynolds number, flow regime, and the friction factor. Use this to make a single validated pipe selection without calling recommend_diameter and friction_loss separately.

pump_selectionA

Size a centrifugal pump for a required flow rate and total dynamic head. Computes hydraulic power, brake (shaft) power, motor nameplate power with safety factor, specific speed for pump-type classification (radial/mixed/axial flow), NPSH available from suction conditions, and suggests standard motor sizes and compatible speeds. Estimates pump efficiency automatically from the power level, or accept a user-specified value. Returns a complete pump sizing summary suitable for procurement or EPANET pump-curve entry. Use this after pipe_sizing_wizard to size the pump for a validated pipe network.

list_fitting_kfactorsA

List K-factors (minor loss coefficients) for common pipe fittings. Returns a table of fitting names, K-factors, categories, and equivalent-length ratios. Filter by category: valve, elbow, tee, other, or all. Use this to find the correct K-factor before calling calculate_minor_loss, or to compare loss characteristics of different fitting types.

calculate_minor_lossA

Calculate total minor (dynamic) head loss through a set of pipe fittings using the K-factor method: h = K × v² / (2g). Provide a pipe diameter, flow rate, and a list of fitting types with quantities. Each fitting's K-factor is looked up from the built-in database (use list_fitting_kfactors to see available types), or supply a customK for non-standard fittings. Returns: flow velocity, individual fitting losses, total minor loss, total equivalent length of pipe, and pressure drop. Use this to account for valves, elbows, tees, and other fittings when sizing pipes or pumps.

fetch_road_networkA

Fetch the road network for a geographic bounding box from OpenStreetMap (fixed public Overpass endpoint only — never Nominatim, never a caller-supplied URL) and return structured road geometry and attributes for future EPANET network generation: OSM way id, name, highway classification, full WGS84 geometry, length (m), and lanes/maxspeed/surface/oneway/bridge/tunnel where OSM has them (missing tags come back null). When waterwayTypes is set, matching linear waterway ways (river/stream/brook/canal/drain/ditch — for river-intake placement) are fetched in the same request and returned under waterways with way id, name, waterway class, geometry, length (m), and width/intermittent/tunnel. The bounding box is validated (ranges, min < max, per-axis span and total area caps) and the results are capped (maxResults, default 500, hard cap 5000) and pageable (offset; roads and waterways are paged independently with the same offset/maxResults). The Overpass query requests only the requested highway/waterway classes (roadTypes/waterwayTypes whitelists; roadTypes defaults to the standard drivable set, waterwayTypes is off unless given), carries a descriptive User-Agent, has a 30 s timeout and a 25 MB response cap, and fails closed on any Overpass error/timeout. Identical (bbox, roadTypes, waterwayTypes) requests are served from a short-lived in-memory cache. PURE READ: no network session, model, or file is touched — nothing is generated or optimized yet. Data © OpenStreetMap contributors, licensed ODbL (https://www.openstreetmap.org/copyright).

sample_elevationsA

Sample real ground elevations from the bundled IFSAR 10 m DEM (Philippines) for WGS84 points, or for the nodes of a loaded network whose x/y coordinates are WGS84 (or UTM with a zone). Pure read — nothing is modified. Returns elevation in meters per point, bilinearly interpolated from the 2x2 pixel neighborhood; points outside the tile grid or over nodata come back with elevation null. The bundled DEM is the default elevation source for create_network (elevationSource), so this is the first call in the GIS workflow: sample, then build or set_node_elevation. Configure the data folder with EPANET_DEM_DIR (defaults to the repo's apps/app/public/dem/ifsar-10m).

assign_demandsA

Convert a population table into per-node base demands for a loaded network: demand = people × litersPerCapitaPerDay × peakingFactor ÷ 86400, converted from liters/second into the model's flow units (so GPM/CFS/MGD models get the right numbers automatically). The computed demands replace each junction's base demand through the same guarded, engine-validated path as set_junction_demand: every junction must exist, junctions with demand categories in the [DEMANDS] section are rejected (they override the [JUNCTIONS] column), each computed demand must be within the model's demand bounds, and the edited model must re-parse with exactly the requested demands and unchanged node/link counts. Previous simulation results are invalidated — re-run run_simulation afterwards. This is the 'population in, demands out' step of the GIS workflow (mcp_gis_roadmap.md Phase 3).

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

Latest Blog Posts

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/hjlrosales/EPAnet-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server