Skip to main content
Glama
brayangcastro

industrial-mcp

industrial-mcp

An MCP server that gives Claude (or any MCP-compatible AI host) read access to industrial sensor data and safety-gated control over motors and actuators.

The hard part of Physical AI isn't getting a language model to talk about a factory — it's getting it to act on one without anyone losing sleep. That requires three boring things working together:

  1. Tool schemas the model can understand and call correctly.

  2. Safety preconditions that block dangerous actions even when the model is confident.

  3. An audit trail that survives the next post-mortem.

This repo is a working implementation of all three, in under 500 lines of Python, using FastMCP. Default mode is read-only and ships with a deterministic mock adapter modeling a 7-silo grain storage facility, so it runs in CI with no infrastructure.


Architecture

┌─────────────────────────────────────────────────────────────────────┐
│  MCP host  (Claude Desktop, Claude Code, Cursor, etc.)              │
│   user prompt ──► model decides which tool to call                  │
└──────────────────────────────┬──────────────────────────────────────┘
                               │  stdio  (JSON-RPC over MCP)
                               ▼
┌─────────────────────────────────────────────────────────────────────┐
│  industrial-mcp  (this repo)                                        │
│                                                                     │
│   server.py ── wires tools, env config, audit log                   │
│      │                                                              │
│      ├── tools.py ── 5 read tools + 1 write tool (dry-run default)  │
│      │                                                              │
│      ├── safety.py ── preconditions, advisory warnings              │
│      │                                                              │
│      ├── audit.py ── append-only JSONL of executed actions          │
│      │                                                              │
│      └── adapters/                                                  │
│            ├── mock.py    ← default, deterministic demo data        │
│            ├── mqtt.py    ← (your impl) live ESP32 fleet            │
│            └── rest.py    ← (your impl) thin REST adapter           │
└──────────────────────────────┬──────────────────────────────────────┘
                               │
            ┌──────────────────┼──────────────────┐
            ▼                  ▼                  ▼
   [MQTT broker]      [internal REST API]   [historian / TSDB]

Related MCP server: Robonine MCP Server

Quick start

Run the server, talk to it from Claude Desktop, see it work.

# 1. Run the server with the mock adapter (no infra required)
uvx industrial-mcp@latest

# 2. Add this to ~/Library/Application Support/Claude/claude_desktop_config.json
#    (macOS) — see examples/claude-desktop-config.json

Then in Claude Desktop:

¿Qué silos tienen alerta crítica hoy?

Claude calls list_plantsget_active_alerts → answers with the data. See examples/demo-transcript.md for a full session.

To enable live (non-dry-run) execution against the mock adapter:

INDUSTRIAL_MCP_ALLOW_WRITES=true uvx industrial-mcp@latest

This flag does not affect dry runs — those always work. It only unlocks the path where a tool call would actually mutate state.


Tools exposed over MCP

Tool

Type

Purpose

list_plants

read

List facilities the server can see.

list_silos

read

Silos at a plant with capacity in tons.

get_silo_thermometry

read

Latest thermometry snapshot (min/avg/max °C).

list_motors

read

Motors at a plant, optionally filtered by kind.

get_active_alerts

read

Active alerts, filterable by severity.

trigger_motor_action

write

Start/stop a motor — dry-run by default, safety-gated, audited.

Tool schemas live in src/industrial_mcp/tools.py. Keep their docstrings short and exact — they become the model's tool descriptions.


Why three layers of safety, not one

trigger_motor_action will only execute when all of the following hold:

  1. The LLM explicitly sets dry_run=False.

  2. The call includes an operator_id and a reason.

  3. The server itself was started with INDUSTRIAL_MCP_ALLOW_WRITES=true.

  4. Every precondition in safety.evaluate_motor_action passes.

Step 3 is the one that matters most. The model can hallucinate dry_run=False; the operator field can be spoofed by a clever prompt; the safety check can have a bug. But if the server was started read-only, none of that touches a motor. The deploy posture is the last word, not the prompt.

Every executed call is appended to an append-only JSONL audit log:

{"ts": 1779600000.12, "actor": "op-42", "action": "motor.start",
 "target": "fan-7-1", "outcome": "applied",
 "details": {"reason": "silo-7 at 32.1°C, manual fan-on"}}

Dry runs are not logged. They're not interesting and they'd dilute the signal.


Adapters

The mock adapter ships in src/industrial_mcp/adapters/mock.py and is the only one wired in the scaffold. It produces deterministic data so CI and demos behave identically.

To talk to a real plant, drop a sibling module (e.g. mqtt.py) implementing the same surface — list_plants, list_silos, get_silo_thermometry, list_motors, get_motor, get_plant_context, get_active_alerts, apply_motor_action — and select it with INDUSTRIAL_MCP_ADAPTER=mqtt.

A reference MQTT adapter (HiveMQ-compatible, paho-mqtt) is the next issue in the tracker.


Development

git clone https://github.com/brayangcastro/mcp-industrial-agent
cd mcp-industrial-agent
uv sync --extra dev
uv run pytest -v
uv run ruff check src tests

CI runs the same two commands on Python 3.11 and 3.12 — see .github/workflows/ci.yml.


FAQ

Why not just give the model raw API keys? Because then every prompt injection is one HTTP call away from a production motor. The MCP surface is narrow on purpose: the model sees six functions, not your AWS console.

Why a separate dry_run flag instead of a confirmation step? Confirmation steps add latency and break flow. The dry run returns the outcome the model would have caused — preconditions, warnings, state delta — as data the model can keep reasoning over. Live execution is then a one-line escalation, not a five-prompt dance.

Is the audit log enough for compliance? No. It's enough to reconstruct what happened. It is not enough on its own for IEC 62443, IATF 16949, or similar. Pair it with your plant's existing change-management system; this repo is a starting point, not a finished compliance story.

Mock adapter only? Where's MQTT / OPC UA? This scaffold deliberately stops at the shape of the surface. Real adapters belong in a follow-up issue and are usually plant-specific anyway — the broker URLs, topic structures, and ACLs you can publish publicly are usually zero.


Status

Active scaffold, not yet 1.0. The shapes are stable; expect breaking changes in non-public APIs until the first MQTT adapter lands.

Author

Built and maintained by Brayan Castro — Ing. Mecatrónica (ITESM Sonora Norte), operating BC Ingeniería from Guasave, Sinaloa. Background: 4+ years building IoT systems for agroindustrial grain handling (firmware ESP32 + thermocouple MUX + cloud), backend services for property management and POS, and conversational AI agents on top of Claude / GPT-4o. Reach out: info@ingebc.com.

License

MIT — see LICENSE.

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/brayangcastro/mcp-industrial-agent'

If you have feedback or need assistance with the MCP directory API, please join our Discord server