An AI agent that answers questions about a machine’s vibration data is providing information. That information can shape a maintenance decision, sometimes a costly one, and a person stands between the answer and whatever happens next. An agent that can open a work order acts on an external system itself. Its output becomes a record in a system that schedules people, reserves spare parts and sometimes takes a machine out of service. This article is about what has to change in the design when an agent moves from informing decisions to executing operations.
Disclosure: AI tools were used to help draft the English text, restructure the article, examine the source code and prepare the figure. I maintain the three open-source projects used as examples below. They are separate projects, and no end-to-end pipeline from sensor to maintenance system has been implemented or validated across them. I use them because their code can be inspected, including the places where it stops.
The first is an experimental proof of concept built around the STWIN.box sensor board: two Model Context Protocol (MCP) servers, one for acquisition and one with its own small vibration-analysis implementation, with Claude as the client and three Skills that guide monitoring, diagnosis and reporting.
The STWIN.box (STEVAL-STWINBX1) is a development kit from STMicroelectronics that carries several MEMS sensors, including a wideband three-axis accelerometer sampled at about 26.7 kHz. In the proof of concept, Claude asks the sensor server to configure the board and record an acquisition over USB, then loads the resulting files into the analysis server.
The second, Predictive Maintenance MCP, is a separate diagnostic tool server that does not run inside the first. The only bridge between them is an offline script that converts a board acquisition into signal files with a metadata companion.
The third, Machina, is a framework for maintenance agents. A domain model of assets, work orders and alarms, connectors to maintenance systems, its own agent runtime, and controls on write operations. It has not been integrated with either of the other two. Its OPC UA and MQTT connectors are how it reads plant data. MCP, by contrast, is only an interface between a model and tools, and the two should not be conflated.
Figure 3. Three independently implemented projects and the one connection this article proposes. Solid arrows are calls or data paths that exist in code. The dash-dot arrows are offline file conversion. The dashed arrow has not been implemented. Diagram prepared with AI assistance.
A vibration signal is a list of numbers until some facts about its acquisition are known, the sampling rate, the physical unit, the shaft speed, a trustworthy timestamp. Bearing diagnosis shows why. A local defect in a rolling-element bearing produces impacts at a characteristic rate, and each such rate equals the shaft’s rotation frequency multiplied by a factor fixed by the bearing’s geometry: the number of rolling elements, their diameter, the pitch diameter and the contact angle. There is one factor each for the outer race, the inner race, the rolling elements and the cage. The formulas are standard kinematics, described in Randall and Antoni’s tutorial on bearing diagnostics. The analysis then looks for spectral lines near the expected frequencies. Without the geometry or the shaft speed there is nothing to look for, and because real bearings slip slightly, even correct inputs give an approximate target.
What matters for agent design is what a tool does when one of these facts is missing. In the proof of concept, the diagnosis tool accepts a call without shaft speed. It then returns basic statistics, an empty list of diagnoses and a warning. Its description also tells the model not to invent a speed and to ask the operator. These are two different kinds of control. The description is an instruction the model may or may not follow. The empty list is enforced by code, and it is the accompanying warning that keeps “not assessed” from looking like “no fault found”.
Predictive Maintenance MCP treats the missing fact as a result in its own right. A severity verdict under ISO 20816–3 requires knowing whether the signal is acceleration or velocity, and the server will not infer that from the amplitude:
if declared_unit is None: iso_result = _refused_iso_block( signal_id=signal_id, reason=( "Signal unit not declared — ISO 20816-3 severity requires " "knowing whether the signal is acceleration ('g'/'m/s2') or " "velocity ('mm/s'/'m/s'); units are never guessed from " "amplitude." ),
Excerpt from diagnosis_pipeline.py*. The call continues with a remedy.*
The rest of the analysis still runs. Only the severity block comes back with status: "refused", a reason and a remedy. The reason for putting this in the result schema is stated in the model's docstring: a refusal written only to a log is one that an LLM client can lose.
One caveat applies to this whole layer. These computations are deterministic and reproducible, which is why they sit in specialised tools, but that does not make their output a diagnosis. A deterministic check can still support the wrong fault, or several at once, or flag a healthy machine. Its output is evidence, with its own criteria and limits.
In these projects the model interprets a request, selects tools, asks for what is missing, retrieves context, combines results and explains them to someone who may not read spectra. It does not perform the signal processing. That is an architectural choice and says nothing about whether language models can handle time series. A computation that has to be explicit, constrained and repeatable is placed in a component that can be tested on its own.
The weak point is the last step. A server controls what its tools return, not what the model writes afterwards. “The outer race is damaged” in a chat reply is the model’s narration of a frequency match and should not be read as a measurement.
Before any action, a phrase like “the cooling pump” has to be bound to one physical asset. Machina’s entity resolver scores candidates and sorts them into bands. A weak match or an exact tie between two candidates is not acted on, and the agent asks which asset is meant. A work order against the wrong pump looks correct and is hard to notice later.
Maintenance history raises a quieter problem: “no previous interventions” and “the history could not be read” are different facts. In Machina’s built-in alarm-to-work-order workflow, a failed history lookup does not stop the run. The step is recorded as skipped with its error kept, which is distinct from an empty result. The built-in notification does not mention history at all, though, so the technician sees neither the history nor its absence.
Here the three projects stop meeting. Predictive Maintenance MCP produces structured evidence, tested frequencies, candidate fault types, a severity verdict or a refusal, and the declared conditions of the measurement. Machina’s workflow starts from an alarm: an asset, a parameter name, a value, a threshold and a unit. Its failure analyzer then matches the parameter against known failure modes by rule. Spectral evidence does not fit into a single value and a threshold.
No interface between the two has been written. What can be said is what such a contract would have to guarantee. It would need to carry the asset and measurement-point identifiers, the provenance of the signal, the sampling and operating conditions, the indicators together with the criteria that produced them, the stated limitations, what was missing, and links to the supporting analysis.
Above all it would have to keep three states apart, valid evidence that the machine is operating normally, an explicit refusal to assess, and information that was simply unavailable. Each calls for a different response, and they are easy to collapse. If all three reach the next system as the same empty field, the distinction is lost and whoever reads the field has to guess. One plausible guess is that nothing was found, which is not what a refusal says.
The problem is not specific to vibration analysis or to these projects. It appears wherever one tool’s result becomes another system’s input, as a retrieval step that found nothing versus one that failed, a validator that declined to judge, a classifier that abstained. Tool protocols supply part of the answer. MCP, for instance, lets a tool declare an output schema and flag execution errors. A refusal is neither an error nor an ordinary result, though, so it has to be modelled deliberately and then kept intact by every component that passes it on, the language model included.
Machina places three controls on writes, and they differ in scope.
Sandbox mode intercepts writes in two places. Connector methods marked as writes raise an error before executing, and the workflow engine replaces the steps it classifies as writes with placeholders. That covers the write paths the framework knows about, by whichever route the call arrived. A custom connector method that is not marked may fall outside it. Human confirmation is narrower. It applies to mutating tool calls made in the agent’s LLM loop: the write waits for a yes and is withheld if the channel cannot ask or the sender is not identified. A workflow triggered directly is guarded by sandbox mode alone, and the framework’s MCP server has no confirmation step, because there the MCP client decides which calls run.
The third control is a check on the target of a work order. The asset named in the call must exist, must be one the current turn actually resolved, and must have been resolved with enough confidence. Existence alone would not be enough, because the model sees candidate identifiers in its context and can pick any of them.
These are controls inside the agent framework, and together they are still not an authorization model. Machina registers tools according to what its connectors can do, which is a statement about capability and not about who may use it. Its security notes say plainly that every valid token can call every registered tool and that client identity is not yet written to logs. A “yes” at a prompt does not establish who answered, or whether that person may raise a corrective order on a critical asset. The MCP specification draws a similar line: it asks clients to keep a human able to deny tool calls and, separately, requires servers to implement access controls.
The system that already answers those questions is the maintenance system itself, with its roles and approval states. What follows is a recommendation, and none of these projects has validated it against a production CMMS: the agent’s account should hold the narrowest rights that work, and an agent-created order should enter in a state that a planner still has to release.
Taken separately, these projects show that some boundaries can be made explicit in code. A tool that returns a refusal in place of an unsupported verdict, an agent that will not write against an asset it has not resolved.
The open problem is the crossing between diagnosis and maintenance, and it is a general one for tool-using agents. When a result moves into a system that acts on it, its limits and its refusals have to move with it. Until there is a contract that preserves what the evidence means, including what it does not say, connecting the two would be easy to do and hard to trust.
From Vibration Data to Work Orders: Designing Industrial AI Agents with Explicit Boundaries was originally published in Towards AI on Medium, where people are continuing the conversation by highlighting and responding to this story.