Skip to main content
Glama
worldparts

worldparts

by raimondasl

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 warnings

The 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 list

Add it to a uv project:

uv add git+https://github.com/raimondasl/worldparts

With 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 mcp

Claude 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 bar

solve() 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

centrifugal_pump

Variable-speed pump from head, power and NPSH curves; affinity laws, efficiency, best-efficiency point, wear inputs (wear_head, wear_efficiency)

cavitation, low_flow, beyond_curve, outside_preferred_region, motor_overload, reverse_flow

tank

Open cylindrical tank with bottom ports or a top inlet (inlet_height), level-dependent head, overflow, mixed temperature

tank_empty, low_level, tank_overflow, drawing_air

media_filter

Sand or cartridge filter; linear media loss that grows with clogging plus housing loss

change_required, over_rated_flow, reverse_flow

uv_reactor

UV disinfection with a rated pressure drop and a plug-flow average dose

underdose, lamp_off, reverse_flow

pipe

Darcy-Weisbach pipe (Churchill friction factor), minor losses, static head

high_velocity, high_relative_roughness, below_vapour_pressure

valve

Control valve: Kv, linear, equal-percentage or quick-opening characteristic, leakage, actuator lag

high_pressure_drop

check_valve

Non-return valve: Kv forward, small leakage in reverse

none

supply

Fixed-pressure source: main, reservoir, pressurised line

none

drain

Open discharge to atmosphere

backflow

leak

Orifice to atmosphere for burst or background leakage, Q = Cd A sqrt(2 dp / rho); opening can change mid-run

backflow

mixing_faucet

Single-lever mixer with energy-balance mixing (secondary)

scald_risk, crossflow, back_siphonage, low_supply_pressure

instantaneous_water_heater

Flow-switched tankless heater with a power limit (secondary)

setpoint_not_met, below_activation_flow

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.0

Evidence

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 tools
add_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_systemA
Read-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_wntrA
Read-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_componentA
Read-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

disconnectB
Destructive

Remove the direct connection between two ports.

Inputs: a (required), b (required), system_id (required). Returns: name, system_id, components, connections, +2 more

export_systemA
Read-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_systemA
Read-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_componentsA
Read-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_variablesA
Read-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_componentA
Destructive

Remove a component instance and every connection to it.

Inputs: name (required), system_id (required). Returns: name, system_id, components, connections, +2 more

remove_controlA
Destructive

Remove a control; its input keeps its last value.

Inputs: name (required), system_id (required). Returns: controls, system_id

run_contractsA
Read-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

solveA
Read-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

    A3.9/5.0

    Scored across 20 tools

    Disambiguation5/5

    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.

    Naming Consistency5/5

    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.

    Tool Count4/5

    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.

    Completeness4/5

    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

    ActivityActive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      D
      maintenance
      Enables 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.
      -
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables 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