rei-lads-mcp
⚠️ v0.1.0-preview — early-feedback release. NOT for production use.API is not stable before v1.0.0. Breaking changes may land in any 0.x version.X.509 certificate authentication is not supported in the 0.1.x line(username/password only). See ROADMAP.md for Phase 5 items.
An MCP server that exposes OPC UA LADS (Laboratory Agnostic DeviceStandard) instruments to LLM agents with two Rei-native disciplinesbaked in:
- Approval-gated execution — every state-changing tool validates itsoperation name against a server-declared enum (LADS state-machinemethods, program templates). Arbitrary strings are rejected at theMCP boundary, before any network call fires.
- Witnessed provenance — every state-changing call emits a SHA-256record (initiator, node, operation, params, samples, timestamp) thatis persisted to a local append-only JSONL and retrievable by prefix.
Modeled on the LADS reference implementations atopcua-lads and grounded in theRei / D-FUMT approval-gate +witnessed by design.
Install
Requires Python 3.12+.
python -m venv .venv
./.venv/bin/pip install -r requirements.txt # Linux/macOS
./.venv/Scripts/pip install -r requirements.txt # Windows
Quick start
Boot a LADS reference server (for example, lads-viscometer fromopcua-lads/lads-server-collection),then run:
python server.py
This starts a stdio MCP server. Point any MCP client at it.
Programmatic sanity check (no MCP client required):
python test_tools_list.py # 15 tools registered, schemas generated
python test_live_phase4.py # end-to-end against a running LADS server on
# opc.tcp://localhost:4840
Tools (15)
Connection
lads_connect(alias, server_url, user?, password?, wait_timeout_s?, cert_path?, key_path?)lads_register_alias(alias, server_url, description?, user?, password?)lads_list_aliases()lads_connect_by_alias(alias, wait_timeout_s?)
Discovery (read-only)
lads_list_devices(alias)lads_get_functional_units(alias, device_index)lads_get_sensor_value(alias, device_index, unit_index, function_index)lads_get_state(alias, device_index, unit_index)lads_get_state_machine_methods(alias, device_index, unit_index)lads_list_program_templates(alias, device_index, unit_index)lads_list_results(alias, device_index, unit_index)
State-changing (emit witness records)
lads_call_state_machine_method(alias, device_index, unit_index, method_name)— validates method_name against server-declared inventorylads_start_program(alias, device_index, unit_index, template_name, properties?, samples?, supervisory_job_id?, supervisory_task_id?)— validates template_name against ProgramManager templates
Witness retrieval
lads_list_witness_recent(limit?)lads_show_witness(sha256_prefix)— min 4 characters
Approval-gate demo
lads_call_state_machine_method(alias, 0, 0, "Start")
# -> {invocation: "queued (async)", witness_hash: "..."}
lads_call_state_machine_method(alias, 0, 0, "ArbitraryExec")
# -> {error: "method 'ArbitraryExec' not declared by this state machine",
# valid_methods: ["Start", "StartProgram", "Stop"]}
The rejection happens at the MCP tool boundary — no OPC UA call is firedfor an operation name that the LADS server did not declare. Same patternfor lads_start_program(template_name=...).
Witnessed provenance
Every state-changing tool call emits a record like:
{
"initiator": "mcp-caller",
"node_alias": "viscometer",
"server_url": "opc.tcp://localhost:4840",
"device_browse_name": "myViscometer",
"unit_browse_name": "ViscometerUnit",
"operation": "StartProgram",
"template_name": "Analytical Method A (30rpm)",
"params": {"Speed": "30", "Duration": "60"},
"sample_context": [{"ContainerId": "vial-01", "SampleId": "sample-a",
"Position": "A1", "CustomData": "phase4-test"}],
"timestamp_utc": "2026-08-26T00:23:54.333266+00:00",
"phase": "accepted",
"sha256_hex": "174b5e1498a5d0cca55ce9291adf95f612161ed590b839258e700e994cba25ae"
}
Persisted to ~/.rei-lads/witness/YYYY-MM.jsonl (env overrideREI_LADS_WITNESS_DIR). Retrievable via the two lads_*_witness_*tools by SHA-256 prefix.
Related work
opcua-lads/lads-server-collection— LADS reference OPC UA servers (Viscometer / pH-Meter / Balance / Freezer / FT-NIR)opcua-lads/lads-client-py— the Python client this bridge wrapsTECTOS-JP/lab-visa-mcp— sibling MCP for PyVISA-controlled instrumentsfc0web/rei-lang— the Rei language project this bridge implements the discipline of- Rei-Certified program (draft) — the compliance surface this bridge is a candidate G1 pilot for
Preview status
- Phase 1-4 spike complete — 15 tools, L2 and L3 both green againsta live LADS viscometer sim
- Phase 4 fixes that are already in code: banner silencing at import,
Connection.initializedpolling,QualifiedNameunwrap - Phase 5 is not yet implemented — see ROADMAP.md
- API stability: not before v1.0.0 — expect breaking changes in 0.x
Full spike history is preserved in SPIKE-HISTORY.mdfor the record.
License
MIT — see LICENSE. LADS ecosystem convention.
Contributing
See CONTRIBUTING.md. External contributors willrequire a signed CLA once the Rei-project CLA is finalized.