AgentStack
MCP verified MIT Self-run

Mcptoolkit For Codesys

mcp-zbest1000-mcptoolkit-for-codesys · by zbest1000

MCP server that lets Claude (or any Model Context Protocol client) drive the CODESYS V3.5 SP22 PLC IDE via its Python ScriptEngine - projects, POUs, build, devices, libraries, and live online control.

No reviews yet
0 installs
4 views
0.0% view→install

Install

$ agentstack add mcp-zbest1000-mcptoolkit-for-codesys

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution Used
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Mcptoolkit For Codesys? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

mcptoolkit-for-codesys

Drive the CODESYS V3.5 SP22 PLC IDE from Claude — a Model Context Protocol server.

[](https://github.com/zbest1000/mcptoolkit-for-codesys/actions/workflows/test.yml) [](https://github.com/zbest1000/mcptoolkit-for-codesys/releases) [](LICENSE)

A Model Context Protocol server that drives the CODESYS V3.5 SP22 IDE through its Python ScriptEngine. It lets Claude (or any MCP client) open projects, create POUs/DUTs/GVLs with methods and properties, build, add and version devices, manage libraries, and log into a live PLC to read, write, and force variables.

> New to this? Start with the [Setup Guide](SETUP.md) — a plain-English, > step-by-step install that assumes no programming experience. > > Want to understand how it works? The [docs/](docs/) folder explains > each part in plain language: [concepts](docs/concepts.md), the > [watch page](docs/dashboard.md), [reliability](docs/reliability.md), > [configuration](docs/configuration.md), and [security](docs/security.md).

Status — v0.2.2, verified end-to-end on a real CODESYS V3.5 SP22 Patch 1 instance. Everything from creating a project through the full online cycle (login → run → read live values → write → stop → logout) works against a running soft PLC, driven through the server's 82 tools. It is covered by 199 host unit tests plus 6 live integration tests — you can run the live ones yourself with MCPTOOLKIT_LIVE=1 pytest tests/integration. It should also work on SP19 and up wherever the scripting API matches, though only SP22 has been exercised. The full history is in [CHANGES.md](CHANGES.md).

Contents

  • [Feature highlights](#feature-highlights)
  • [Why another CODESYS MCP](#why-another-codesys-mcp)
  • [How it works](#how-it-works)
  • [Install](#install)
  • [Configure Claude Desktop](#configure-claude-desktop)
  • [Example: a first session](#example-a-first-session)
  • [Remote access over SSH](#remote-access-over-ssh)
  • [Environment variables](#environment-variables)
  • [Reliability & modal-dialog defense](#reliability--modal-dialog-defense)
  • [Observability](#observability)
  • [Tools](#tools)
  • [Version control](#version-control)
  • [What's verified](#whats-verified)
  • [Known limitations](#known-limitations)
  • [Roadmap](#roadmap)
  • [Security model](#security-model)
  • [Architecture notes & gotchas](#architecture-notes--gotchas)
  • [Acknowledgements](#acknowledgements)
  • [License](#license)

Feature highlights

  • Full IEC authoring — projects, folders, POUs (Program/FB/Function in any

IEC language), DUTs (struct/enum/union/alias), GVLs, FB methods + properties, declaration/implementation round-trip, variable-level editing, tree introspection, PLCopenXML import/export, and a git-friendly mirror export.

  • Build pipeline with structured messages (severity/source/position) and a

build.validate lint report that names root causes.

  • Task configuration — list/set interval, priority, watchdog; create tasks.
  • Device tools — search the 3000+ device repository, add devices

(PLC → fieldbus chains, e.g. EtherNet/IP), and device.update to fix device-descriptor library pins.

  • Library management — add/remove references, diagnose missing libraries,

search disk, and an install_missing orchestrator that auto-fixes version pins; install packages via APInstaller.

  • Online/runtime — login (user-supplied or env-var credentials), download,

start/stop/reset, read/write/force live variables, timestamped monitoring snapshots. Physical-impact ops are confirm-gated.

  • Reliability — heartbeat + liveness detection, hung-watcher auto-recovery,

process adoption (no duplicate IDEs), and a background modal-dialog guard.

  • Observability — structured JSON logs + an optional read-only web

dashboard, plus a hang-proof codesys.diagnose tool.

Why another CODESYS MCP

Earlier community CODESYS MCP projects inspired this one (see [Acknowledgements](#acknowledgements)), but it is a from-scratch implementation built around a few deliberate engineering choices that keep it stable on current service packs:

  • A single-threaded loop on the IDE's primary thread — no clr.AddReference

for threading and no execute_on_primary_thread (removed in SP21.5). It is the only model that stays reliable on recent service packs.

  • Atomic file-based IPC rather than a socket — requests and results are

exchanged as files (written *.tmp then renamed), so it survives a frozen IDE and never reads a half-written message.

  • Pydantic-validated wire contracts on the host side, so a malformed call

fails cleanly before it ever reaches CODESYS.

  • Self-registering handler modules — each tool group is one small Python file

that registers itself, which keeps the 82-tool surface easy to extend.

Breadth (82 tools across 10 areas), dependable unattended operation (see [Reliability](#reliability--modal-dialog-defense)), and a documented safety model (see [Security model](#security-model)) round it out — each covered in its own section below rather than repeated here.

How it works

 Claude ─── stdio ───▶ mcptoolkit_for_codesys.server (CPython 3.11+)
                              │
                              │ writes commands/.json
                              ▼
                       /
                              ▲
                              │ writes results/.json
                              │
                       CODESYS.exe --runscript=watcher.py  (IronPython 2.7)
                              │
                              ▼
                       CODESYS Scripting API
                       (projects, online, system, ...)

Two programs, two Pythons. CODESYS can only be scripted from inside its own embedded Python (IronPython 2.7), which is too old to speak modern MCP. So a modern-Python server handles Claude, and a small watcher running inside CODESYS does the actual work. The server supervises one long-lived CODESYS.exe running watcher.py on the IDE's primary UI thread (it yields via system.delay() and never touches System.Threading — the only model that stays stable on SP21.5+).

**The ` is the channel, not a scratch folder.** The two halves can't call each other directly, so they exchange messages as files in one shared folder: the server drops a request in commands/, the watcher writes the answer to results/. The same folder also holds watcher.ready (a PID marker), a watcher.heartbeat, a STOP sentinel, the staged watcher.py`, and the log. Three things follow:

  • Both halves must point at the same workdir, or they can't talk to each

other. (This is why a remote SSH setup pins --workdir on both sides.)

  • It is the trust boundary. File IPC has no authentication — whoever can write

into commands/ can drive CODESYS. Keep it a private, per-user folder; never a shared or network path (see [Security model](#security-model)).

  • One watcher per workdir. Two IDEs sharing one folder would grab each other's

messages and corrupt results, so a second server adopts a running watcher rather than spawning a rival (see [Reliability](#reliability--modal-dialog-defense)).

Why files instead of a socket? IronPython 2.7's networking is fragile and the IDE can freeze on a dialog; files survive both, and each write is atomic (*.tmp + rename) so a half-written message is never read.

Startup is lazy. CODESYS takes 30–90 s to open, so the server spawns it on the first tool call, not at launch — otherwise Claude's ~60 s connect timeout would trip every time. The first tool call is slow; the rest are fast.

Install

Requires:

  • Windows + CODESYS V3.5 SP22 installed via APInstaller (any SP19+ should also

work; only SP22 is the stated test target).

  • Python 3.11+ on the host.
cd "C:\path\to\mcptoolkit-for-codesys"
py -3.11 -m venv .venv
.venv\Scripts\activate
pip install -e .

For development (run the test suite):

pip install -e .[dev]
pytest

The host test suite (199 tests) covers: Pydantic schemas, install discovery from APInstaller JSON, the full tool registry shape, end-to-end IPC against a fake watcher, input validation, watcher liveness/adoption, error envelopes, the dashboard status builder, and the online safety controls (confirm-gate + env-var credentials). The watcher itself is IronPython 2.7 inside CODESYS and isn't unit-tested — it's exercised by the live integration suite under tests/integration/ (gated by MCPTOOLKIT_LIVE=1):

MCPTOOLKIT_LIVE=1 pytest tests/integration -v

That suite spawns/adopts a watcher, builds a full project, asserts device.update takes the build 17 → 0 errors, and round-trips the online surface. It's off by default so the normal pytest run stays CODESYS-free.

Configure Claude Desktop

Add to %APPDATA%\Claude\claude_desktop_config.json:

{
  "mcpServers": {
    "codesys": {
      "command": "C:\\path\\to\\mcptoolkit-for-codesys\\.venv\\Scripts\\mcptoolkit-for-codesys.exe",
      "args": ["--sp", "22"],
      "env": {
        "MCPTOOLKIT_WORKDIR": "C:\\Users\\you\\AppData\\Local\\mcptoolkit-for-codesys"
      }
    }
  }
}

Command-line arguments (pass these in the args list):

| Argument | Default | Purpose | |---|---|---| | --workdir | %TEMP%\mcptoolkit-for-codesys | The IPC mailbox folder (commands/ + results/). | | --sp | highest installed | Pin a CODESYS service pack, e.g. --sp 22. | | --headless | off (IDE visible) | Launch CODESYS with --noUI. | | --log-level | INFO | DEBUG / INFO / WARNING / ERROR. |

Args vs. environment variables — which wins. Every flag above also has an MCPTOOLKIT_* env-var equivalent, and you can set it in the config's args list or its env block. They aren't redundant — they're layered:

command-line flag   >   environment variable   >   built-in default

Use an argument for an explicit, per-launch setting; use an environment variable for an ambient one you set once. A few settings are env-only on purpose: CODESYS_EXE / CODESYS_PROFILE (this machine's install paths — set once, not per launch), the MCPTOOLKIT_DEV debug gate, and credential references (username_env / password_env) that keep secrets out of args, which are recorded in the conversation transcript. Full reference: [Environment variables](#environment-variables) and [docs/configuration.md](docs/configuration.md).

The first call from Claude spawns the IDE (visible by default; pass --headless to launch with --noUI).

Running Claude and CODESYS on different machines? You can drive a CODESYS IDE on another PC over SSH — and still watch the IDE on that PC's screen while Claude operates it. See [Remote access over SSH](#remote-access-over-ssh) below.

Example: a first session

You drive everything through Claude in plain language — you never call tools directly. For example:

> You: Create a standard CODESYS project at C:\work\demo.project, add a > function block Motor with a Start method, build it, and tell me whether it > compiles.

Claude calls, in order:

  1. codesys.project.create_standard — copies the shipped Standard template

(Device + Application + MainTask + PLC_PRG) to that path and opens it.

  1. codesys.pou.create — adds the Motor function block.
  2. codesys.pou.create_method — adds Start on Motor.
  3. codesys.build.build — compiles and returns structured errors/warnings.

…then summarizes the result for you. The first call opens CODESYS (~60–90 s); the rest are quick, and you can watch each step happen in the IDE window. More things you can just ask for:

  • "Open C:\plant\line2.project and list any missing libraries."

project.open (which auto-attaches a diagnosis) + library.diagnose.

  • "Log into the PLC, start it, and read PLC_PRG.iCounter."online.login

online.start (Claude must pass confirm: true — it actuates equipment) → online.read.

  • "Export the project as a git-friendly source tree under .\src."

project.mirror_export.

The full list of 82 tools is in [Tools](#tools) below.

Remote access over SSH

You can run Claude on one PC and CODESYS on another on the same network. MCP's stdio transport means the client launches the server as a subprocess — so set that command to ssh, and it runs the server on the remote machine and pipes the JSON-RPC stream back:

{
  "mcpServers": {
    "codesys": {
      "command": "ssh",
      "args": ["@", "C:\\mcp\\codesys-mcp-stdio.cmd"]
    }
  }
}

The server, launcher, and config are otherwise unchanged — SSH just carries the same stdio stream, and adds the authentication + encryption the local file IPC deliberately lacks. Three things to know:

  • Seeing the IDE. A process launched by SSH runs in a non-interactive Windows

session, so a CODESYS it spawns is invisible on the remote screen. To watch the IDE there, start it yourself first with the start-codesys-visible helper; the server then adopts that running instance instead of spawning a hidden one. (Don't need to see it? Add --headless.)

  • Authentication. Use an SSH key — the headless subprocess can't answer a

password prompt. And no -t/-tt in the args: a forced TTY corrupts the JSON-RPC stream.

  • Security. This exposes live PLC control to a remote machine, so keep it on a

private network or VPN, behind key auth.

Editable launcher templates are in [examples/remote-ssh/](examples/remote-ssh/), and the full walkthrough — enabling OpenSSH, key setup, the day-to-day flow, and troubleshooting — is in [REMOTE.md](REMOTE.md).

Environment variables

Set these in the config's env block or your shell. A command-line flag overrides its env-var equivalent (see the precedence note above).

| Variable | Purpose | |---|---| | MCPTOOLKIT_WORKDIR | Where commands/ + results/ live. Default: %TEMP%/mcptoolkit-for-codesys. | | CODESYS_EXE | Override discovery and use this CODESYS.exe. | | CODESYS_PROFILE | Required when CODESYS_EXE is set; path to a .profile.xml. | | MCPTOOLKIT_SP | Prefer this CODESYS SP (e.g. 22). | | MCPTOOLKIT_HEADLESS | 1 to launch with --noUI. | | MCPTOOLKIT_LOG | DEBUG / INFO / WARNING / ERROR. | | MCPTOOLKIT_DEV | Set to 1 to register the _introspect and _eval diagnostic ops in the watcher. These are NOT exposed as MCP tools — they're callable via direct file IPC into /commands/ — but _eval evaluates arbitrary IronPython in the watcher's namespace, so it's off by default. Useful for development/debugging API drift. Alternative: drop a sentinel file at /dev.flag (same effect, no env-var needed). |

Reliability & modal-dialog defense

The watcher runs single-threaded on the CODESYS UI thread, so a modal dialog freezes it. Three layers keep the server drivable unattended:

  1. Prevention — the watcher sets `system.prompt_handling =

ForwardSimplePrompts | LogSimplePrompts | LogMessageKeys` at startup, so simple prompts are auto-answered rather than shown as modals.

  1. Heartbeat + liveness — the watcher writes /watcher.heartbeat

each loop tick (idle) and at each op boundary (busy + op + deadline). The host classifies it healthy / hung (idle-stale >30s, or busy past the op's deadline + grace) / dead (PID gone). A hung watcher is killed and respawned on the next call; a restarted host adopts an already-running watcher instead of spawning a duplicate.

  1. Dialog guard — a host-side background task auto-confirms the watcher's

own safe dialogs (storage-format upgrade, save prompts) by clicking Yes/OK/Continue. It never clicks No/Cancel; an unrecognized dialog is left alone and surfaced.

codesys.diagnose is the hang-proof health check: it reads only host-side state (PID, heartbeat, the IDE's visible windows) and returns instantly even when every other tool

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.