plc-ai-agent
Provides tools for monitoring and diagnostics of Siemens S7-1500 PLCs via OPC UA, and for generating, validating, and optionally compiling IEC 61131-3 SCL code through Siemens TIA Portal Openness.
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., "@plc-ai-agentcheck the PLC alarms and motor temperature, then tell me if anything needs attention"
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.
PLC AI Diagnostic & Code Agent
Local AI agent (Ollama + MCP) for PLC diagnostics and SCL code generation via OPC UA and TIA Portal Openness. A two-layer system that reads industrial PLC data and drafts IEC 61131-3 SCL code — 100 % local, no cloud, no API keys.
Industrial AI portfolio project by eLSeR17. Demonstrates the full IT/OT bridge: an OPC UA monitoring layer feeding a Model Context Protocol (MCP) server, and a code-generation layer that produces validated SCL with a mandatory human review gate.
Problem
Industrial plants run on Siemens S7-1500 PLCs that publish live process data over OPC UA. Turning that data into predictive diagnostics and maintainable control code still requires a specialist staring at tag tables and a separate engineer hand-writing SCL. LLM agents could help with both — if they could read the PLC and write valid, reviewable SCL.
Official vendor AI offerings (e.g. Siemens' Eigen Engineering Agent) are cloud-based, come with licensing limits, and their terms prohibit scripted automation — and many plants cannot let process data leave the shop floor at all. The answer is a local, open-source agent: the LLM runs on your own hardware and your data never leaves your network.
Related MCP server: tiacommander-mcp
Solution (two layers)
Layer | Purpose | Status |
Layer 1 — Monitoring | OPC UA client reads PLC tags; an MCP server exposes 7 read-only tools ( | ✅ Phases 1–2 |
Layer 2 — Code generation | A local ReAct agent (Ollama) instructs a deterministic SCL generator: the LLM selects a template, never writes SCL freely. Output is validated syntactically and, on Windows, can be compiled through an optional TIA Portal Openness bridge. Never downloads to a controller without human review. | ✅ Phases 3–6 |
Non-negotiable safety invariant: the agent is read-only on the PLC in Layer 1, and in Layer 2 its output is validated and reviewed by a human before anything reaches a controller. See Security.
Demo
All outputs below are real, produced by scripts/cli.py against the bundled
simulator (no hardware, no network, fully reproducible).
1. PLC diagnostics (live tags + status)
$ python3 scripts/cli.py estado --json
{
"plc": "opc.tcp://127.0.0.1:4840",
"mode": "SIMULATOR",
"last_scan_ms": 50.0,
"uptime_s": 0.0
}
$ python3 scripts/cli.py tags motor.temp_C --json
{
"tags": [
{
"name": "motor.temp_C",
"value": 62.47086715698242,
"unit": "°C",
"timestamp": "2026-09-11T15:55:16.172664+00:00",
"quality": 1
}
]
}
$ python3 scripts/cli.py alarmas --json
{ "alarms": [] } # all 10 monitored tags inside their thresholds2. Historical series (local CSV historian)
$ PYTHONPATH=src python3 -m plc_ai.data_gen --days 3 # generate history CSV
wrote 4320 rows -> data/historical/history.csv
$ python3 scripts/cli.py historico motor.temp_C --json
{
"series": [
{
"tag": "motor.temp_C",
"points": [
{"t": "2026-09-09T15:50:00+00:00", "v": 62.47086728988105},
{"t": "2026-09-09T16:00:00+00:00", "v": 64.58467365158069},
... (432 points total for 3 days)
]
}
]
}3. CMMS work-order lifecycle (JSON + SAP-PM-style CSV)
$ python3 scripts/cli.py crear-ot --maquina motor --prioridad alta \
--descripcion "Motor 3 temp high" --accion "Check cooling fan" --json
{ "id_ot": "e4077595", "maquina": "motor", "prioridad": "alta",
"estado": "abierta", "accion": "Check cooling fan" }
$ python3 scripts/cli.py ot list --json
[ { "id_ot": "e4077595", "maquina": "motor", "prioridad": "alta",
"estado": "cerrada", "resultado": "reparado" },
{ "id_ot": "3add8851", "maquina": "bomba", "prioridad": "media",
"estado": "abierta" } ]
$ python3 scripts/cli.py ot close 3add8851 --resultado reparado \
--observaciones "Seal replaced, no leak" --json
{ "id_ot": "3add8851", "estado": "cerrada", "resultado": "reparado" }
$ python3 scripts/cli.py export-csv --output cmms_export.csv
Exported 2 orders to cmms_export.csv
$ cat cmms_export.csv
id_ot,maquina,prioridad,estado,creada_en,cerrada_en,resultado
e4077595,motor,alta,cerrada,2026-09-11T15:46:51,2026-09-11T15:46:57,reparado
3add8851,bomba,media,cerrada,2026-09-11T15:46:52,2026-09-11T16:03:17,reparado4. SCL generation + validation
$ python3 scripts/cli.py scl generador --plantilla proteccion_termica \
--objetivo "Protect conveyor motor from overheating" \
--parametros '{"limite": 85, "histeresis": 3}' --json
{
"scl": "// ------------------------------------------------------------------\n// FB_ProteccionTermica - thermal protection with hysteresis\n// GENERATED by plc-ai-agent (template). Review before download.\n// Objetivo: Protect conveyor motor from overheating\n// parametros: limite=85, histeresis=3\n// ------------------------------------------------------------------\nFUNCTION_BLOCK FB_ProteccionTermica\n\nVAR_INPUT\n temp_actual : REAL; // current measured temperature\n temp_max : REAL; // alarm trip threshold\n temp_hyst : REAL; // hysteresis band (e.g. 2.0)\n hist_reset : BOOL; // manual reset of the latched alarm\nEND_VAR\n\nVAR_OUTPUT\n alarma : BOOL; // latched high-temperature alarm\n en_servicio : BOOL; // TRUE while block is armed\nEND_VAR\n\nVAR\n alarma_interna : BOOL; // raw trip flag\nEND_VAR\n\nBEGIN\n IF temp_actual > temp_max THEN\n alarma_interna := TRUE;\n ELSIF temp_actual < (temp_max - temp_hyst) THEN\n alarma_interna := FALSE;\n END_IF;\n IF alarma_interna THEN\n alarma := TRUE;\n END_IF;\n IF hist_reset THEN\n alarma := FALSE;\n END_IF;\n en_servicio := TRUE;\nEND_FUNCTION_BLOCK",
"advertencias": [
"Code generated from template 'proteccion_termica': review the logic against the real machine before downloading to the PLC.",
"The LLM selects the template and parameters; the generator produces exact SCL. The LLM never writes SCL freely (ADR-015)."
]
}
$ python3 scripts/cli.py scl validar data/fixtures/scl_good.scl --json
{ "valido": true, "errores": [], "warnings": [...] }
$ python3 scripts/cli.py scl validar data/fixtures/scl_bad.scl --json
{ "valido": false,
"errores": [
"unbalanced IF/END_IF (1 vs 0)",
"missing ';' at end of assignment line: 'alarma := TRUE'",
"found '=' where ':=' may be required: 'alarma = FALSE'"
], "warnings": [...] }The validator catches exactly the three intentional errors in the bad fixture — deterministic, no LLM involved.
5. TIA Openness bridge (optional, Windows) — honest failure mode
# Bridge URL not configured -> controlled error, never a crash
$ PYTHONPATH=src python3 -c "from plc_ai.openness import OpennessBridgeClient; \
print(OpennessBridgeClient().health_check())"
{'ok': False, 'error': 'Openness bridge not configured'}Architecture
┌───────────────── Layer 1 · MONITORING ─────────────────┐
│ │
S7-1500 ◄─────┤ OPC UA (port 4840) asyncua simulator ◄────────┤
(real PLC, │ │ (3 machines / 10 tags, │
native UA) │ ▼ deterministic, seed=42) │
│ OpcUaClient.read_tags() ── value + unit + QUALITY │
│ │ │
│ ▼ │
│ MCP server (stdio) ◄── ReAct agent (Ollama LOCAL) │
│ 7 tools qwen2.5-coder:7b, urllib-only │
└─────────────────────────────────────────────────────────┘
│ selects template (+params)
▼
┌───────────────── Layer 2 · CODE GENERATION ─────────────┐
│ generate_scl() → validate_scl() │
│ (deterministic templates, (IEC 61131-3 structural │
│ LLM NEVER writes SCL freely) validator) │
│ │ │
│ ▼ (optional, Windows only) │
│ Openness bridge (HTTP/JSON) → TIA Portal → PLCSIM │
│ │ │
│ ▼ │
│ HUMAN REVIEW ──► download to controller (never auto) │
└─────────────────────────────────────────────────────────┘Real-PLC integration notes:
docs/REAL_PLC_GUIDE.mdTIA Openness integration contract:
docs/TIA_OPENNESS_GUIDE.mdFull architecture decision log:
docs/DECISIONS.md
Stack
Python 3.11+ (CI runs 3.11 and 3.12)
asyncua — OPC UA client/server (the standard Python OPC UA implementation)
MCP SDK (
mcp>=2.0,MCPServer, stdio transport)Ollama — local LLM (
qwen2.5-coder:7b), reachable only inside the Docker network ashttp://ollama:11434urllib only for the LLM/HTTP clients — no heavyweight LLM SDK
Standard library for the CLI, CMMS and history layers
Getting started
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
pytest tests/ -q # 165 tests, no network, no LLM needed
# Diagnostics against the bundled simulator
python3 scripts/cli.py estado
python3 scripts/cli.py tags all
python3 scripts/cli.py alarmas
python3 scripts/cli.py historico motor.temp_C
# Generate history data for historico (example CSV also committed in data/fixtures/)
PYTHONPATH=src python3 -m plc_ai.data_gen --days 7Full agent run (local LLM via Ollama)
The agent simulates the MCP client: it decides which tools to call, reads the results and answers with grounded diagnostics:
# Inside the Docker network where Ollama lives (see .env.example)
OLLAMA_HOST=ollama:11434 OLLAMA_MODEL=qwen2.5-coder:7b \
python3 scripts/e2e_agent.py "Mide la temperatura del motor y si es
alta, genera un bloque SCL de proteccion termica."No Ollama running? The agent degrades gracefully to a controlled error answer
instead of hanging — see docs/LIVE_EVAL.md.
MCP server (for any MCP-capable client)
export PLC_USE_SIMULATOR=true
PYTHONPATH=src python3 -m plc_ai.mcp.server # stdioThe seven tools:
Tool | Input | Output |
|
|
|
| — |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Configuration is 100 % env-driven — see .env.example:
PLC_ENDPOINT/OPCUA_ENDPOINT, PLC_NAMESPACE/OPCUA_NS,
HIST_DATA_DIR, OT_DIR, PLC_USE_SIMULATOR, ALLOW_WRITE_TO_PLC,
OLLAMA_HOST, OLLAMA_MODEL, AGENT_MAX_STEPS, OPENNESS_BASE_URL.
Facts / Hypotheses / Estimates
Honest engineering — this is an AI/OT bridge; hardware validation is pending.
Type | Claim | Evidence |
FACT | 165 tests pass, deterministic |
|
FACT | A real local LLM (Ollama | Live e2e documented in |
FACT | The SCL validator detects exactly the 3 intentional errors of |
|
FACT | The simulator speaks standard OPC UA on |
|
FACT | TIA Portal Openness requires Windows + a license (V18+); its vendor terms prohibit scripted automation | Reviewed in |
HYPOTHESIS | The same OPC UA client works against a real S7-1500 with the same tag configuration | Not tested — no PLC in the dev environment (tag/namespace mapping documented in |
HYPOTHESIS | The Openness bridge compiles generated SCL in Windows + TIA V20 without changes | Not tested — requires Windows + TIA license |
HYPOTHESIS | Simulated tag curves are representative of real machine signals | Validated for signal shapes (sinusoid + drift + injected faults); not against real vibration/temperature spectra |
ESTIMATE | OPC UA read latency ~30–100 ms | From asyncua loop timings on a local network (assumption: LAN, low load) |
ESTIMATE | One FB compiles in TIA in seconds (order of magnitude) | Not measured — no Windows environment |
Limitations
No real hardware tested. Everything runs against the deterministic simulator; the real-PLC path is implemented but unverified (see
docs/REAL_PLC_GUIDE.mdfor the exact steps).Compilation stops at syntax on Linux. Layer 2 ends at the SCL validator; a real compile needs Windows + TIA Portal Openness + license.
Simulator scope. 3 machines / 10 tags with 9 alarm rules — not every failure mode of a real plant.
Local LLMs can hallucinate. Mitigated three ways: the LLM selects one of 8 deterministic templates (it never writes SCL freely), readings are only trusted when
quality == 1and the timestamp is fresh, and the agent is trained (system prompt) to answer with HECHOS/HIPÓTESIS instead of inventing.Single-user, stdio MCP. No HTTP transport, no multi-user session model.
Security
No cloud, no API keys. The LLM runs locally (Ollama in the Docker network); nothing leaves the shop floor.
Read-only by default.
ALLOW_WRITE_TO_PLC=false; there is no write path implemented at all.Never downloads to production autonomously. SCL output requires human review; the optional Openness bridge has no scheduler and no auto-deploy.
Only placeholders in
.env.example; real.envis gitignored.
Roadmap
Validate against a real S7-1500 (tag mapping + namespace; checklist ready in
docs/REAL_PLC_GUIDE.md).Close the compile loop on Windows (Openness bridge + PLCSIM).
More SCL templates (PID, interlocking), alarm-correlation diagnostics, multi-session MCP (HTTP transport).
Related portfolio projects
plc-ai-agent is part of a public AI-engineering portfolio by
eLSeR17. Sibling projects demonstrate
complementary skills:
alpha-agent— LLM agent with function-calling, guardrails and external evaluations.smart-contract-rag— production RAG over smart-contract audits with golden-dataset evals and a regression guard.evalforge— standalone evaluation framework (dual judge, regression guard) that black-box-evaluates the agent projects.pdm-agent— predictive-maintenance agent: sensor ML pipelines feeding an LLM work-order layer.
Contributing
Issues and PRs are welcome. Keep changes small and testable: every PR must
keep pytest green and ruff check clean. Both run in CI.
License
MIT — see LICENSE. The generated SCL is yours; review it before download (that is not a license term, it is the safe thing to do).
This server cannot be deployed
Maintenance
Related MCP Connectors
Query Allen-Bradley and Siemens PLC projects, live tag values, and analyses in plain English.
Industrial glossary, protocol reference, technical search and OEE calculation. Read-only.
Cross-OEM industrial machine intelligence: identity, normalization, automation, attestation.
Read-only AI coding tools for change verification, release readiness, capacity, and guidance.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceIndustrial-grade MCP server for Siemens TIA Portal (V17–V21). 120+ tools for PLC programming: create blocks (FB/FC/OB/DB), manage tags, compile, download, and simulate with PLCSim Advanced. On-premise, sovereign-AI compatible.18MIT
- FlicenseNot gradedqualityAmaintenanceMCP server that connects AI assistants to Siemens TIA Portal via the Openness API. AI-assisted PLC programming, project management, hardware configuration, cross-reference analysis, and deployment. 19 tools, 230 actions.35-
- AlicenseBqualityAmaintenanceProvides AI agents with safe, governed read access to industrial control systems (OPC-UA, Modbus, S7, Mitsubishi, MTConnect, MQTT/Sparkplug) plus cross-protocol diagnostics for troubleshooting data breaks, alarm floods, and unhealthy tags.21531MIT
- AlicenseNot gradedqualityCmaintenanceEnables live tag read/write access to Rockwell Automation Logix5000 controllers over EtherNet/IP without requiring Studio 5000 Logix Designer, including discovery, tag listing, and batch operations.MIT