industrial-mcp
An MCP server that gives AI hosts read access to industrial grain-facility data and safety-gated motor control.
List facilities and assets —
list_plants,list_silos,list_motors(optionally filtered by kind).Inspect sensor health —
get_silo_thermometryreturns fill, cable count, and min/avg/max grain temperature in °C.Check alerts —
get_active_alertslists active alerts, optionally filtered by severity.Control motors safely —
trigger_motor_actionstarts/stops motors; dry-run by default, requiresdry_run=False,operator_id,reason, and server-levelINDUSTRIAL_MCP_ALLOW_WRITES=trueto execute.Audit executed actions — successful motor commands are appended to an append-only JSONL audit log; dry runs are not logged.
Use different data sources — ships with a deterministic mock adapter and supports a live ESP32 thermometry adapter; unknown adapters cause startup failure rather than silent fallback.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@industrial-mcpWhat are the active alerts in plant 2?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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:
Tool schemas the model can understand and call correctly.
Safety preconditions that block dangerous actions even when the model is confident.
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.
The read path is verified end to end against real hardware: an
ESP32-S3 with DS18B20 probes on a OneWire mux, answering a model's
questions about grain temperature over the LAN. Warming one probe by
hand moved max_temp_c from 28.0 to 31.0 — and dropped the average,
because the other probe was in cold water. That measurement is why
these tools return min, max, and faulted_sensors instead of one
reassuring number. Evidence, including what is not verified:
docs/hardware-verification.md
(español).
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/ │
│ ├── base.py ← PlantAdapter protocol (the contract) │
│ ├── mock.py ← default, deterministic demo data │
│ └── esp32.py ← HTTP to a live SiloScan module │
└──────────────────────────────┬──────────────────────────────────────┘
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
[MQTT broker] [internal REST API] [historian / TSDB]Related MCP server: MCP PLC Core
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.jsonThen in Claude Desktop:
¿Qué silos tienen alerta crítica hoy?
Claude calls list_plants → get_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@latestThis 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 |
| read | List facilities the server can see. |
| read | Silos at a plant with capacity in tons. |
| read | Latest thermometry snapshot (min/avg/max °C). |
| read | Motors at a plant, optionally filtered by kind. |
| read | Active alerts, filterable by severity. |
| read | Find modules on the LAN when one stopped answering. Never writes config. |
| 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:
The LLM explicitly sets
dry_run=False.The call includes an
operator_idand areason.The server itself was started with
INDUSTRIAL_MCP_ALLOW_WRITES=true.Every precondition in
safety.evaluate_motor_actionpasses.
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 contract every adapter must satisfy lives in
src/industrial_mcp/adapters/base.py as a PlantAdapter Protocol —
list_plants, list_silos, get_silo_thermometry, list_motors,
get_motor, get_plant_context, get_active_alerts,
apply_motor_action. Motor records must carry plant_id; the tool
layer resolves plant context from the motor rather than from a
constant, so a motor without one is refused instead of evaluated
against the wrong plant.
INDUSTRIAL_MCP_ADAPTER selects which one runs. It knows mock
(deterministic, shipped, used by CI) and esp32 (HTTP to a live
SiloScan thermometry module). An unrecognized name raises at startup —
the server never falls back to the mock, because answering questions
about a plant you are not connected to is worse than refusing to start.
INDUSTRIAL_MCP_ADAPTER=esp32 \
INDUSTRIAL_MCP_ESP32_HOST=banco-silo3.local \
INDUSTRIAL_MCP_ESP32_DEVICE_ID=banco-silo3 \
INDUSTRIAL_MCP_ESP32_CABLES=0,8 \
uv run industrial-mcpINDUSTRIAL_MCP_ESP32_CABLES defaults to channel 0 alone — each
channel costs a OneWire conversion, so the default is cheap rather than
complete. Leaving it out silently drops every other probe.
Use a name, not an address. Firmware 0.5.0+ publishes
<device_id>.local over mDNS. DHCP will move the address out from under
any file you write it into — the bench module took three different
leases in three reboots, and every stale config in this repo's history
traces back to that.
INDUSTRIAL_MCP_ESP32_DEVICE_ID is optional and turns on self-healing:
when the configured host stops answering, the adapter looks the module
up by name, then by subnet sweep, and adopts the new address only if
the device identifies itself as that id. Without it the adapter
reports device_unreachable instead of guessing — a sweep cannot tell
"my module moved" from "some other module answered", and silo 3's tools
reading silo 7's sensors would produce numbers that look perfectly
reasonable and come from the wrong bin.
The scan_devices tool exposes the same sweep to the model, read-only:
it reports what answered and never edits configuration. Scanning is
restricted to private address space and to ranges of /22 or smaller.
Two things the esp32 adapter does that are worth copying into your
own adapter, both of them about not laundering a sensor fault into a
plausible number:
A reading counts only when the firmware classified it
validand the temperature is actually present. Averaging a missing reading as 0 °C is how a hot silo reads cool.An unreachable module returns
{"error": "device_unreachable"}, never an exception and never a stale number wearing a fresh timestamp. The timestamp comes from the machine doing the read, because the module's clock is only right after an NTP sync.
To talk to a different plant, drop a sibling module implementing the
Protocol and add its name to build_adapter in server.py.
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 testsCI 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.
Is this only a mock? Where's MQTT / OPC UA?
No longer only a mock — the esp32 adapter reads a live device over
HTTP, and the read path is verified against it. MQTT and OPC UA are
still absent, and they are usually plant-specific anyway: the broker
URLs, topic structures, and ACLs you can publish publicly are usually
zero. The PlantAdapter Protocol is the extension point.
So the safety gate is proven against real equipment?
Against a real pin, yes. Firmware 0.4.0 exposes /api/relay, and
the four-step escalation was run against it: dry run left the pin low,
a call without operator_id was rejected with the pin low, a read-only
server rejected it with the pin low, and only the fourth call drove it
high. Be precise about the limit, though — that is a logic-level GPIO
with an LED on it, not a three-phase fan with inrush current and
interlocks. What was proven is that the gate stops a real actuator from
moving; motor sequencing is a different problem.
How do you know the relay actually switched?
Because the module is asked, not trusted. GET /api/relay reports the
level read back off the pad next to the command it was given, and
apply_motor_action decides success from the reading. A 200 response
with the pin still low comes back as a fault. An actuator that only
repeats the order it was given is not telemetry, it is an echo.
Status
Read and write paths both verified against hardware. Not 1.0 — the shapes are stable, but expect breaking changes in non-public APIs.
Area | State |
| Shipped, deterministic, used by CI |
| Verified against a live module — evidence |
Safety gate + audit log | Verified against a physical pin — dry run and both rejections left it low |
Actuator fault detection ( | Triggered on hardware — feedback wire pulled; safety refused with every gate open |
Real motor (inrush, interlocks) | Not attempted — the verified actuator is a GPIO pair with an LED |
MQTT / OPC UA adapters | Not started |
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.
Available Tools
6 toolsget_active_alertsA
List active alerts at a plant, optionally filtered by minimum severity.
| Name | Required | Description | Default |
|---|---|---|---|
| plant_id | Yes | ||
| min_severity | No | info |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. 'List' implies a read-only operation, and no side effects or limitations are hidden. However, it does not disclose behavioral details such as output sorting, pagination, or definition of 'active', though these are minor for a simple list.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single concise sentence that is front-loaded with the action, zero filler, and no repetition of schema properties.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the simple two-parameter read-only tool and the presence of an output schema, the description sufficiently covers the core functionality and optional filtering. It lacks only minor contextual guidance such as sourcing plant_id from list_plants, which is not critical.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate. It adds meaning by linking 'plant' to plant_id and 'optionally filtered by minimum severity' to min_severity, explaining the filter concept. It does not elaborate on enum values or defaults, but the schema provides those.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'List', the resource 'active alerts', and the scope 'at a plant', distinguishing it from sibling tools that handle plants, silos, thermometry, motors, and motor actions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context for when to use the tool: when active alerts for a plant are needed. While it does not explicitly name alternatives or exclusions, the sibling tools are clearly different resources, so usage is unambiguous.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_silo_thermometryA
Return the latest thermometry snapshot for one silo.
Includes fill percent, cable count, and min/max/avg grain temperature in Celsius. Use this when the operator asks about a specific silo's health.
| Name | Required | Description | Default |
|---|---|---|---|
| silo_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It discloses the 'latest snapshot' nature, includes specific data fields (fill percent, cable count, min/max/avg temperature in Celsius), and implies read-only behavior. This adds value beyond the bare schema, though it could mention error cases or data availability.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three concise, front-loaded sentences, each serving a purpose: what it does, what it includes, and when to use it. No redundant language.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the simplicity of the tool (one parameter, read-only), the description is sufficiently complete. It lists key output fields, and an output schema is present for detailed return values. It doesn't address edge cases, but these are less critical for a straightforward read operation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description must compensate. It clarifies that silo_id identifies the specific silo for the request, but adds no format or example. This is minimal but adequate for a single required string parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Return the latest thermometry snapshot for one silo,' specifying the verb, resource, and scope. It distinguishes from siblings like list_silos or get_active_alerts by focusing on a single silo's thermometry data.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says 'Use this when the operator asks about a specific silo's health,' providing a clear when-to-use scenario. It doesn't mention exclusions or alternatives, but the context is sufficiently distinct from sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_motorsA
List motors at a plant. Optionally filter by kind.
| Name | Required | Description | Default |
|---|---|---|---|
| kind | No | ||
| plant_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
There are no annotations, so the description carries the full burden of disclosing behavioral traits. It states 'List' which implies a read-only operation, but provides no additional context such as pagination, ordering, authentication needs, or side effects. It is not misleading but is minimal, lacking any behavioral detail beyond the action itself.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, front-loaded sentence that states the action, object, context, and optional filter without any redundant words. Every part contributes meaning, and it is appropriately concise.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple, has an output schema, and the description covers the core function. However, it lacks usage guidance and behavioral context that would make it fully self-sufficient. It is adequate but has clear gaps in when-to-use and behavior disclosure, so it earns a mid-range score.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With schema description coverage at 0%, the description must compensate for parameter meaning. It does mention 'kind' as an optional filter, providing semantic value beyond the schema's plain enum. However, it does not describe 'plant_id' at all, though that is partially inferable from 'at a plant.' The description only partially compensates for the lack of schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific action ('List') on a specific resource ('motors') within a context ('at a plant'), and mentions an optional filter ('by kind'). This distinguishes it from sibling tools like list_plants, list_silos, and trigger_motor_action, which operate on different entities or actions.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives a clear context (motors at a plant, optionally filtered by kind) but does not explicitly say when to use this tool versus alternatives, nor mention any exclusions or prerequisites. The usage is implied rather than explicitly articulated, so it earns a mid-range score.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_plantsA
List industrial facilities the server can see.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description is brief and does not disclose side effects, authorization needs, or rate limits. The verb 'List' implies read-only, but with no annotations, the description carries the burden and provides only minimal behavioral context beyond the action itself.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single concise sentence that is front-loaded with the verb and resource. Every word earns its place with no unnecessary elaboration.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple list tool with no parameters and an existing output schema, the description adequately covers the essentials. It specifies the scope ('the server can see') and leaves return details to the output schema. Slightly more context about what qualifies as 'industrial facilities' could improve completeness, but it remains sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, so schema coverage is complete. The description adds no parameter details, but the baseline for zero-parameter tools is 4, and the description aligns with that baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb 'List' and identifies a clear resource 'industrial facilities', with the scope 'the server can see'. It distinguishes from siblings like list_silos and list_motors by targeting plants specifically.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is provided on when to use this tool versus alternatives. The description only states what it does, without mentioning exclusions, prerequisites, or that siblings like list_silos are for different resource types.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_silosB
List grain silos at a plant, with capacity in metric tons.
| Name | Required | Description | Default |
|---|---|---|---|
| plant_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden. It adds behavioral detail by indicating the output includes capacity in metric tons, but it does not state whether the operation is read-only, whether it returns all silos, or any other behavioral traits. This is slightly better than a tautology but still minimal for an unannotated tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, front-loaded sentence of nine words. Every word contributes meaning, with no redundancy or filler. It is appropriately sized for the tool's simplicity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple (one parameter, list operation), and an output schema exists, so return values do not need to be described. However, the description lacks usage guidance and provides only minimal behavioral context. It is adequate as a minimum viable description but leaves gaps in guidance and transparency for an agent selecting this tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It links the sole parameter plant_id to the phrase 'at a plant', which confirms that plant_id identifies the silo location. However, it does not explain the format, allowed values, or any other constraints, leaving the parameter semantics mostly to the schema's field name and title.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific verb ('List') and resource ('grain silos'), and adds the scope ('at a plant') and a relevant detail ('with capacity in metric tons'). This distinguishes it from siblings like list_plants (by resource), list_motors (by resource), and get_silo_thermometry (by listing vs. retrieving temperature data).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No explicit guidance is given about when to use this tool versus alternatives. The phrase 'at a plant' implies the plant_id parameter, but the description does not mention any exclusions or provide context for choosing list_silos over other tools (e.g., get_silo_thermometry).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
trigger_motor_actionA
Request a start or stop of an industrial motor.
Defaults to a DRY RUN: returns what would happen, evaluates safety preconditions, and lists warnings — without sending any command to the field. To actually execute:
Pass
dry_run=False.Pass
operator_id(recorded in the audit log).Pass
reason(short free-text justification).The server itself must be started with
INDUSTRIAL_MCP_ALLOW_WRITES=true; otherwise the call is rejected even when the LLM sets the flags.
Every executed command is appended to an audit log; dry-run calls are not logged.
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | ||
| reason | No | ||
| dry_run | No | ||
| motor_id | Yes | ||
| operator_id | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Given there are no annotations, the description carries full responsibility for disclosing behavior. It extensively covers the dry-run default, safety precondition evaluation, no field command on dry-run, audit logging for executed commands, and the server flag that gates writes. This is exceptional transparency for a mutation tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with a numbered list and every sentence provides distinct value. It is concise yet comprehensive, covering behavior, prerequisites, and security considerations without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an industrial control tool with no annotations, the description thoroughly covers execution requirements, safety behavior, and logging. The presence of an output schema reduces the need to explain return values. It is complete for its intended use.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It explains the meaning and purpose of dry_run, operator_id, and reason effectively within the execution steps. It does not explicitly define motor_id and action, but those are straightforward (motor_id from name, action from enum). This good partial compensation earns a 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Request a start or stop of an industrial motor' with a specific action and resource. This distinguishes it from sibling tools like list_motors and get_active_alerts, which are read-only.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit step-by-step instructions for executing a real action, including passing dry_run=False, operator_id, and reason, plus the server-side environment requirement. It does not explicitly mention when not to use the tool or compare it to alternatives, but the context is clear and practical.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
6 tool updates
v0.1.0- First observed
get_active_alerts - First observed
get_silo_thermometry - First observed
list_motors - First observed
list_plants - First observed
list_silos - First observed
trigger_motor_action
TDQS
Scored across 6 tools
Each tool targets a distinct resource and action: plants, silos, thermometry, motors, alerts, and motor actions. The only possible overlap is between list_silos and get_silo_thermometry, but one lists silos and the other retrieves data for a specific silo, so there is no ambiguity.
All tool names follow a consistent verb_noun pattern: list_plants, list_silos, list_motors, get_active_alerts, get_silo_thermometry, trigger_motor_action. The verbs are clear and the style is uniformly snake_case, making the set predictable and easy to navigate.
With 6 tools, the server is well-scoped for industrial monitoring and control. Each tool serves a clear purpose without excessive overlap or unnecessary additions, and the count is neither too thin nor too heavy for the domain.
The tool set covers the core workflows: listing assets, retrieving detailed sensor data, checking alerts, and issuing motor control commands with safety checks. Minor gaps exist, such as no dedicated tool for getting a single motor's detailed status or acknowledging alerts, but these do not critically hinder agent operations.
Maintenance
Related MCP Connectors
Hosted MCP server connecting claude.ai, ChatGPT and other AI apps to your own computer
MCP server unifying ERPs, CRMs, APIs and knowledge base for Claude, ChatGPT and Gemini.
An MCP server that provides an API to LLMs to manage their JumpCloud resources.
Cloud-hosted MCP server for durable AI memory
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceA local MCP server that connects Claude Code to your work environment through auditable tools for file operations, API calls, and command execution, with safety gates and configuration.-
- FlicenseBqualityBmaintenanceUniversal MCP server for industrial PLC communication, enabling AI agents to read sensors, alarms, status, setpoints, and write setpoints via adapters for Modbus, S7, or custom PLCs.6-
- AlicenseAqualityFmaintenanceMCP server for industrial PLC integration, enabling AI to read tags, monitor alarms, and interact with Allen-Bradley ControlLogix PLCs via natural language.9MIT
- AlicenseNot gradedqualityCmaintenanceThe first and only MCP server for PLC (Programmable Logic Controller) intelligence. Give any AI agent direct access to industrial automation data — ladder logic, tag databases, cross-references, fault root cause analysis, and sequence blockersMIT