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.