Specification · section 10

Three control surfaces

The same six primitives are reachable three ways. They compose: an agent uses MCP to explore and decide, writes a code file for the parts that must run fast or long, and uses the CLI to check on it.

10.1 MCP bridge

A single MCP server exposes a whole lab to any MCP host through ten generic tools (§3.3), each with input_examples. Because the descriptor is the documentation, no per-device tool code is written, and the tool list does not grow with the lab.

MCP tool MHP call
mhp_find directory/search (or a scan of cards in a small lab)
mhp_describe device/describe {detail, select}, default summary; with resource= it calls resources/read
mhp_read signals/read
mhp_write settings/write
mhp_invoke actions/invoke (+ optional wait)
mhp_job jobs/status, jobs/pause, jobs/resume, jobs/cancel
mhp_estop safety/estop on one device or every device this session touched
mhp_run runs a script against the Lab client; returns stdout only (§3.4)
mhp_lab fleet management: status, scan, add, onboard, recipes, registry, safety card, events, remove, reload, release (§9.1)
mhp_data runs, their files, the run log, and what devices pushed (op='updates', §8)
mhp_method the device's methods: find, list, get, save, run, diff, retire (§4.5, §5.8)
resource mhp://<id>/descriptor, mhp://<id>/<path> descriptor and package resources of devices this session has opened

MHP errors surface as MCP tool results with isError: true and the structured mhpError body, so the model sees why a write was refused and can adjust. A refused call inside an mhp_run script returns the same body.

npx openmhp-cli setup                                        # scientist's laptop: runtime, skills, harness registration
mhp-mcp                                                  # the lab in ~/.openmhp/fleet.json; mhp_lab fills it
mhp-mcp --directory http://directory:18900               # big lab: nothing loaded until searched
mhp-mcp --directory http://directory:18900 --http 18800  # over HTTP for remote harnesses: bearer token required
mhp-mcp thermo=http://bench:18921 arm=http://arm-pc:18921   # small lab: cards indexed at start

The bridge is the lab's MCP server. Any harness that speaks MCP over stdio or HTTP (Claude Code, Codex, OpenClaw, Hermes, Claude Science, Open Science, or a custom agent) connects to it and sees the same eleven tools.

10.2 Command line

mhp http://bench:18921 describe
mhp http://bench:18921 read block_temperature lid_closed
mhp http://bench:18921 write target_temperature 95
mhp http://bench:18921 invoke run_protocol '{"steps":[...],"cycles":30}' --wait
mhp http://bench:18921 invoke open_lid --approved      # the person at this terminal confirms
mhp http://bench:18921 estop "smoke from lid"

The CLI is the debugging and shell-scripting surface, and what an agent reaches for inside a Bash tool.

10.3 Code files (SDK)

from openmhp.client import Lab

lab = Lab({"arm": "http://arm-pc:18921", "thermo": "http://bench:18921"})
arm, thermo = lab["arm"], lab["thermo"]

with arm, thermo:                                    # leases on both, renewed by a heartbeat
    arm.write("speed", 30)
    arm.wait(arm.invoke("pick_plate", location="deck_A1"))
    arm.wait(arm.invoke("place_plate", location="thermocycler"))
    result = thermo.wait(thermo.invoke("run_protocol", steps=PCR, cycles=30))
    arm.wait(arm.invoke("pick_plate", location="thermocycler"))

An agent writes a file like this when a protocol must run for hours or step faster than its own reasoning loop. The devices then execute it without the model in the loop; the model reads the result, or gets a notification when something trips.