AgentStack
MCP verified MIT Self-run

Codesys MCP

mcp-luke-harriman-codesys-mcp · by luke-harriman

An MCP server for Codesys that enables AI systems to stand up a live UI where they can edit/push code and view variables live on device.

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

Install

$ agentstack add mcp-luke-harriman-codesys-mcp

✓ 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 No
  • 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 Codesys MCP? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

codesys-mcp-persistent

MCP server that exposes the CODESYS V3.5 IDE scripting API as 41 tools and 3 resources, with the CODESYS UI kept open across calls via a file-based IPC watcher.

Unlike headless-only wrappers that spawn a new --noUI CODESYS process per command, this server launches CODESYS with its UI visible and routes every tool call through a watcher script running inside that same instance. Changes appear in the IDE in real time and the user can interact with CODESYS alongside AI-driven automation.

Requirements

  • Windows
  • Node.js 18+
  • CODESYS V3.5 SP19 or SP20 for persistent mode. SP21+ runs only in headless mode — see [Known limitations](#known-limitations).

Install

git clone 
cd Codesys-MCP
npm install
npm run build
npm link

Configure

.mcp.json (Claude Code):

{
  "mcpServers": {
    "codesys": {
      "command": "codesys-mcp-persistent",
      "args": [
        "--codesys-path", "C:\\Program Files\\CODESYS 3.5.20.0\\CODESYS\\Common\\CODESYS.exe",
        "--codesys-profile", "CODESYS V3.5 SP20",
        "--mode", "persistent"
      ]
    }
  }
}

For SP21+, set --mode headless (each tool call spawns a --noUI CODESYS process).

CLI

| Flag | Description | Default | |---|---|---| | -p, --codesys-path | Path to CODESYS.exe | $CODESYS_PATH or C:\Program Files\CODESYS 3.5.21.0\CODESYS\Common\CODESYS.exe | | -f, --codesys-profile | CODESYS profile name | $CODESYS_PROFILE or CODESYS V3.5 SP21 | | -w, --workspace | Workspace for relative paths | cwd | | -m, --mode | persistent or headless | persistent | | --no-auto-launch | Don't launch CODESYS on startup | auto-launch on | | --fallback-headless | Fall back to headless if persistent launch fails | true | | --keep-alive | Keep CODESYS running after server stops | false | | --kill-existing-codesys | Kill any running CODESYS.exe before launching | false | | --timeout | Default command timeout | 60000 | | --detect | List installed CODESYS versions and exit | - | | --verbose / --debug | Logging | - |

Environment variables:

  • CODESYS_PATH, CODESYS_PROFILE — defaults for the corresponding flags
  • CODESYS_MCP_READY_TIMEOUT_MS — watcher-ready deadline (default 180000)

Tools

Management

| Tool | Description | |---|---| | launch_codesys | Manually launch CODESYS (use with --no-auto-launch). Adopts a live session if one exists | | shutdown_codesys | Shut down the persistent CODESYS instance | | get_codesys_status | Report state, PID, execution mode | | eval_python | [DEV] Execute arbitrary IronPython 2.7 against the live scriptengine. Code must print("SCRIPT_SUCCESS") before exit |

Project

| Tool | Description | |---|---| | open_project | Open an existing .project file | | create_project | Create a new project. Defaults to copying CODESYS's installed Standard.project; pass templatePath to copy a specific .project, or templateName to instantiate a template registered with CODESYS's Template Manager | | list_project_templates | Enumerate templates from CODESYS's Template Manager and %ProgramData%/CODESYS | | save_project | Save the currently open project | | compile_project | Build with structured error output (120s timeout) | | get_compile_messages | Retrieve last compiler messages without rebuilding |

POU / code authoring

| Tool | Description | |---|---| | create_pou | Create a Program, Function Block, or Function | | set_pou_code | Set declaration and/or implementation. Accepts Method/Property paths (Application/MyFB/Method1). Auto-saves to disk | | create_property / create_method | Add a Property / Method to a Function Block | | create_dut | Create a Data Unit Type (Structure, Enumeration, Union, Alias) | | create_gvl | Create a Global Variable List, optionally with initial declaration | | create_folder | Create an organisational folder | | delete_object | Delete a user-created object. Refuses system nodes (Application, Device, Library Manager, etc.) and top-level paths | | rename_object | Rename any project object | | get_all_pou_code | Bulk read every declaration + implementation in the project (120s timeout) | | search_code | Regex or literal substring search across every textual POU/Method/Property/DUT/GVL body | | find_references | Word-boundary search for a symbol; wraps search_code | | rename_symbol | Textual rename across all POU bodies. Two-phase plan + apply; dryRun=true by default |

Online / runtime

| Tool | Description | |---|---| | connect_to_device | Login to the PLC runtime. Optionally pass ipAddress (and gatewayName, default Gateway-1); the device address is set then re-resolved from IP form to the gateway-scan node form before login | | disconnect_from_device | Logout. No-op if not connected | | set_credentials | Set default username/password for subsequent logins. Username must be non-empty | | set_simulation_mode | Toggle device-level simulation mode | | get_application_state | Report run / stop / exception plus login status | | read_variable | Read a live variable. Path: GVL.var / PRG.var / GVL.s.aRoi[0].field (no Application. prefix) | | write_variable | Write via set_prepared_value + force_prepared_values. The variable is FORCED until unforced or runtime restart | | download_to_device | Download compiled application. mode: auto (default), online_change, or full. Boot application created after login. 120s timeout | | start_stop_application | Start or stop the PLC application | | monitor_variables | Sample one or more variables at a fixed interval. Duration capped at 60s, intervalMs floor 10ms |

Online tools route every scriptengine.online call through an ExecuteSource frame via the with_executor helper in ensure_online_connection.py. Without it, create_online_application raises InvalidOperationException: Stack empty from IPC-driven scripts on real PLCs.

Library management

| Tool | Description | |---|---| | list_project_libraries | List referenced libraries with version info | | add_library | Add a library reference. Pass the fully-qualified placeholder string from the Library Manager UI (e.g. Standard, * (System)) — bare names like Util won't resolve |

Device tree

| Tool | Description | |---|---| | list_device_repository | Enumerate device descriptors in the local CODESYS Device Repository. Optional vendor, nameContains, maxResults filters | | inspect_device_node | Read-only introspection of a device node: descriptor metadata, parameters with values, child sub-devices | | add_device | Wrap parent.add_device(name, type, id, version). Pair with list_device_repository to source canonical ids | | set_device_parameter | EXPERIMENTAL. Many fieldbus parameters are GUI-only and return a clear error | | map_io_channel | Bind (or clear) a fieldbus I/O channel to a global variable. Channel resolved by name path (Inputs/Byte 0/Bit 3) or numeric indices (0/3) |

Archiving

| Tool | Description | |---|---| | create_project_archive | Save the open project as a .projectarchive. Saves unsaved edits first |

Resources

| URI | Description | |---|---| | codesys://project/status | Scripting status and open-project info | | codesys://project/{path}/structure | Project tree | | codesys://project/{path}/pou/{pou}/code | POU / Method / Property declaration + implementation |

{path} and {pou} use RFC 6570 reserved expansion — pass values with raw : and /, not percent-encoded. On Windows, use forward slashes:

codesys://project/C:/Users/me/Documents/MyPLC.project/structure
codesys://project/C:/Users/me/Documents/MyPLC.project/pou/Application/MyFB/Method1/code

> The two templated resources don't appear in ListMcpResourcesTool (only project-status is static). > > ⚠️ If the requested project path differs from the currently-open project, the resource handler refuses to switch — it raises rather than silently swapping context.

Execution modes

Persistent (default). On launch, the server scans %TEMP%/codesys-mcp-persistent/ for a live session left by a previous MCP server (matching profile, PID alive, ready.signal present) and adopts it. Otherwise it spawns CODESYS.exe --runscript=watcher.py (no --noUI). The watcher writes ready.signal, then runs a background thread that polls a commands/ directory and marshals each script onto the CODESYS UI thread via system.execute_on_primary_thread(). Results land in results/; Node polls with exponential backoff (100ms → 1s). The UI stays interactive between commands and pauses briefly during synchronous API calls (compile, open).

Headless. Each tool call spawns a new --noUI CODESYS process, runs the script, and exits. No UI. Used when --mode headless is set, when --no-auto-launch is used and launch_codesys hasn't been called, or when persistent launch fails and --fallback-headless is on.

Detect installations

codesys-mcp-persistent --detect

Scans Program Files and Program Files (x86) for CODESYS installations.

Known limitations

  • Persistent mode is broken on CODESYS V3.5 SP21+. CODESYS removed se.system.execute_on_primary_thread() from the scripting API somewhere in the SP21 line. Persistent launch completes (ready.signal fires) and then every tool call returns Marshal error: The functionality 'system.execute_on_primary_thread(...)' is no longer supported. --fallback-headless is launch-time only and does not catch this. Workarounds: (a) run with --mode headless; (b) use the forward-port at phobicdotno/Codesys-MCP-SP21-plus. SP19/SP20 are unaffected.
  • set_pou_code auto-saves to disk every successful call. UI Ctrl+Z does NOT recover prior content.
  • set_pou_code empty string is a no-op. Passing declarationCode: "" or implementationCode: "" leaves the section unchanged. To clear a section, pass a single-line placeholder (e.g. // cleared).
  • delete_object refuses system nodes and any top-level object (no /). System-reserved exact paths: Application, Device, Project Settings, Device/Plc Logic, Application/Library Manager, Application/Task Configuration, Application/Task Configuration/MainTask, Device/Communication, Device/Communication/Ethernet, Device/SoftMotion General Axis Pool (and the Device/Plc Logic/... variants).
  • set_credentials rejects empty usernames. For no-auth runtimes, do not call this tool at all.
  • add_library requires a fully-qualified placeholder matching the Library Manager UI string (e.g. Standard, * (System)). Bare names fail with placeholder library X could not be resolved.
  • DUT names containing . are rejected by CODESYS (The name 'X.Y' is not valid for this object).
  • Identifier length isn't enforced at create time. CODESYS only complains at compile/save. Stick to ≤32-char IEC identifiers.
  • find_object_by_path refuses ambiguous matches and returns None. Pass a more specific path.
  • is_simulation_mode getter returns None on some device descriptors. The setter works; verify via compile + login.

Troubleshooting

CODESYS not found — verify with --detect. Executable is typically at C:\Program Files\CODESYS 3.5.XX.X\CODESYS\Common\CODESYS.exe.

Every tool call returns Marshal error: ... is no longer supported — you're on SP21+. Restart with --mode headless.

Watcher timeout — default is 180s. Cold first launches of older SPs can exceed the older 60s budget. Bump further on slow hardware: CODESYS_MCP_READY_TIMEOUT_MS=300000. If CODESYS is still coming up, just call launch_codesys again — the launcher will re-attach to the live PID rather than spawning a second instance.

Command timeout — default 60s; compile_project, get_all_pou_code, and download_to_device use 120s. Increase with --timeout .

Project file locked across MCP restarts — the launcher adopts a live session from the prior server on startup. If you still hit StandardError: The selected project is currently in use, kill the orphan CODESYS.exe via Task Manager, or pass --kill-existing-codesys on next launch (off by default to protect external IDE sessions).

Online tools fail — they require a device/gateway configured (or connect_to_device(ipAddress=...), or set_simulation_mode(enable=true)), a successful compile, and a reachable PLC.

Network error: No route to host from connect_to_device even though ping works — V3 login routes by the gateway-scan node address (e.g. 0301.B0F7), not by the IP-encoded form (0192.0168.0083.0247) that set_gateway_and_address stores from a raw IP. connect_to_device re-scans and re-sets the address before login; if it still fails, the gateway-to-runtime port (TCP 11740 by default) is probably blocked.

Development

npm install
npm run build      # tsc + copy src/scripts/ -> dist/scripts/
npm test
npm run typecheck
pwsh dev/check-scripts.ps1   # ASCII + template-reference static checks

Project layout:

src/
  bin.ts              CLI entry point
  server.ts           MCP tool / resource registration
  launcher.ts         CODESYS process management + session adoption
  ipc.ts              File-based IPC transport
  headless.ts         Headless fallback executor
  script-manager.ts   Python template loading + interpolation
  executor-proxy.ts   Race-free executor swap during background auto-launch
  result-parser.ts    RESULT_JSON marker extraction
  scripts/            IronPython 2.7 watcher + helpers + tool scripts
tests/
  unit/               IPC, script manager, launcher
  integration/        Script pipeline + manual CODESYS tests

License

MIT

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.