OpenMHS

Docs

Install it, watch the demo, run a gateway, then connect a device and an agent. Everything here works with simulated devices first.

Install

OpenMHS ships two programs: openmhs (the command line, which can also run the gateway) and openmhsd (the gateway only). Both are one self-contained binary with the virtual lab built in.

No release has been published yet. Until one is, build from source. Release archives for Windows, Linux and macOS, and the Python package on PyPI, are prepared but not published.

You need Rust 1.89 or newer from rustup.rs, and Git. Python 3.10+ is needed for the Python SDK, examples and Python adapters.

git clone https://github.com/KNambiarDJsc/Open-MHS
cd Open-MHS
cargo install --locked --path crates/openmhs-cli    # installs openmhs and openmhsd
openmhs --version

To work on OpenMHS rather than use it, build in place and run from target/:

cargo build -p openmhs-cli
./target/debug/openmhs --version         # Windows: .\target\debug\openmhs.exe
PlatformStatus
Windows 11 x86_64Built and tested locally and in CI
Linux x86_64 (Ubuntu)Built and tested in CI
macOS arm64Configured in CI and the release build; not yet confirmed
macOS x86_64In the release build; untested
Windows: install the Microsoft C++ Build Tools ("Desktop development with C++") first; Rust needs its linker. Build from PowerShell, not Git Bash: Git's own link command hides Microsoft's link.exe and the build fails with link: extra operand.

Full guide: docs/installation.md.

Quickstart

1. See it work (about 10 seconds). A guided run of a real gateway over its API:

openmhs demo

2. Run the gateway yourself. In terminal 1:

openmhs serve --sim

In terminal 2, as the operator, allow an agent to use the simulated power supply and create its credential. The token is printed once.

openmhs devices list
openmhs grants add --id agent-psu --principal agent:demo --device sim.psu-01 --capability '*' --ops read,write --effect allow
openmhs tokens create agent:demo --any-scope

Then act as the agent.

# Linux / macOS
export OPENMHS_TOKEN=omhs_...
openmhs exec sim.psu-01 power.output.voltage.setpoint --value 5V     # refused: jumps too fast
openmhs exec sim.psu-01 power.output.voltage.setpoint --value 2V     # applied and read back

# PowerShell
$env:OPENMHS_TOKEN = "omhs_..."
openmhs exec sim.psu-01 power.output.voltage.setpoint --value 5V
openmhs exec sim.psu-01 power.output.voltage.setpoint --value 2V

The 5 V request ends DENIED command.slew_exceeded and nothing is sent to the device. The 2 V request ends VERIFIED -> succeeded (confirmed by readback). Last, as the operator (without the token):

openmhs audit verify

The simulated devices are sim.thermo-01 (temperature sensor), sim.psu-01 (power supply) and sim.axis-01 (linear stage). State resets when the gateway restarts.

Connect your own device

The gateway knows a device from a profile (a JSON file) or an adapter (a small program). Nothing reaches it unless a grant allows it.

1. Pick how OpenMHS talks to it

Your deviceUseStart with
Speaks Modbus TCP (PLCs, meters, drives, chambers)a profileopenmhs profile new modbus
Speaks MQTT (ESP32, Raspberry Pi, IoT nodes)a profileopenmhs profile new mqtt
Speaks SCPI over TCP or serial (power supplies, meters, scopes)a profileopenmhs profile new scpi
Serves a JSON HTTP API (your own firmware, a small web service)a profileopenmhs profile new http
Has its own API or SDK (for example OctoPrint)a Python adapterPython adapter SDK
Needs Rust, or a protocol not listeda Rust adapteropenmhs adapter new my-device
OPC UA server, ROS 2 topicsthe bundled adapters (read-only, observe-only)OPC UA, ROS 2

A bare microcontroller has to run firmware that speaks one of these first; firmware/esp32-mqtt-node is a complete ESP32 example with its profile.

2. Describe it

openmhs profile new mqtt --id lab.node-01      # a working starter profile
openmhs profile check lab.node-01.json         # checks the file; connects to nothing

Edit three parts: connection (where the device is), descriptor.capabilities (what it can do: type, unit, range, side effect, readback, freshness; be strict, these are enforced), and mapping (register, topic, command or URL for each capability).

3. Start the gateway, simulator first

openmhs serve --profile-sim lab.node-01.json     # simulated: safe to experiment
openmhs serve --profile lab.node-01.json         # the real device at "connection"

4. Decide what is allowed

Nothing is allowed at first, not even for the operator. Give an agent exactly what it needs:

openmhs grants add --id agent-node --principal agent:demo --device lab.node-01 \
  --capability '*' --ops read,write --effect allow --max-side-effect reversible --reason "first device"
openmhs tokens create agent:demo --scope 'lab.node-01:*:read' --scope 'lab.node-01:*:write' --ttl 7d

Use --effect require_approval when a person should confirm each command (openmhs approve <id>). The token's scopes are a second ceiling: it can do nothing the grants do not also allow.

5. Use it

openmhs read lab.node-01 air.temperature
openmhs exec lab.node-01 fan.on --value true          # VERIFIED, confirmed by readback
openmhs exec lab.node-01 vent.position --value 140%   # DENIED, outside 0..100 %
Before a real device, read docs/hardware and the hardware guidelines. Those guides are unverified.

Full guide: docs/connect-a-device.md.

Connect an AI agent over MCP

openmhs mcp is an MCP server on stdin and stdout. It is a client of a running gateway and holds one agent token, never the operator's. For Claude Code:

claude mcp add openmhs -e OPENMHS_TOKEN=<token> -- openmhs mcp

Any other MCP host: a stdio server, command openmhs, argument mcp, environment OPENMHS_TOKEN.

{
  "mcpServers": {
    "openmhs": {
      "command": "openmhs",
      "args": ["mcp"],
      "env": { "OPENMHS_TOKEN": "<token>" }
    }
  }
}

The agent then sees tools such as list_devices, describe_device, read_state, check_command, submit_command, get_command, reconcile_command and the plan tools. Refusals come back as structured results with a suggested next step. Details: docs/mcp.md.

Python SDK

Standard library only, Python 3.10+. Not on PyPI yet; install it from the checkout.

pip install ./python
from openmhs import Client, Quantity

gw = Client.from_home(token="<agent token>")       # finds the running gateway
r = gw.read("sim.thermo-01", "temperature.ambient")
if r.usable:                                       # fresh enough to act on
    print(r.value)
receipt = gw.write("sim.psu-01", "power.output.voltage.setpoint", Quantity(2, "V"),
                   idempotency_key="step-1")
if receipt.confirmed:                              # verified by reading the device back
    print("done")
elif receipt.denied:                               # a refusal is a receipt, not an exception
    for reason in receipt.reasons:
        print(reason.code, reason.details)

Leases and plans are available too. See python/README.md.

Check what happened

As the operator (no OPENMHS_TOKEN):

openmhs commands list              # every command, its state and why
openmhs audit tail                 # the latest audit records
openmhs audit verify               # audit log intact: N records, hash chain verified

When something says no, the error code says why, and troubleshooting says what to do.

Watch an agent replan

A scripted agent, with no model and no API key, connects over MCP, hits a slew limit, reads the gateway's advice, and ramps instead:

cargo build -p openmhs-cli
python examples/agent-loop/mock_agent.py

An optional variant drives the same loop with Claude; see examples/agent-loop.

Run conformance

openmhs conformance --report-dir reports       # simulators, about 15 seconds, one JSON report each
openmhs conformance --profile adapters/openmhs-scpi/profiles/bench-psu.json
openmhs conformance --adapter-manifest adapters/python/psu-sim/manifest.json
The suite really sends commands and injects faults. Run it against fixtures and simulators only, never against equipment in use.

To list an adapter in the registry and the compatibility matrix, commit its report and an entry; see registry/README.md.

Write an adapter

openmhs adapter new bench-lamp --openmhs-path .

This creates a working adapter crate with a fake device and conformance tests. Follow the adapter guide and the walkthrough. Adapters in Python use the Python adapter SDK and run out of process; the OctoPrint adapter is a complete example.

Run the tests

The full list CI runs is on the guidelines page. The main ones:

cargo test --workspace                          # spec fixtures, policy, engine, gateway, CLI, MCP
python tools/check_schemas.py                   # schemas in Python (pip install jsonschema)
python -m unittest discover -s python/tests     # Python SDK, examples, parity (cargo build first)

Reference documents

These live in the repository and are the authoritative versions. An mdBook documentation site is built from them but not yet published.

DocumentCovers
ConceptsThe ideas in one page
Tutorial and first deviceStep-by-step introductions
CLI referenceEvery command
ConfigurationThe gateway home, settings
SimulatorsThe virtual lab and its faults
SpecificationIndex of the v0.1 draft and its JSON Schemas
Descriptors, unitsDevices, capabilities, constraints; the UCUM unit table
Lifecycle, errorsCommand states, idempotency, recovery; stable error codes
AuthorizationPrincipals, grants, approvals, defaults
Local APITransport, authentication, roles, methods
ConformanceLevels L0 to L3, fixtures, faults, requirements
Topology, plansCapability graph, leases, multi-device plans
Device profilesModbus TCP, MQTT, SCPI, HTTP
Compatibility matrixGenerated evidence per adapter
LimitationsWhat it does not do, and what is untested
Transport securityTLS for MQTT and HTTP; when plaintext is refused
Architecture decisionsWhy each design was chosen
ChangelogWhat changed; no release yet