worldparts
Click on "Deploy 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., "@worldparts build a skid with a tank, pump, filter, valve, and UV reactor, then check it"
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.
worldparts
Tested water components an AI agent can wire together instead of writing physics code.
worldparts gives an AI agent parts to build water systems from: pumps, valves, pipes, tanks, media filters and UV reactors that it selects, sets and connects through typed tools. Every part declares its units, hard limits, operating envelope and warning codes. Every part ships scenarios and behavioural contracts that run in CI. The agent composes the system and the library does the physics. When a result falls outside a model's validity, the result carries a warning.
The focus is pumping, water treatment and distribution: design checks, what-if analysis, and calibration and diagnosis against plant data.
Our own benchmark keeps the claims honest. On small, fully specified calculations, frontier models writing Python from scratch were as accurate as agents using worldparts, and several times cheaper (results). worldparts therefore aims at what a one-off script does not give you: a reusable system model that can be reviewed and rerun, calibration and diagnosis against measurements, real product data with provenance, and systems too large or long-running to rebuild each time.
Status: v0.1.0, alpha. The interfaces may still change. See Status.
What an agent can do
The call sequence below builds a small purification skid over MCP (tank, pump, media filter, throttling valve, UV reactor, discharge), finds a problem and fixes it in five calls. The arguments are abbreviated; each call is one typed MCP tool call with its result.
list_components(query="uv") -> uv_reactor
describe_component(["tank", "centrifugal_pump", "media_filter", "valve", "uv_reactor",
"drain"]) -> ports, parameters, units, limits
load_system({"worldparts_system": "0.1", "name": "skid",
"components": [{"name": "raw", "type": "tank"}, ...,
{"name": "valve", "type": "valve", "parameters": {"kv": 40}}, ...],
"connections": [["raw.outlet", "pump.inlet"], ... down the line to out.port]})
-> system_id "s1", issues: 0 errors (raw.inlet is capped)
solve(s1) -> pump 36.8 m3/h, UV dose 29.3 mJ/cm2
warnings: pump.beyond_curve, filter.over_rated_flow, uv.underdose
solve_for(s1, target="uv.dose", value="45 mJ/cm2", vary="valve.opening", lower=0.05, upper=1)
-> opening 0.393, 24.0 m3/h, no warningsThe agent writes no equations. The warnings tell it that the pump is running past its
curve, the filter is overloaded and the water is under-disinfected. The fix is a hand
check away: dose = 20 mW/cm² × 15 L / (24 m³/h) = 20 × 2.25 s = 45 mJ/cm².
tests/test_docs.py replays this sequence against the server.
Related MCP server: QSDsan Engine MCP
Install
worldparts needs Python 3.11 or later. The commands below use uv.
Try it without installing:
uvx --from git+https://github.com/raimondasl/worldparts worldparts listAdd it to a uv project:
uv add git+https://github.com/raimondasl/worldpartsWith the optional WNTR/EPANET adapter:
uv add "worldparts[wntr] @ git+https://github.com/raimondasl/worldparts"Connect the MCP server
The server runs over stdio: worldparts mcp. Run by hand in a terminal, it prints nothing
and waits for an MCP client on stdin; you normally let the client start it (below). Stop
it with Ctrl+C.
Claude Code:
claude mcp add worldparts -- uvx --from git+https://github.com/raimondasl/worldparts worldparts mcpClaude Desktop or any other MCP client: add the server to the client's configuration
(for Claude Desktop, claude_desktop_config.json):
{
"mcpServers": {
"worldparts": {
"command": "uvx",
"args": ["--from", "git+https://github.com/raimondasl/worldparts", "worldparts", "mcp"]
}
}
}The server has 18 tools: list_components, describe_component, run_contracts,
create_system, add_component, remove_component, set_values, connect,
disconnect, add_control, remove_control, check_system, list_variables, solve,
solve_for, simulate, get_system and load_system. add_control and
remove_control manage control loops (a PI loop or a hysteresis switch that reads one
variable and writes one input); solve, solve_for and simulate report them under
controls, and variables=["control"] selects their results. When the wntr package is installed, the
WNTR adapter adds export_system and compare_with_wntr. Systems
live as long as the server process; get_system and load_system save and restore them.
The server's instructions steer an agent to the short path for a new system, the one in
the sequence above: list_components, then describe_component with every part in one
call (it takes a list of up to 12 ids or aliases and returns {components: [...]} in that
order), then one load_system call with the complete document (components with parameters
and inputs, connections and controls). Its issues are the check_system report, so the
next call is solve, solve_for or simulate. The step-by-step tools (create_system,
add_component, connect, set_values) are for editing, with check_system after edits.
Python in 20 lines
A pump lifts water from a ground tank through a DN50 riser to a free discharge 25 m up:
import worldparts as wp
s = wp.System("rooftop lift")
s.add("tank", "tank", initial_level="1.5 m") # ground tank, 2 m diameter by default
s.add("pump", "centrifugal_pump") # default curve: about 25 m3/h at 26 m, 2900 rpm
s.add("valve", "valve", kv="40 m3/h") # roughly a DN50 globe valve
s.add("riser", "pipe", length="60 m", diameter="50 mm", roughness="0.045 mm",
height_difference="25 m", minor_loss=1) # 1 = exit loss of the free discharge
s.add("roof", "drain")
for a, b in [("tank.outlet", "pump.inlet"), ("pump.outlet", "valve.port_a"),
("valve.port_b", "riser.port_a"), ("riser.port_b", "roof.port")]:
s.connect(a, b)
r = s.solve()
print(f"{r['pump.volume_flow']:.1f} m3/h, {r['pump.head']:.1f} m, {r['pump.efficiency']:.0f} %")
print([w.path for w in r.warnings])
sim = s.simulate(duration="30 min", step="10 s")
print([(w.time, w.path) for w in sim.warnings])Output:
14.7 m3/h, 31.0 m, 54 %
['pump.outside_preferred_region']
[(0.0, 'pump.outside_preferred_region'), (960.0, 'tank.low_level'), (1190.0, 'tank.drawing_air'), (1200.0, 'tank.tank_empty'), (1200.0, 'pump.low_flow')]The pump runs at 61 % of its best-efficiency flow, which is below the preferred operating
region (an info). The simulation drains the ground tank. As the lift grows, the flow
falls from 14.7 to 13.6 m³/h, so a hand estimate is 4.71 m³ at a mean of 14.2 m³/h, or 20
minutes. The simulated tank empties at 1200 s. The tank then reports that the pump is
drawing air, and the pump reports low flow.
Plain numbers are in each variable's declared unit. Strings may carry their own unit
("1.5 m", "40 m3/h"). Pressures are gauge unless stated otherwise.
System documents and the CLI
The CLI works on system documents: YAML (or JSON) files that list the components with
their parameters, the connections and, optionally, a simulation block. This is the lift
above as a document:
worldparts_system: "0.1"
name: rooftop lift
components:
- {name: tank, type: tank, parameters: {initial_level: 1.5}}
- {name: pump, type: centrifugal_pump}
- {name: valve, type: valve, parameters: {kv: 40}}
- name: riser
type: pipe
parameters: {length: 60, diameter: 50, roughness: 0.045, height_difference: 25, minor_loss: 1}
- {name: roof, type: drain}
connections:
- [tank.outlet, pump.inlet]
- [pump.outlet, valve.port_a]
- [valve.port_b, riser.port_a]
- [riser.port_b, roof.port]
simulation: {duration: 30 min, step: 10 s}Plain numbers are in the declared units (diameter: 50 is 50 mm); strings with units work
too (diameter: 50 mm). You do not have to write documents by hand: System.to_dict()
returns one (with full component ids and every value in declared units). Continuing the
Python example:
import yaml
s.reset_states() # back to the initial tank level after the simulation
s.simulation = {"duration": "30 min", "step": "10 s"}
with open("lift.yaml", "w", encoding="utf-8") as f:
yaml.safe_dump(s.to_dict(), f, sort_keys=False)Then, from the same directory:
uv run worldparts validate lift.yaml
uv run worldparts solve lift.yaml --var pump
uv run worldparts simulate lift.yaml # uses the simulation block
uv run worldparts simulate lift.yaml --duration "5 min" --step "1 s" --var tank.level
uv run worldparts solve lift.yaml --units "*.volume_flow=m3/h"Pipes, valves, supplies and drains report flow in L/min; pumps, tanks, filters and UV
reactors in m³/h. --units PATTERN=UNIT (repeatable) converts any reported variable, for
example --units "*.volume_flow=m3/h" or --units "pump.inlet.p=bar absolute". The other
commands are list, describe, check-catalog and, with the WNTR adapter, export; every
command except mcp takes --json (worldparts --help).
More documents and scripts, including a purification skid and a pump lift to a rooftop
tank, are in examples/. They are in the repository, not in the
installed package: clone it (git clone https://github.com/raimondasl/worldparts) to run
them.
Controls
A control reads one reported variable and writes one component input (design 13.1). A PI loop holds a booster pump's discharge at 3 bar:
b = wp.System("booster")
b.add("mains", "supply", pressure="0.5 bar")
b.add("pump", "centrifugal_pump", speed=0.5)
b.add("zone", "valve", kv="10 m3/h")
b.add("out", "drain")
for a, c in [("mains.port", "pump.inlet"), ("pump.outlet", "zone.port_a"),
("zone.port_b", "out.port")]:
b.connect(a, c)
b.add_control("duty", "pi", measure="pump.outlet.p", setpoint="3 bar",
actuate="pump.speed", gain=0.1, integral_time="2 s",
output_min=0.3, output_max=1.2)
r = b.solve() # goal-seeks the speed that holds 3 bar
print(f"speed {r['pump.speed']:.3f}, {r['pump.outlet.p']:.2f} bar")
sim = b.simulate("2 min", "1 s", events=[{"at": "60 s", "set": {"zone.opening": 0.5}}])
print(f"speed {sim.final['pump.speed']:.3f}, {sim.final['pump.outlet.p']:.2f} bar")Output:
speed 0.935, 3.00 bar
speed 0.885, 3.00 barsolve() goal-seeks every PI actuator within its output limits and leaves it at the value
found; simulate() runs the loop as a sampled-data controller whose command acts from the
sample where it is computed, like an event. A hysteresis control switches an input
between two values (a level switch on a fill pump). Results carry r.controls and the
series control.duty.output and control.duty.measure. System documents list controls
under controls, and simulation events may ramp a value: {at, ramp: {path: [start, end]}, over}. See examples/booster_station.yaml.
The catalogue
Eleven components, all with the id prefix worldparts.hydraulic.. The full reference
(ports, parameters with units, defaults and limits, modes, warnings, bindings and
provenance) is generated from the manifests: docs/catalog.md.
Component | What it models | Key warnings |
| Variable-speed pump from head, power and NPSH curves; affinity laws, efficiency, best-efficiency point, wear inputs ( |
|
| Open cylindrical tank with bottom ports or a top inlet ( |
|
| Sand or cartridge filter; linear media loss that grows with clogging plus housing loss |
|
| UV disinfection with a rated pressure drop and a plug-flow average dose |
|
| Darcy-Weisbach pipe (Churchill friction factor), minor losses, static head |
|
| Control valve: Kv, linear, equal-percentage or quick-opening characteristic, leakage, actuator lag |
|
| Non-return valve: Kv forward, small leakage in reverse | none |
| Fixed-pressure source: main, reservoir, pressurised line | none |
| Open discharge to atmosphere |
|
| Orifice to atmosphere for burst or background leakage, Q = Cd A sqrt(2 dp / rho); opening can change mid-run |
|
| Single-lever mixer with energy-balance mixing (secondary) |
|
| Flow-switched tankless heater with a power limit (secondary) |
|
The runtime is a quasi-steady hydraulic and thermal network solver with time stepping for tanks and actuators. It does not model water hammer, compressible flow, pipe heat loss or temperature-dependent water properties. Each manifest lists its own assumptions and what it leaves out.
Manifests, contracts and provenance
Each component is a YAML manifest plus a Python class. The manifest is what the agent
reads (through describe_component) and what the runtime and the tests enforce. It
declares ports, parameters, inputs, states and observables with units and hard limits. It
also declares modes, an envelope of soft validity rules, and code-emitted warnings.
Scenarios are small systems with expected results, taken from hand calculations. Contracts
are properties checked over a sweep, such as "flow never falls as the valve opens" or
"mass in equals mass out". worldparts check-catalog runs every scenario and contract, so
each manifest is a tested claim. Provenance records the sources of the equations and where
the default data came from. The defaults are generic illustrative values, never a specific
product. The format is specified in
spec/component-manifest.md.
A trimmed excerpt of the valve manifest (full file):
id: worldparts.hydraulic.valve
summary: Throttling valve with Kv sizing, inherent characteristic, leakage and first-order actuator lag.
ports:
- {name: port_a, type: fluid, medium: water, description: Inlet in the forward direction.}
- {name: port_b, type: fluid, medium: water, description: Outlet in the forward direction.}
parameters:
- {name: kv, type: number, unit: m3/h, default: 2.5, minimum: 0.0001, maximum: 100000, description: ...}
inputs:
- {name: opening, description: "Commanded valve opening: 0 closed, 1 fully open.", unit: "1", default: 1.0, minimum: 0, maximum: 1}
observables:
- {name: pressure_drop, unit: bar, pressure_reference: difference, description: Pressure at port_a minus pressure at port_b.}
envelope:
- {code: high_pressure_drop, severity: warning, condition: "pressure_drop > 3", message: ...}
scenarios:
- id: kv-at-1-bar # hand calculation: 2.5 m3/h * sqrt(1000 / 998.2) = 41.7042 L/min
system: {components: [...], connections: [...]}
expect:
- {variable: dut.volume_flow, value: 41.7042, abs_tol: 0.01}
contracts:
- id: mass-conservation
scenario: kv-at-1-bar
sweep: {variable: dut.opening, from: 0, to: 1, steps: 11}
check: {type: equal, left: dut.port_a.m_flow, right: -dut.port_b.m_flow, abs_tol: 1.0e-9}
implementations:
reference: {python: "worldparts.components.valves:TwoWayValve"}
modelica: {class: Buildings.Fluid.Actuators.Valves.TwoWayLinear, ...}
wntr: {element: TCV, ...}
provenance:
data: {acquisition: generic, notes: Default parameters are generic illustrative values ...}
license: Apache-2.0
data_license: CC0-1.0Evidence
Our benchmark (v0.2). 32 tasks (calculations and design-judgement reviews) ran under two conditions: an agent with only the worldparts MCP tools, and an agent writing Python from scratch with numpy, scipy, fluids and wntr. Claude Sonnet 5 and Claude Opus 5.5 each ran every task in both conditions. Both passed 97 to 100 percent in both conditions. The from-scratch agent used about one eighth of the tokens. The only worldparts failure traced to an ambiguous component description, since fixed. Details, harness problems found along the way, and limits: docs/benchmark-results-v0.2.md.
The design follows a research report, World model libraries for AI agents. Three of its findings drive the design:
LLM-written physical models compile but rarely simulate correctly. In ModiGen, GPT-4o's Modelica loaded 95.56 % of the time but was functionally correct only 24.44 % of the time. In a 2026 fluid-systems benchmark, ten LLMs scored 0.0 on simulation fidelity.
Agents do well when they compose verified parts through typed tools. EPANET-Agentic, which gives an agent function-call tools over WNTR, reported 100 % task success on three benchmark networks. An earlier framework, LLM-EPANET (Goldshtein, Perelman, Schuster and Ostfeld, Environmental Modelling & Software 205, 107148, 2026), translates questions into EPANET code and runs it; it reached 56 to 81 % overall on its 69 queries, and over 90 % on the simpler ones. The two studies used different tasks.
No existing library packages components for agents. The physics exists in Modelica Buildings, WNTR/EPANET and WaterTAP. None of them ships an agent-readable manifest, behavioural contracts or data provenance.
Before this release, one agent used the MCP server, with tools only, for two tasks: a pump lift to a rooftop tank and a purification skid with a clogging filter. It made 43 tool calls with one error. Its answers agreed with its hand calculations. It reported 16 friction items, and most were fixed before this release. See docs/agent-trial.md. That was one informal trial, not a benchmark.
Status and roadmap
v0.1.0 is the first public release: manifest format 0.1, 11 components, the reference
runtime, the System API, the MCP server and the CLI. The WNTR/EPANET adapter is described
in docs/wntr-adapter.md. The composition benchmark (v0.2) is done
(results). Next come pump systems and diagnostics (v0.3), real product data with provenance (v0.4) and a
Modelica backend (v0.5). See docs/roadmap.md, the design contract
docs/design.md and the changelog.
Contributing
See CONTRIBUTING.md for development setup, how to add a component and how contracts are tested. The design contract changes first; code follows.
License
Apache-2.0 for the code and manifests (LICENSE). The default parameter data in the manifests is CC0-1.0.
Available Tools
20 toolsadd_componentA
Add a component instance to a system.
Returns the resolved parameters, inputs and states with units, the instance's port paths, and pre-flight issues about it (unconnected ports until you connect them).
Inputs: name (required), component (required), system_id (required), inputs, +1 more. Returns: hint, name, type, ports, +5 more
add_controlA
Add a control that reads one result and writes one component input.
pi: solve finds the output holding the setpoint (control_saturated if unreachable); simulate runs it after each step's solve. hysteresis: on below on_below, off above off_above (short_cycling if too frequent). Returns every control.
Inputs: name (required), type (required), actuate (required), measure (required), +2 more. Returns: controls, system_id
check_systemARead-only
Structural pre-flight with stable issue codes.
Codes: unknown_component, unknown_port, incompatible_ports, self_connection, unconnected_port (warning), no_pressure_reference, boundary_short_circuit, parameter_out_of_range, invalid_value; for controls unknown_variable, invalid_control, control_conflict. solve and simulate refuse to run while any error remains.
Inputs: system_id (required). Returns: ok, errors, issues, warnings, +1 more
compare_with_wntrARead-only
Solve the system here and in EPANET (via WNTR) and compare: per-link flows (m3/h) and node pressures (bar gauge), differences and why they diverge.
Inputs: system_id (required). Returns: report, system_id, max_flow_rel_diff, max_pressure_abs_diff
connectA
Connect two ports. Connecting several ports to one node forms a junction (tee).
Errors name the offending port and list the instance's valid ports. Returns the connections and the ports still unconnected (unconnected ports are capped).
Inputs: a (required), b (required), system_id (required). Returns: name, system_id, components, connections, +2 more
create_systemA
Create a new empty system and return its system_id.
For building step by step; a new system is quicker as one load_system call.
Inputs: name (required), description. Returns: name, system_id, components, connections, +2 more
describe_componentARead-only
Describe component types: everything needed to use them.
Pass every component you need as a list in one call: the result is then {components: [...]} in that order (one id: one description). Each has ports; parameters and inputs with units, defaults and hard limits (table columns in row order); states and observables; modes (the first whose condition holds is reported); warning codes; scenario and contract ids; bindings. Plain numbers are in these units; pressures are gauge unless stated; quantity 'temperature_difference' converts by scale only. A state's steady is 'settle' (solve sets its equilibrium) or 'hold' (solve keeps it, e.g. a tank level). A warning's source is 'envelope' (its condition holds) or 'component' (raised by code; message says when).
Inputs: component (required), detail. Returns: object
disconnectBDestructive
Remove the direct connection between two ports.
Inputs: a (required), b (required), system_id (required). Returns: name, system_id, components, connections, +2 more
export_systemARead-only
Export a system as EPANET .inp text (units CMH, Darcy-Weisbach).
Faucets and heaters are unsupported. notes: what the export approximates.
Inputs: system_id (required), target. Returns: text, notes, target, system_id
get_systemARead-only
The system document (design 6.3): components with explicit values, connections, changed states, controls and the optional simulation block. Pass it to load_system later.
Inputs: system_id (required). Returns: document, system_id
list_componentsARead-only
Search the component catalogue.
Returns id, alias, name, summary, ports, key parameters (with units and defaults), fidelity level and tags of every matching component. Call without a query to list everything, then describe_component with all the components you need in one call.
Inputs: query. Returns: note, count, components
list_variablesARead-only
Every variable path with kind, unit, limits and description.
Kinds: parameter, input, state (settable), observable, port ('..p' in bar gauge, '.m_flow' in kg/s into the component, '.T' in degC) and control ('control..output' and '.measure'; component='control' lists them).
Inputs: system_id (required), component. Returns: system_id, variables
load_systemA
Build a whole system in one call from a system document; returns its system_id.
The quickest way to create a system: every component (with parameters and inputs), connection and control in one document, then solve. get_system returns such a document. A wrong shape fails with every schema error and the expected shape of the offending part; unknown types, ports and invalid values are kept and listed in issues (the check_system report, no need to call it): fix every error, then solve.
Inputs: document (required). Returns: name, issues, controls, system_id, +4 more
remove_componentADestructive
Remove a component instance and every connection to it.
Inputs: name (required), system_id (required). Returns: name, system_id, components, connections, +2 more
remove_controlADestructive
Remove a control; its input keeps its last value.
Inputs: name (required), system_id (required). Returns: controls, system_id
run_contractsARead-only
Run a component's scenarios and contracts and report pass or fail.
Scenarios have expected results; contracts check monotonicity, bounds, conservation and warning conditions against the reference implementation.
Inputs: component (required). Returns: passed, component, contracts, scenarios
set_valuesA
Set inputs, parameters or states by path (atomically: all or nothing).
Plain numbers are in the declared unit; strings may carry units. Returns the values now in effect, in declared units.
Inputs: values (required), system_id (required). Returns: values, system_id
simulateA
Simulate over time with a fixed step and timed events (set or linear ramp).
Samples at every multiple of step, at each event time and at the end. Returns downsampled series with min, max and final over the full run; each warning once (first time, last time, active at end); every mode change. Controls act after each sample's solve (taking effect over the next step); their series are always included and controls reports each loop at the end. Unless restore is true, the system keeps its final state.
Inputs: duration (required), system_id (required), step, units, +4 more. Returns: time, issues, samples, controls, +6 more
solveARead-only
Solve the steady operating point.
Returns the selected values with units (6 significant digits; pressures carry their reference, port pressures are bar gauge), the mode of every instance, component warnings and non-fatal pre-flight issues. Fails with the list of errors when check_system reports any. Controls: PI actuators are set to hold their setpoints, hysteresis switches hold their state; controls reports each loop (output, measured, setpoint, error, saturated or state).
Inputs: system_id (required), units, variables. Returns: modes, issues, values, controls, +5 more
solve_forA
Goal seek: the value of one input or parameter that gives a target result.
Brent's method on vary between lower and upper until target equals value, e.g. the pump speed for 15 m3/h. If the target does not cross the value inside the bounds, the error reports it at both bounds and nothing changes. On success vary stays at the value found; the result is the operating point there, as from solve.
Inputs: vary (required), lower (required), upper (required), value (required), +4 more. Returns: vary, found, modes, issues, +11 more
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 20 tools
Every tool targets a distinct operation: system retrieval vs. construction, component search vs. description, steady vs. goal-seek vs. dynamic simulation. Even close pairs like get_system/load_system and solve/solve_for are clearly separated by their descriptions.
Tool names follow a consistent imperative lower_snake_case pattern, mostly verb_noun (get_system, list_components, create_system, add_control). A few bare verbs (connect, solve, simulate) are still predictable and match common simulation vocabulary.
At 20 tools, the set is on the heavier side, but the domain—hydraulic system modeling, simulation, controls, and validation—justifies the breadth. Each tool addresses a distinct phase of the workflow, so the count feels slightly over but reasonable rather than bloated.
The surface covers creation, inspection, modification, connection, control, solving, simulation, export, and validation comprehensively. Minor gaps exist, such as no explicit delete_system tool or system listing, but these do not block core modeling and analysis workflows.
Maintenance
Related MCP Connectors
Parametric 3D CAD for AI agents: build print-ready parts, check them, export STL, 3MF or STEP.
Deterministic reasoning stack for AI agents: simulate, decide & compute, plus cross-domain tools.
Design circuits, simulate and verify embedded firmware, and lay out PCBs with AI agents.
AI-callable calculators and engineering models with real formulas. No hallucinated math.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables language models to perform hardware engineering tasks including CAD part design and heat transfer simulations. Provides tool calls for building mechanical components and running thermal analysis through natural language interactions.-
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to simulate wastewater treatment processes using natural language, with dual MCP and CLI adapters for flexible integration.MIT
- AlicenseNot gradedqualityBmaintenanceA water treatment process simulation engine that exposes WaterTAP capabilities through MCP and CLI adapters, enabling AI agents to build, solve, and optimize flowsheets using natural language.1MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to discover, configure, measure, and operate simulated lab instruments (microscope, oscilloscope, robot arm, liquid handler and reader) through a single tool layer. Requests are validated before dispatch, confirmed against the instrument's own readback, and qualified with units, age, calibration, uncertainty, and quality flags, so mismatches, stale data, and partial effects are reported instead of silently retried.MIT