Specification · section 5
Primitives
All requests are JSON-RPC 2.0. Method names are namespaced with /, as in MCP.
| Primitive | Methods | Direction |
|---|---|---|
| Lifecycle | initialize, ping | client → server |
| Describe | device/describe {detail, select} | client → server |
| Resources | resources/list, resources/read {path} | client → server |
| Directory | directory/search, directory/get, directory/stats | client → directory server |
| Signals | signals/list, signals/read, signals/subscribe | client → server |
| Settings | settings/list, settings/write | client → server |
| Actions | actions/list, actions/invoke, jobs/status, jobs/list, jobs/cancel | client → server |
| Safety | safety/limits, safety/estop, safety/reset | client → server |
| Session | session/acquire, session/release | client → server |
| Notifications | notifications/signals/update, notifications/settings/changed, notifications/jobs/*, notifications/safety/* | server → client |
| Elicitation | elicitation/confirm (optional, §7.2) | server → client |
5.1 initialize
→ {"jsonrpc":"2.0","id":1,"method":"initialize",
"params":{"protocolVersion":"2026-09-09","clientInfo":{"name":"claude-lab","version":"1.0"}}}
← {"jsonrpc":"2.0","id":1,"result":{
"protocolVersion":"2026-09-09",
"device":{"id":"thermocycler-01","class":"thermocycler","make":"SimBio","model":"TC-96"},
"capabilities":{"signals":true,"settings":true,"actions":true,
"subscribe":true,"lease":true,"estop":true}}}
Servers MUST respond to initialize before any other method. Capabilities are booleans; a client MUST NOT call a method whose capability is false.
5.2 device/describe
Returns the descriptor (§4) at the requested detail tier (card, summary or full, §3.1) or the named items in select, plus state, one of idle, busy, fault, estop. Hosts SHOULD call this with summary before operating a device, then select the items they will use.
5.3 Signals: read
→ {"method":"signals/read","params":{"names":["block_temperature","lid_closed"]}}
← {"result":{"values":{"block_temperature":22.0,"lid_closed":true},"ts":1788991266.5}}
Omitting names reads every signal. signals/subscribe asks the server to push notifications/signals/update for the named signals; over HTTP these arrive on the /events stream, over stdio as interleaved notification lines.
5.4 Settings: write
→ {"method":"settings/write","params":{"name":"target_temperature","value":95}}
← {"result":{"ok":true,"name":"target_temperature","value":95}}
→ {"method":"settings/write","params":{"name":"target_temperature","value":200}}
← {"error":{"code":-32010,"message":"target_temperature=200 above max 105","data":{"min":4,"max":105}}}
Optional params: approved: true for confirm settings, and dryRun: true to run every gate, touch nothing, and return what would have been written.
5.5 Actions and jobs
actions/invoke returns immediately with a job. The device does the work; the agent polls or subscribes.
→ {"method":"actions/invoke","params":{"name":"run_protocol",
"params":{"steps":[{"temp":95,"hold_s":30},{"temp":58,"hold_s":30},{"temp":72,"hold_s":45}],"cycles":30}}}
← {"result":{"job":{"id":"job_04324008","action":"run_protocol","state":"queued","progress":0.0}}}
→ {"method":"jobs/status","params":{"id":"job_04324008"}}
← {"result":{"job":{"id":"job_04324008","state":"running","progress":0.43}}}
... later, unprompted ...
← {"method":"notifications/jobs/finished","params":{"job":{"id":"job_04324008","state":"done",
"progress":1.0,"result":{"cycles_completed":30,"final_block_temperature":72}}}}
Job states run queued → running → done | failed | cancelled. A failed job puts the device in fault until safety/reset. A busy device refuses a second non-concurrent invoke with DeviceBusy.
5.6 Safety
safety/limits returns the effective limits, interlocks and approval levels in one call, for hosts that want to show the envelope to a human.
safety/estop MUST stop all motion and energy output as fast as the hardware allows, cancel every running job, and put the device in estop. It MUST succeed regardless of lease, approval level or interlock state. It is the one call an agent may always make.
safety/reset returns the device to idle after estop or fault. It MUST fail with DeviceBusy while any job is still running.
5.7 Session leases
session/acquire {ttl} gives the calling client exclusive write and invoke rights until ttl seconds elapse or session/release. Reads, ping, describe and estop are never blocked by a lease. Leases let an orchestration script own several devices for the duration of a protocol without a second agent interleaving commands.