fc0web

rei-lads-mcp

Community fc0web
Updated

MCP bridge for OPC UA LADS lab instruments, with approval-gated execution and witnessed provenance. v0.1.0-preview.

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:

  1. 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.
  2. 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 inventory
  • lads_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

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.initialized polling, QualifiedName unwrap
  • 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.

MCP Server · Populars

MCP Server · New

    PSU3D0

    agent-spreadsheet

    MCP server for spreadsheet analysis and editing. Slim, token-efficient tool surface designed for LLM agents.

    Community PSU3D0
    pitiflautico

    NeoBrowser

    MCP server that drives real Chrome with your real logged-in sessions — genuine fingerprint (passes bot.sannysoft), human-like input, bot-wall aware. 43 tools, single static Rust binary.

    Community pitiflautico
    aeonfun

    Aeon MCP Server

    The most autonomous AI agent framework: runs unattended on GitHub Actions, self-healing skills, drives Claude Code, Grok, Codex & more. No approval loops. Configure once, forget forever.

    Community aeonfun
    nhadaututtheky

    NeuralMemory

    NeuralMemory stores experiences as interconnected neurons and recalls them through spreading activation, mimicking how the human brain works. Instead of searching a database, memories are retrieved through associative recall - activating related concepts until the relevant memory emerges.

    Community nhadaututtheky
    norrietaylor

    Distillery

    Team knowledge evaporates daily — pairing sessions, debugging context, architectural rationale lost to Slack. Distillery captures it at the point of creation, connects it into a living graph, and surfaces it conversationally. It monitors feeds, tracks what matters to your projects, and alerts you before you know to ask. A team brain that learns.

    Community norrietaylor