Industrial-AIOps Energy
PYPI · IAIOPS-ENERGY · SCANNED SEP 20
Industrial-AIOps energy edition: IEC-104/DNP3/IEC-61850 read-only OT connectors on iaiops.core.
Available components
How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. How we score → Why this is hard to score →
Supply Chain Security99
- No malware found by supply-chain analysis.Pass
- No known CVEs affecting this package version or its production dependencies.Pass
- Runs hatchling.build at install time, a recognised native-build step with no shell scripting around it. View diagnostics → Pass
- 4 of 42 dependencies flagged as unhealthy. View diagnostics → Partial
Provenance & Transparency32
- Source repository is publicly reachable at the declared URL. View diagnostics → Pass
- Provenance check failed: no build-provenance attestation is published. See how to fix → View diagnostics → Fail
- License check failed: no license is declared. See how to fix → Fail
- Actively maintained (last published 48 days ago).Pass
- Disclosure check failed: no security disclosure policy was found in the source repository. See how to fix → Fail
Schema Quality & AI Usability58
- AI-judged instruction clarity (excellent).Pass
- Context-footprint check failed: tool/resource definitions use about 24357 tokens (~304/item across 80 items; 80 tools + 0 resources), over budget; trim descriptions and params. See how to fix → Fail
- Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management80
- Stability observed for 24 of 30 days with no destabilising changes; credit accrues until the full window elapses.Partial
Tool Coverage71
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 0% of tool parameters carry a description.Fail
- Structured output schemas are declared (1% of tools); any adoption earns full credit.Pass
Tool Safety100
- No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
- All 3 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.Pass
- An AI judge read all 81 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
- Implements a supported MCP spec version (2025-11-25); the latest is 2026-07-28.Pass
How do I install the Industrial-AIOps Energy MCP server?
Industrial-AIOps Energy runs locally as a PyPI package, launched with uvx iaiops-energy. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.
pypi · iaiops-energy
claude mcp add industrial-aiops-iaiops-energy -- uvx iaiops-energy
{
"mcpServers": {
"industrial-aiops-iaiops-energy": {
"command": "uvx",
"args": [
"iaiops-energy"
]
}
}
} {
"servers": {
"industrial-aiops-iaiops-energy": {
"command": "uvx",
"args": [
"iaiops-energy"
]
}
}
} codex mcp add industrial-aiops-iaiops-energy -- uvx iaiops-energy
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"industrial-aiops-iaiops-energy": {
"type": "local",
"command": [
"uvx",
"iaiops-energy"
],
"enabled": true
}
}
} openclaw mcp add industrial-aiops-iaiops-energy --command uvx --arg iaiops-energy
mcp_servers:
industrial-aiops-iaiops-energy:
command: "uvx"
args: ["iaiops-energy"] {
"McpServers": {
"industrial-aiops-iaiops-energy": {
"Transport": "stdio",
"Command": "uvx",
"Arguments": [
"iaiops-energy"
]
}
}
} assistant mcp add industrial-aiops-iaiops-energy -t stdio -c uvx -a iaiops-energy
{
"mcpServers": {
"industrial-aiops-iaiops-energy": {
"command": "uvx",
"args": [
"iaiops-energy"
]
}
}
} Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.
- 19 Sept 26 −3
- Stability: pass → 0.77 functional
- 18 Sept 26 0
- Stability: 0.97 → pass security
- 17 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 93 to 97. That category is still filling its 30-day observation window: 28 days of observed history at the previous scan, 29 at this one. The score rises as the window fills, whether or not the server changes. Other categories moved too: Schema Quality & AI Usability fell 1.
- 15 Sept 26 +15
- Malware scan: unverified → pass ▲ security
- 14 Sept 26 −14
- Malware scan: pass → unverified ▼ security
- 12 Sept 26 −2
- Stability: pass → 0.80 functional
- 11 Sept 26 0
- Stability: 0.97 → pass security
- 10 Sept 26 +1
- Schema quality: 21133 → 23544 ▼ functional
Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.
Captured 20 Sept 2026 · Analysed pypi/iaiops-energy@0.1.12
Provenance No attestation
The registry publishes no build provenance for this version, so there is nothing to verify.
| Result | No attestation |
|---|---|
| Ecosystem | pypi |
Background: How many MCP packages publish verified provenance →
Install scripts 1 script
| Hook | Tier | Command |
|---|---|---|
| build_backend | allowlisted | hatchling.build |
Background: Why install scripts are a supply-chain risk →
Dependencies 42 packages
| Packages resolved | 42 |
|---|---|
| Stale | 3 |
| No linked repository | 1 |
| Tree resolution | Complete |
Background: SBOMs and build attestations, explained →
The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →
investigation_show ~36
[READ][risk=low] Re-read a saved investigation — the state it was left in.
| Name | Type | Req | Description |
|---|---|---|---|
| investigation_id | string | yes | – |
No output schema declared.
No examples provided.
learn_cause_weights ~386
[READ][risk=low] Learn a per-site RCA {cause: weight} profile from history. Derives a per-site cause-weight profile from a corpus of CONFIRMED past incidents so downtime_root_cause adapts to what THIS site's evidence actually predicts. Pure + explainable: each weight is the smoothed signal→cause precision relative to chance (>1 = evidence for that cause is reliable here, <1 = often misleading) — no black box. Anti-overfit: Laplace smoothing + a per-cause min-sample guard, and a fall-back to the shipped defaults when the corpus is too thin. Feed the returned 'cause_weights' to downtime_root_cause's cause_weights argument. Advisory: it tunes ranking, never executes anything. Args: history: Confirmed incidents — [{cause, signals:[...]}] where 'cause' is the known root cause and 'signals' are the cause labels the evidence pointed at (both from the copilot taxonomy: mechanical_fault, comms_loss, sensor_fault, material_starvation, quality_reject, changeover, utility_fault). min_samples: Minimum confirmed incidents before adapting at all (default 8); below it the defaults are kept. smoothing: Laplace pseudo-count pulling each estimate toward chance (default 1.0). Returns dict: {cause_weights:{cause: multiplier}, n_incidents, per_cause:{cause: {support, hits, precision, weight, note}}, rationale}. Example: learn_cause_weights(history=[{"cause":"mechanical_fault", "signals":["mechanical_fault"]}, {"cause":"comms_loss","signals":["comms_loss"]}]).
| Name | Type | Req | Description |
|---|---|---|---|
| history | array | yes | – |
| min_samples | integer | – | – |
| smoothing | number | – | – |
No output schema declared.
No examples provided.
line_relation_declare ~239
[READ][risk=low] Record that one asset feeds another — the second RCA axis. `[READ]` follows this repo's convention, where the tag is about PLANT state: it touches no device, exactly like `baseline_record_change` and `adopt_alias_map`, which are the same shape. It does write — a declaration about the line, into the site knowledge base. With time alone, an upstream stoppage produces a string of equally-confident downstream false causes, because on a line downstream co-occurrence is guaranteed whatever the cause. That guarantee is exactly why this is a declaration and not something inferred (D25): a person stating the line order needs no inference at all. ``by`` is required — a person is the evidence, and an edge with no author is indistinguishable from a guess a year later. Self-loops and cycles are refused here, where somebody can still fix them.
| Name | Type | Req | Description |
|---|---|---|---|
| by | string | yes | – |
| downstream | string | yes | – |
| site | string | – | – |
| upstream | string | yes | – |
No output schema declared.
No examples provided.
line_relations_list ~38
[READ][risk=low] The declared line order for a site, and what each asset feeds.
| Name | Type | Req | Description |
|---|---|---|---|
| site | string | – | – |
No output schema declared.
No examples provided.
mechanism_library_check ~200
[READ][risk=low] What a mounted fault-mechanism library says about one candidate cause. Three answers, and the difference between the first two is the whole point: * ``nothing_known`` — the library has never heard of this cause. **Not** "no objection": a knowledge base that knows nothing about something has not cleared it. * ``known``, not excluded — mechanisms for it apply here, with what would confirm each. * ``known``, excluded — every mechanism for it is inapplicable to this equipment, so the candidate can be ruled out. That is the strong move a ranker cannot make. It never confirms. Raising a candidate to `confirmed` comes from outside the ranking — a measurement, a reproduction, or a person (D29).
| Name | Type | Req | Description |
|---|---|---|---|
| cause | string | yes | – |
| protocol | string | – | – |
| site | string | – | – |
No output schema declared.
No examples provided.
mechanism_library_list ~37
[READ][risk=low] The fault mechanisms mounted for a site, and where each came from.
| Name | Type | Req | Description |
|---|---|---|---|
| site | string | – | – |
No output schema declared.
No examples provided.
monitor_changes ~317
[READ][risk=low] Capture only the value CHANGES of a point over a bounded window. Polls ``ref`` and returns only the changes (with timestamps), not every sample — the OT deadband-report pattern. Works across OPC-UA / Modbus / S7 / Mitsubishi MC / EtherNet/IP. Hard-capped by duration_s and max_changes (never an infinite loop). Args: ref: Point to watch — OPC-UA node id, Modbus address, S7 address string, MELSEC device, or Logix tag (per the endpoint's protocol). endpoint: Endpoint name from config. duration_s: Wall-clock window in seconds (1..120, capped server-side). interval_ms: Poll interval in milliseconds (>=50). deadband: Numeric change must exceed this to count (0 = any change). max_changes: Stop after this many changes (1..500, capped server-side). Returns dict: {endpoint, ref, duration_s, interval_ms, deadband, samples_polled, change_count, changes:[{value, previous, source_timestamp, wall_clock}]}. Example: monitor_changes(ref="ns=2;i=5", endpoint="line1", duration_s=20, deadband=0.5).
| Name | Type | Req | Description |
|---|---|---|---|
| deadband | number | – | – |
| duration_s | integer | – | – |
| endpoint | – | – | – |
| interval_ms | integer | – | – |
| max_changes | integer | – | – |
| ref | string | yes | – |
No output schema declared.
No examples provided.
oee_compute ~536
[READ][risk=low] OEE = Availability × Performance × Quality (+ loss/energy depth). Args: planned_time_s: Planned production time (seconds). run_time_s: Actual running time (seconds) — planned minus downtime. ideal_cycle_time_s: Ideal/nameplate cycle time per part (seconds). total_count: Total parts produced. good_count: Good (non-reject) parts produced. breakdown_time_s: Optional — unplanned-stop seconds (splits availability loss). setup_time_s: Optional — changeover/setup seconds (splits availability loss). minor_stop_time_s: Optional — minor-stop seconds (splits performance loss; the remainder is speed loss). startup_reject_count: Optional — startup/warm-up rejects (splits quality loss; the remainder is production rejects). actual_kwh: Optional — measured energy for this run; enables the energy block. baseline_kwh: Optional — expected/baseline energy for the actual-vs-baseline deviation verdict. emission_factor_kg_per_kwh: Optional — carbon factor (kg CO2e/kWh). Default is a flagged placeholder (see the tool's carbon note); pass the grid's value. energy_tolerance: ± band (fraction) for the over/under/on-target verdict. Returns dict: OEE factors + oee/oee_pct + inputs + losses, plus ``six_big_losses`` (breakdown/setup/minor-stops/speed/startup/production-reject time-ladder that sums with OEE to 100%) and, when ``actual_kwh`` is given, ``energy`` (kwh_per_unit, carbon, and baseline deviation). Example: oee_compute(planned_time_s=28800, run_time_s=25200, ideal_cycle_time_s=2.0, total_count=12000, good_count=11800, setup_time_s=1800, actual_kwh=940, baseline_kwh=880).
| Name | Type | Req | Description |
|---|---|---|---|
| actual_kwh | – | – | – |
| baseline_kwh | – | – | – |
| breakdown_time_s | – | – | – |
| emission_factor_kg_per_kwh | – | – | – |
| energy_tolerance | number | – | – |
| good_count | number | yes | – |
| ideal_cycle_time_s | number | yes | – |
| minor_stop_time_s | – | – | – |
| planned_time_s | number | yes | – |
| run_time_s | number | yes | – |
| setup_time_s | – | – | – |
| startup_reject_count | – | – | – |
| total_count | number | yes | – |
No output schema declared.
No examples provided.
oee_multidim ~339
[READ][risk=low] Aggregate OEE (+ optional energy) across dimensions. Args: records: Labelled records — {<dimension labels>, planned_time_s, run_time_s, ideal_cycle_time_s, total_count, good_count} plus optional actual_kwh / baseline_kwh to enable the energy rollup. dimensions: Dimension keys to group by (default ['machine','part','shift']); use ['shift'] for the classic by-shift energy comparison. emission_factor_kg_per_kwh: Optional carbon factor (kg CO2e/kWh); default is a flagged placeholder — pass the grid's published value. energy_tolerance: ± band (fraction) for the actual-vs-baseline verdict. Returns dict: {dimensions, group_count, mean_oee, worst_performers:[...], matrix:[{dimensions, oee, oee_pct, availability, performance, quality, energy?}]}. When any record carries energy, adds an ``energy_baseline`` block that flags cross-group deviation anomalies (tolerance + robust-outlier rules). Example: oee_multidim(records=[{"shift":"day","planned_time_s":28800, "run_time_s":25000,"ideal_cycle_time_s":2,"total_count":12000, "good_count":11800,"actual_kwh":940,"baseline_kwh":880}], dimensions=["shift"]).
| Name | Type | Req | Description |
|---|---|---|---|
| dimensions | – | – | – |
| emission_factor_kg_per_kwh | – | – | – |
| energy_tolerance | number | – | – |
| records | array | yes | – |
No output schema declared.
No examples provided.
onboarding_config_draft ~344
[READ][risk=low] The config.yaml endpoints a stored scan can justify. Closes the gap where a site scanned forty devices and then retyped all forty by hand. Reads a stored scan; writes nothing. `config.yaml` is edited by a person, exactly as with `iaiops tags apply`. **Every drafted field says whether the scan established it.** A field with `value: null` was NOT established, and its `caution` says what it is waiting for — do not fill one in and do not drop it. Dropping it lets the protocol default apply in silence, which is how a Modbus gateway gets read at unit 1 and shows a confident number for the wrong machine. Carry the cautions to whoever merges this; they are the content, not decoration. **Only CONFIRMED protocols become endpoints.** An open port means something is listening, not that it speaks the protocol; those hosts appear under `skipped` with the reason. `limits` states what this draft structurally cannot contain — a protocol's absence here is not evidence of its absence at the site. `tags` is always empty and this tool will not fill it. A scan finds devices; which point means run state or good count is process knowledge, and a wrong production counter yields a plausible OEE, which is worse than an error (D16). That confirmation is a person's, via `iaiops tags export` / `apply`. `scan_id` empty means the newest stored scan.
| Name | Type | Req | Description |
|---|---|---|---|
| db | string | – | – |
| scan_id | string | – | – |
No output schema declared.
No examples provided.
onboarding_status ~469
[READ][risk=low] Where this site is on the path from a network to an answer. One altitude below `site_readiness`. That one lists every scenario and what each is waiting for; this one answers the smaller, earlier question a caller actually has first — **which journey this site is on, and which single command comes next** on it. There are two, with different first steps: devices — no UNS yet: scan → endpoints → point list → what each point MEANS → collect → ask the question uns — data already in an MQTT/UNS broker: connect → audit the namespace → choose points → MEANS → collect → ask `track` is `auto` (derived from config.yaml), `devices` or `uns`. On `uns` a stored namespace audit forks it into B1 (audited clean) and B2 (governance is the work); until an audit is stored the result says B1/B2 is unknown rather than assuming either. When nothing is configured, or both kinds of endpoint are, the single step is a question with two `choices` — relay both to the caller and pick neither; the files on disk cannot say which kind of site this is, and neither can you. Derived from the local store and `config.yaml` every time; there is no onboarding state file, so a site that edits config.yaml by hand or restores a backup still gets a true answer rather than a remembered one. Every step carries its own `command`, including the ones still waiting. Show the caller the one under `next_command`; the rest are context, not a to-do list, and handing someone five commands is the same as handing them none. A step that is genuinely done stays `done` even when it sits behind the cursor — a site that hand-wrote config.yaml before ever scanning has real endpoints, and reporting otherwise to keep the sequence tidy would be a lie in the direction that makes this tool look more necessary. Contacts nothing. `db` overrides the local store path; empty means the iaiops store.
| Name | Type | Req | Description |
|---|---|---|---|
| db | string | – | – |
| track | string | – | – |
No output schema declared.
No examples provided.
pdm_forecast ~572
[READ][risk=low] Forecast a value's trend + time until it crosses a warn/alarm limit. The predictive step above baseline_check (which flags a violation that already happened): fits a robust Theil-Sen trend to the recent history and, if it continues, estimates the ETA to the nearest limit in the direction of travel — the early warning that makes maintenance predictive (inverter/turbine degradation, bearing drift, filter clogging). Refuses thin history; read-only, pure over the provided series; no device I/O. Beyond the trend, the result deepens into three explainable, stdlib-only views: a degradation 'pattern' (gradual vs sudden vs cyclic), a remaining-useful-life 'rul' block when degrading (linear + exponential extrapolation to the limit, a confidence band from the slope spread, and a fit R^2), and optional time-domain 'waveform' features (RMS/kurtosis/crest/... for vibration-type signals). Each states its own uncertainty rather than guessing. Args: series: Time-ordered samples: [{value, timestamp?}] (timestamp ISO-8601; if all present the ETA is in seconds, otherwise in samples). >= 30 numeric samples required. warn_high/alarm_high/warn_low/alarm_low: Optional limits; the forecast targets the nearest one in the trend's direction (rising → highs, falling → lows). imminent_within_s: ETA (seconds) at/under which status is 'imminent' (default 86400 = 24h). include_waveform: Add the time-domain 'waveform' feature block (default True). Set False for slow trend-only signals where vibration features do not apply. Returns dict: {status (insufficient_data|stable|degrading|imminent), samples, direction, slope_per_unit, unit (s|samples), current, limit:{name,value}, eta_to_limit, degradation:{pattern,confidence,rationale,metrics}, waveform:{rms,crest_factor,kurtosis,...} (when include_waveform), rul:{linear,exponential,eta_band,recommended_model,confidence,...} (when degrading)}. Example: pdm_forecast(series=[{"valu…
| Name | Type | Req | Description |
|---|---|---|---|
| alarm_high | – | – | – |
| alarm_low | – | – | – |
| imminent_within_s | number | – | – |
| include_waveform | boolean | – | – |
| series | array | yes | – |
| warn_high | – | – | – |
| warn_low | – | – | – |
No output schema declared.
No examples provided.
plc_program_drift ~406
[READ][risk=low] Has this program changed since its approved snapshot, and what moved? Three verdicts, and the wording of each is load-bearing. **identical** means the same SHA-256 and nothing else earns the word. **logic_changed** means the extracted structure differs — reported per block, naming which categories (variables / calls / branches / timers_counters) moved. **changed_outside_ extracted_structure** means the bytes differ while every block fingerprint matched: usually comments or formatting, but these parsers extract structure rather than parse a grammar, so a real change inside a construct they do not model looks identical from here. Calling that "documentation only" would be the comfortable reading of evidence that does not support it, so it is not called that, and it is not a clearance — line and comment counts are reported beside it so a reviewer can see which way it leans. Nothing here decides whether a change was authorised; that is change control's job. No device is touched — this reads a file a person exported. Args: path: The current exported program file to check. name: Tracked program name (defaults to the file's stem). against: Snapshot id to compare with; default is the latest. Returns dict: {program, name_source, baseline:{snapshot_id, taken_at, label, source_file}, current:{source_file}, verdict, content_changed, structure_changed, content_sha256:{before, after}, blocks:{added[], removed[], changed:[{block, kind, line, previous_line, changed[]}], unchanged}, totals:{block_count, line_count, comment_count}, parse_errors, note, advisory}. Example: plc_program_drift(path="~/exports/Line3_today.scl", name="Line3").
| Name | Type | Req | Description |
|---|---|---|---|
| against | – | – | – |
| name | – | – | – |
| path | string | yes | – |
No output schema declared.
No examples provided.
plc_program_history ~222
[READ][risk=low] Tracked programs, or one program's snapshot history. Local read of the program-baseline store — no file is parsed and no device is touched. Nothing is ever pruned automatically: a change-control history that quietly drops last quarter's baseline is worse than one that grows, and a stored row is block names, hashes and counts rather than source. Removing history is a deliberate act and is CLI-only (`iaiops program forget`) — deleting change-control evidence should not be one tool call away. Args: name: Tracked program name. Omit for the list of every tracked program. Returns dict (listing): {store, program_count, programs:[{program, snapshot_count, latest, latest_taken_at}]}; (one program): {store, program, snapshot_count, snapshots:[{snapshot_id, taken_at, source_file, content_sha256, label, note}]}. Example: plc_program_history(name="Line3").
| Name | Type | Req | Description |
|---|---|---|---|
| name | – | – | – |
No output schema declared.
No examples provided.
plc_program_outline ~319
[READ][risk=low] Structural outline of an EXPORTED PLC program file. Parses one exported text file (Siemens SCL/ST .scl/.st, AWL/STL .awl, Rockwell Studio 5000 .L5X — .txt is content-sniffed) and returns blocks (FB/FC/OB/DB/routines/AOIs) with VAR sections, IF/CASE branch inventory, timers/counters, and the call graph. Never uploads from a live PLC; reads exactly the named file (≤5 MB). Every element cites source_file + line (rung number for L5X ladder) — quote those citations when explaining. Malformed sections degrade to entries in parse_errors, never a crash. Args: path: Exported program file (.st/.scl/.awl/.l5x/.txt; must exist, ≤5 MB). Returns dict: {source_file, format, stats:{blocks, variables, call_edges, branches, timers_counters, comments, lines, parse_errors}, blocks:[{name, kind, language, line, end_line, variables (≤100, variables_truncated), calls, branches, timers_counters, networks, comment}] (≤50, blocks_truncated), call_graph:[{caller, callee, source_file, line}], parse_errors, citation_note}. Example: plc_program_outline(path="~/exports/Line3_Conveyor.scl").
| Name | Type | Req | Description |
|---|---|---|---|
| path | string | yes | – |
No output schema declared.
No examples provided.
plc_program_section ~224
[READ][risk=low] Source text of ONE named block from an exported program. Returns the exact source of a single block (FB/FC/OB/DB name for SCL/AWL; Program.Routine or routine name for L5X — rungs are rendered as '[rung N] ...'), capped at 200 lines with an explicit truncated flag, so the agent reads exactly the section it is explaining instead of guessing. Unknown block names fail with the list of available blocks. Args: path: Exported program file (.st/.scl/.awl/.l5x/.txt; must exist, ≤5 MB). block: Block/routine name (case-insensitive; quotes optional). Returns dict: {source_file, format, block, kind, start_line, end_line, lines_returned, truncated, source, parse_errors}. Example: plc_program_section(path="~/exports/Line3.scl", block="FB_Conveyor").
| Name | Type | Req | Description |
|---|---|---|---|
| block | string | yes | – |
| path | string | yes | – |
No output schema declared.
No examples provided.
plc_program_snapshot ~413
[READ][risk=low] Record an exported program's structure as a change baseline. A control program is a controlled document, and the usual way an undocumented change to one gets noticed is that somebody remembers. This gives the comparison a number: the file's SHA-256, plus a per-block structural fingerprint (name/kind/language, declared variables, calls, branch conditions, timers) that deliberately excludes line numbers, comments and block order — so adding a comment at the top of a file does not report the whole program as changed. Stored locally under the iaiops home as block names, hashes and counts — never a declaration, a source line or a comment — so the store is not a second copy of the program. Reads the named EXPORTED file only; never a live PLC upload. Re-snapshotting a byte-identical file records nothing and says so — a history padded with identical rows hides the rows that are not. Args: path: Exported program file (.st/.scl/.awl/.l5x/.txt; must exist, ≤5 MB). name: Program identity across exports. Defaults to the file's stem, and the result says which was used — the export path changes every time somebody opens the engineering station, the program does not. label: Short label, e.g. "approved v3.2 / MOC-118". note: Free note recorded with the snapshot. Returns dict: {status ('recorded'|'unchanged'), program, name_source, snapshot:{snapshot_id, taken_at, source_file, content_sha256, label, note}, block_count, snapshot_count, previous_snapshot}. Example: plc_program_snapshot(path="~/exports/Line3.scl", label="approved v3.2").
| Name | Type | Req | Description |
|---|---|---|---|
| label | string | – | – |
| name | – | – | – |
| note | string | – | – |
| path | string | yes | – |
No output schema declared.
No examples provided.
plc_program_visibility ~409
[READ][risk=low] Maintainability / operational-risk profile of a legacy PLC program. The "what am I inheriting?" view over one EXPORTED program (SCL/ST, AWL/STL, Rockwell L5X): folds the structural outline into documentation coverage, the least-commented blocks, blocks nothing references (possible dead code), the complexity hotspots, risky constructs (unconditional JMPs, retentive RTO timers, loops), and a TRANSPARENT additive risk score whose every point cites its reason. Structural only — it anchors an engineer's review of a line somebody else left behind, not a semantic understanding. Reads exactly the named file (≤5 MB); never a live PLC upload. Every finding cites source_file + line (rung number for L5X ladder). Args: path: Exported program file (.st/.scl/.awl/.l5x/.txt; must exist, ≤5 MB). Returns dict: {source_file, fmt, stats:{blocks, call_edges, line_count, comment_count, comment_ratio, variables, branches, timers_counters}, documentation:{comment_ratio, band ('well_commented'|'sparse'| 'undocumented'), uncommented_block_count, uncommented_blocks}, entry_points:[{name, kind}], unreferenced_blocks:[{name, kind, source_file, line}], complexity_hotspots:[{block, kind, score, branches, calls, timers_counters, source_file, line}], risky_constructs:{ unconditional_jumps, unconditional_jump_count, loops, loop_count, retentive_timers, retentive_timer_count}, risk:{score (0..100), band ('low'|'medium'|'high'), reasons[]}, parse_errors, note}. Example: plc_program_visibility(path="~/exports/Line3_Conveyor.scl").
| Name | Type | Req | Description |
|---|---|---|---|
| path | string | yes | – |
No output schema declared.
No examples provided.
plc_program_xref ~316
[READ][risk=low] Cross-reference one symbol in an exported PLC program. Finds every read/write/call/declare site of a symbol or absolute address (e.g. Motor_Run, "FB_Conveyor", DB10.DBX0.1, M0.0, Tank[2].Level) in one exported file, quoting the surrounding source line verbatim so the agent cites real code. Access classification is heuristic (op/regex based, not data-flow analysis): SCL ':='→write, '('→call; AWL T/=/S/R→write, L/A/O…→read, CALL→call; L5X OTE/OTL/OTU/RES and MOV-dest→write. For L5X, line is the rung number. Args: path: Exported program file (.st/.scl/.awl/.l5x/.txt; must exist, ≤5 MB). symbol: Symbol / tag / absolute address to trace (word-bounded match). Returns dict: {source_file, format, symbol, hit_count, hits:[{symbol, access, block, source_file, line, source_line}] (≤200), hits_truncated, by_access:{read, write, call, declare, reference}}. Example: plc_program_xref(path="~/exports/OB1.awl", symbol="M10.0").
| Name | Type | Req | Description |
|---|---|---|---|
| path | string | yes | – |
| symbol | string | yes | – |
No output schema declared.
No examples provided.
protocols_supported ~268
[READ][risk=low] Capability map — protocols, status, tools, connection params. Call this to discover what iaiops can do before choosing a protocol/tool. Lists implemented protocols (OPC-UA incl. HDA, Modbus, S7comm, Mitsubishi MC, MTConnect, MQTT/Sparkplug B full-decode, EtherNet/IP Logix) and the EtherCAT roadmap stub, plus cross-protocol analytics (OEE/downtime, asset inventory, CoV), each with its read/write tools and the endpoint params it needs. Also reports whether this server runs under the no-egress gate, so a model is TOLD the posture instead of having to infer it from tools it cannot see. Read/write authorisation is NOT a server posture here — it is the caller's decision; every call (read or write, MCP or CLI) is audited. Returns dict: {tool, posture, implemented_protocols:[...], roadmap_stubs:[...], protocols:[{protocol, status, library, transport, auth, read_tools, write_tools, params}], diagnostics:[...], analytics:[...], tool_counts, safety, write_note, no_egress_mode, no_egress_note}. Example: protocols_supported().
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
rca_corpus_from_maintenance ~419
[READ][risk=low] Turn a CMMS/work-order export into the RCA incident corpus. Auto-builds the labeled history learn_cause_weights needs from closed maintenance records: an explicit taxonomy cause column wins; else a built-in EN/中文 CMMS synonym table (extendable via 'synonyms'); else UNAMBIGUOUS keyword inference over the row's free text using the copilot's own cause keywords. Rows it cannot map land in 'unmapped' with the reason — never silently guessed. 'signals' come from an explicit column or the symptom/alarm text (may stay empty — no fabricated evidence). Pure + advisory; with learn=true the learned weights are included. Args: rows: Work-order records, one dict each. Recognized cause columns: cause / root_cause / failure_class / category / problem_code; free-text columns: description / problem / notes / comment / text / 故障描述; signal text: symptom(s) / alarm(s) / 现象. synonyms: Extra site vocabulary, e.g. {"spindle crash": "mechanical_fault"}; values must be taxonomy causes. learn: Also run learn_cause_weights on the mapped corpus (default true). min_samples: Passed to learn_cause_weights (default 8). smoothing: Passed to learn_cause_weights (default 1.0). Returns dict: {corpus:[{cause, signals}], n_rows, n_mapped, unmapped:[{row, reason, excerpt}], mapped_via, weights?, next_step}. Example: rca_corpus_from_maintenance(rows=[{"category":"轴承损坏", "symptom":"drive overload alarm"}], synonyms={"spindle crash":"mechanical_fault"}).
| Name | Type | Req | Description |
|---|---|---|---|
| learn | boolean | – | – |
| min_samples | integer | – | – |
| rows | array | yes | – |
| smoothing | number | – | – |
| synonyms | – | – | – |
No output schema declared.
No examples provided.
rca_narrate ~241
[READ][risk=low] Narrate a cited RCA verdict in plain language via an on-box LLM. Air-gapped: hands the already-computed, already-cited verdict to a LOCAL model (Ollama) that ONLY rephrases it — it never adds a cause, number, or citation (strict prompt; see docs/RCA.md). Read-only; no device I/O. Needs the extra + a running local model: pip install iaiops[ollama]. Args: verdict: An RCA verdict dict (e.g. the output of downtime_root_cause). base_url: Ollama server URL (default http://localhost:11434). model: Local model name (default 'llama3.1'). provider: LLM provider (currently 'ollama'). Returns dict: {provider, model, narration}. Example: rca_narrate(verdict=<downtime_root_cause output>, model="llama3.1").
| Name | Type | Req | Description |
|---|---|---|---|
| base_url | string | – | – |
| model | string | – | – |
| provider | string | – | – |
| verdict | object | yes | – |
No output schema declared.
No examples provided.
site_readiness ~313
[READ][risk=low] Which scenarios THIS site can run today, and what each gap needs. The companion to `protocols_supported`, one altitude down. That one says what the product can do; this says what this installation can do — and calling the first without the second is how an agent plans a scenario the site has no inputs for. Contacts nothing: no device, no network, no historian. It is derived from `config.yaml` and the local store, which is what makes it runnable against a site nobody has authorised you to probe — the site that most needs it. Three states, and the middle one carries the value: `ready`, `degraded` (it RUNS, on less than full evidence — root cause without a historian still ranks causes, it just cannot see the two hours before the stoppage) and `blocked`. `blocked_on` is the actionable half: one missing input usually unlocks several scenarios, ranked by how many. It never fills a gap in for you. Which tag is the production counter is process knowledge, and a wrong guess yields plausible-looking OEE numbers — considerably worse than an error (D16). Where a prerequisite cannot be supplied at all yet, the row says `not_yet_expressible` rather than implying somebody forgot to configure it. `db` overrides the local store path; empty means the iaiops store.
| Name | Type | Req | Description |
|---|---|---|---|
| db | string | – | – |
No output schema declared.
No examples provided.
stream_publish ~271
[READ][risk=low] Publish already-read normalized points to a message bus (NATS). Egress of data the agent already READ — NOT a control write. Each numeric point becomes a JSON message on ``<subject_prefix>.tag.<metric>``; non-numeric points are skipped (use a historian sink for text/state). Needs the extra: pip install iaiops[nats]. Args: points: Collected point dicts (e.g. from *_read_many): {ref/metric, value, timestamp, ...}. subject_prefix: NATS subject root (default 'iaiops'). servers: Comma-separated NATS server URLs (default nats://localhost:4222). token: Optional NATS auth token. tls: Use TLS to the broker. publisher: Bus kind (currently 'nats'). Returns dict: {publisher, subject_prefix, received, published, skipped_non_numeric}. Example: stream_publish(points=[{"ref": "line1.temp", "value": 21.5}], subject_prefix="plant").
| Name | Type | Req | Description |
|---|---|---|---|
| points | array | yes | – |
| publisher | string | – | – |
| servers | string | – | – |
| subject_prefix | string | – | – |
| tls | boolean | – | – |
| token | string | – | – |
No output schema declared.
No examples provided.
stream_publish_event ~221
[READ][risk=low] Publish one computed event (RCA verdict / alarm) to a message bus (NATS). Egress of a finding the brain already COMPUTED — e.g. an RCA verdict or an alarm episode — to ``<subject_prefix>.<subject>`` as JSON. NOT a control write. Needs: pip install iaiops[nats]. Args: subject: Event subject suffix (e.g. 'rca.verdict', 'alarm.flood'). event: The event payload dict (published as JSON). servers/token/tls/subject_prefix/publisher: bus connection (see stream_publish). Returns dict: {publisher, subject, published}. Example: stream_publish_event(subject="rca.verdict", event={"primary_cause": "seal"}).
| Name | Type | Req | Description |
|---|---|---|---|
| event | object | yes | – |
| publisher | string | – | – |
| servers | string | – | – |
| subject | string | yes | – |
| subject_prefix | string | – | – |
| tls | boolean | – | – |
| token | string | – | – |
No output schema declared.
No examples provided.
subscription_health ~336
[READ][risk=low] Health of a sequenced subscription feed (OPC-UA or Sparkplug B). Detects dropped notifications (sequence gaps), duplicates / out-of-order, a high republish-rejection rate, and overloaded channels — the classic Kepware "too many tags on one channel → republish/queue-flush dropouts" fault. Args: sequence: Sequence numbers actually received, in arrival order. republish_requested: How many republish requests were made. republish_rejected: How many were rejected (server couldn't keep up). tags_per_channel: {channel/endpoint: tag_count} — flags channels over the max. max_tags_per_channel: Density above which a channel is flagged (default 5000). wrap_at: Modulus for rolling counters (e.g. 256 for Sparkplug B seq); omit for monotonic OPC-UA counters. Returns dict: {received, missed_count, duplicate_count, out_of_order_count, republish_requested, republish_rejected, republish_reject_rate, overloaded_channels:[{channel, tags}], max_tags_per_channel, verdict ('ok'|'reordered'|'lossy'|'overloaded'), recommendation}. Example: subscription_health(sequence=[1,2,4,5], tags_per_channel={"ch1":7000}).
| Name | Type | Req | Description |
|---|---|---|---|
| max_tags_per_channel | integer | – | – |
| republish_rejected | integer | – | – |
| republish_requested | integer | – | – |
| sequence | array | yes | – |
| tags_per_channel | – | – | – |
| wrap_at | – | – | – |
No output schema declared.
No examples provided.
substation_event_analysis ~457
[READ][risk=low] Analyse a substation Sequence-of-Events for protection selectivity. Pure structural analysis over INJECTED events (no live protocol I/O, no endpoint): given relay pickups/trips, breaker open/close, lockouts and bus undervoltage with ISO-8601 timestamps, decide what tripped and whether protection coordinated — a selective trip (one zone contained), a non-selective backup operation (wider outage), or a breaker failure. Monitor-only, advisory, cite-first (every claim ties to a timestamped event). Args: events: SOE list of {ref, timestamp (ISO-8601), type, label?}; ``type`` is one of protection_pickup / protection_trip / breaker_open / breaker_close / lockout / bus_undervoltage (a free-text ``label`` is keyword-matched when ``type`` is absent or unknown). ``ref`` is the point name / IOA. breaker_fail_window_s: Breaker-failure timer — the tripped breaker's own open must be seen within this many seconds (default 0.25). backup_margin_s: Backup coordination margin — a breaker opening later than this past the first protection event is treated as backup operation (default 0.5). Returns dict: {events_analyzed, ignored, verdict, first_protection, first_breaker_open, breakers_opened, breaker_open_count, affected_refs, timeline, coordination{status, detail}, note}. ``verdict`` is one of selective_trip / backup_operation / breaker_failure / insufficient. Example: substation_event_analysis(events=[ {"ref": "R1", "type": "protection_trip", "timestamp": "2026-07-12T10:00:00Z"}, {"ref": "BK1", "type": "breaker_open", "timestamp": "2026-07-12T10:00:00.08Z"}]).
| Name | Type | Req | Description |
|---|---|---|---|
| backup_margin_s | number | – | – |
| breaker_fail_window_s | number | – | – |
| events | array | yes | – |
No output schema declared.
No examples provided.
tag_health ~203
[READ][risk=low] Rank tag offenders by bad-quality / flatline / range / anomaly. Args: tags: Per-tag dicts — {ref, label?, samples:[scalars or {value, good|quality}], warn_high?, alarm_high?, warn_low?, alarm_low?}. thresholds: Optional {ref: {warn_high, alarm_high, warn_low, alarm_low}} override. Returns dict: {evaluated, overall ('ok'|'warn'|'alarm'), offender_count, offenders:[{ref, label, samples, latest, flags:[...], anomaly_count, severity (0..3)}], results:[...]}. Flags include bad_quality, flatline, out_of_range_warn/alarm, statistical_anomaly. Example: tag_health(tags=[{"ref":"ns=2;i=5","samples":[70,71,70,99]}]).
| Name | Type | Req | Description |
|---|---|---|---|
| tags | array | yes | – |
| thresholds | – | – | – |
No output schema declared.
No examples provided.
uns_publish ~424
[READ][risk=low] Publish already-read normalized points to an MQTT broker / UNS. Egress of data the agent already READ — NOT a control write. Each numeric point becomes a JSON message on ``<topic_prefix>/<metric>``, with a dotted metric nested into topic levels (``line1.temp`` -> ``plant/line1/temp``) so a Unified Namespace stays browsable. Non-numeric points are skipped (use a historian sink for text/state). The topic is always derived from ``topic_prefix`` — it is never taken verbatim, so this cannot address a command topic. Needs the extra: pip install iaiops[mqtt]. Args: points: Collected point dicts (e.g. from *_read_many): {ref/metric, value, timestamp, ...}. topic_prefix: Root of the topic tree (default 'iaiops'); wildcards are stripped. host: Broker hostname or IP (default localhost). port: Broker port; 0 picks 8883 with TLS else 1883. username: Optional broker username. password: Optional broker password. use_tls: Use TLS to the broker. qos: MQTT QoS 0/1/2 (default 0, fire-and-forget); values outside 0-2 are clamped. retain: Ask the broker to retain the last value per topic (useful for a UNS). Returns dict: {publisher, topic_prefix, broker, received, published, skipped_non_numeric}. Example: uns_publish(points=[{"ref": "line1.temp", "value": 21.5}], topic_prefix="plant", host="10.0.0.5").
| Name | Type | Req | Description |
|---|---|---|---|
| host | string | – | – |
| password | string | – | – |
| points | array | yes | – |
| port | integer | – | – |
| qos | integer | – | – |
| retain | boolean | – | – |
| topic_prefix | string | – | – |
| use_tls | boolean | – | – |
| username | string | – | – |
No output schema declared.
No examples provided.
verify_determinism ~445
[READ][risk=low] Prove this engine's analysis is reproducible without a model. Runs a named suite of analyses (availability, production counts, the Six Big Losses, ISA-18.2 alarm load, control charts, the conservative baseline, the RCA copilot) over a pinned reference dataset, canonically encodes each result and digests it — twice in this process and, with ``subprocesses``, once more in each of two fresh interpreters started at different PYTHONHASHSEED values. That last arm is the one that catches a set or dict iteration order reaching a result; a single run never can. The socket API raises throughout, so a computation that reached for a device or a hostname fails here rather than quietly succeeding on a machine that happens to be online. ``sys.modules`` is checked afterwards for any model library — empty is the guarantee. Use it to answer "how do I know your AI didn't make this number up": the answer is that no model is in the path, and here is the SHA-256 that says so, reproducible on the customer's own box. Read-only; nothing is written unless the CLI (`iaiops verify determinism --out record.json`) is used to save the signable record for a validation file. Args: subprocesses: Also re-run in two fresh interpreters at fixed, different hash seeds (default True; adds roughly a second). Returns dict: {check, result:{verdict ('reproducible'|'not_reproducible'), suite_digest, dataset:{name, revision, digest}, checks:[{name, covers, digest}], arms:[{arm, suite_digest, matches_first_arm}], arms_disagreeing, model_modules_loaded, network}, context:{iaiops_version, python, platform, generated_at, ...}, note}. ``result`` is identical between runs; ``context`` is not — it records when and where this run happened. Example: verify_determinism().
| Name | Type | Req | Description |
|---|---|---|---|
| subprocesses | boolean | – | – |
Structured output declared, but exposes no named fields.
No examples provided.
What is the Industrial-AIOps Energy MCP server?
Industrial-AIOps Energy is an MCP server listed in the public MCP registry as io.github.industrial-aiops/iaiops-energy. Industrial-AIOps energy edition: IEC-104/DNP3/IEC-61850 read-only OT connectors on iaiops.core. This page covers its PyPI package (iaiops-energy).
Is the Industrial-AIOps Energy MCP server safe to use?
Industrial-AIOps Energy scores 73 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 20 September 2026. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.
What tools does the Industrial-AIOps Energy MCP server expose?
Industrial-AIOps Energy exposes 80 tools: protocols_supported, site_readiness, onboarding_status, onboarding_config_draft, health_summary, and 75 more. Their descriptions and schemas cost roughly 23,999 tokens of context every time the server is loaded.
Is the Industrial-AIOps Energy MCP server still maintained?
Industrial-AIOps Energy is still listed as active in the MCP registry. We last reached this channel on 20 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.