te1000-mcp
This server provides an MCP interface for automating Beckhoff TwinCAT 3 XAE engineering via the TE1000 Automation Interface, enabling an AI agent to configure, build, and deploy industrial automation projects.
XAE Shell & Build
Check XAE status, open/save solutions, inspect errors, clean/build/rebuild solutions, run raw DTE commands (guarded), and resolve modal dialogs blocking XAE.
System Manager Tree & IO
Read/write any tree item (identity, children, XML parameters, rename, create, delete, import/export).
Build fully-populated EtherCAT boxes from ESI data; link/unlink variables between PLC and IO (with batch support).
Get/set AMS NetID, rescan PLC, scan IO boxes, and manage bulk variable mappings project-wide.
PLC Project & Code Authoring
Create PLC projects from templates, import existing projects, set boot flags, export/import PLCopen XML.
Author and edit POUs, DUTs, GVLs, interfaces, actions, methods, and properties; manage folders.
Surgical code edits (replace substrings/line ranges, insert, append) in declaration and implementation sections.
Read PLC source with line range or regex slicing, outline structure, and inspect graphical bodies (LD/FBD/SFC/CFC).
Project-wide code search (regex/substring) across all PLC objects.
Run CheckAllObjects offline; deploy PLC boot projects (guarded); control PLC online session (guarded).
Library Management
List, scan, add, remove library references and placeholders; set placeholder resolutions; freeze versions.
Manage machine-wide library repositories: install/uninstall, add/remove/move repos (guarded).
Realtime, Tasks & Routing
Manage RT tasks (list, create, set cycle time/priority/autostart, bind CPU affinity, link to PLC projects).
Configure RT core settings (max CPUs, affinity masks, base time, latency warnings).
ADS route management: list, broadcast-search LAN, add static/project routes (writes guarded).
Fieldbus & Platform
Configure non-EtherCAT fieldbuses: PROFINET, PROFIBUS, CANopen, DeviceNet, EAP — create masters/slaves/boxes, import GSD/GSDML, import DBC files.
NC motion tree: list NC tasks, axes, and axis details.
TcCOM module objects: list, create, configure, enable symbols, assign execution context (guarded).
TwinCAT C++ projects: create, open, generate TMC code, build, publish (publish guarded).
Project variant management: get/set variant config, select active variant, enable/disable tree items per variant.
Engineering settings: silent mode, target platform, solution/PLC archives, independent files.
Measurement & Licensing
Create TwinCAT Scope View/Analytics projects, manage scope elements, record live data (guarded).
Manage TIAN data loggers and stream helpers.
TwinCAT licensing: list license devices, add license nodes, activate OEM response files (guarded).
Runtime Deployment (Confirmation-Gated)
Activate TwinCAT configuration and restart the runtime — both require explicit confirmation tokens.
Safety & Reliability
All destructive/runtime-affecting actions are confirmation-gated and off by default.
The TwinSAFE safety project (
TISC-rooted paths) is never written to by policy.A dialog watchdog detects and optionally auto-dismisses XAE modal dialogs to prevent hangs.
A persistent native C# daemon caches the project tree and PLC source for fast repeated operations.
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., "@te1000-mcp show the current error list"
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.
te1000-mcp
A Model Context Protocol server for Beckhoff TwinCAT 3 engineering automation — drive the TE1000 / XAE Automation Interface from an AI agent or any MCP client.
te1000-mcp exposes the TwinCAT XAE engineering surface — the System Manager tree,
PLC project authoring, IO/EtherCAT configuration, variable linking, builds, and
runtime deployment — as a compact set of MCP tools. It talks to a running XAE Shell
through the TE1000 Automation Interface (COM/DTE), so an agent can configure and build
a TwinCAT project the same way an engineer would in the GUI.
A Node MCP front (index.js) owns the MCP protocol, tool schemas, and confirmation
guards; the COM/DTE work runs in a persistent native C#/.NET daemon
(Te1000Daemon.exe) that the front talks to over a Windows named pipe. The daemon is
the sole backend. See How it works.
This server drives areal engineering tool and can activate or download to a TwinCAT runtime. Every action that touches the target runtime (activate, restart, download, deletes, licensing) is confirmation-gated and off by default. See Safety & guards.
Contents
Related MCP server: TwinCAT Validator MCP Server
Highlights
25 noun-grouped tools covering the automatable TE1000 surface — tree, IO/EtherCAT, linking, PLC project & POU authoring, libraries, tasks, mapping, routes, fieldbuses, TcCOM, C++, measurement/scope, licensing, and variants.
Batch-first — every multi-item operation has a
*_batchform that runs N operations in one DTE attach and returns a compact continue-on-error roll-up, instead of paying a process spawn + attach per call.Native EtherCAT builder —
tc_ethercatcreates fully-populated EtherCAT boxes (correct identity, SyncManagers, FMMUs, PDOs) for any device class by the GUI's own "Add Box" route, driven from the device's ESI.Surgical PLC code edits —
plc_poureads, greps, and patches declaration/implementation text in place and returns only the changed region, keeping agent context small.Safe by default — destructive and runtime-affecting actions are confirmation-gated; the safety project is never written to, by policy.
Resilient to GUI modals — a dialog watchdog detects and (optionally) auto-dismisses modal dialogs that would otherwise hang a synchronous COM call forever.
Persistent native daemon — a long-lived C#/.NET daemon holds the COM session and caches the project tree and POU source text, so warm
plc_pou searchruns roughly 500× faster than the old per-call spawn model.Speaks both MCP protocol eras — the stateless 2026-07-28 revision (envelope-carrying
server/discoverprobe, per-request_metaenvelopes,resultTypecache-hint stamping) and the legacy
initializehandshake (≤ 2025-11-25), on one stdio endpoint. The opening message pins the era per connection (MCP SDK v2serveStdio); a claim-less opening — including a bareserver/discover— is served as legacy traffic per the stdio binding's rules.
How it works
MCP client (agent)
│ stdio (JSON-RPC, MCP)
▼
index.js ───────────────► Te1000Daemon.exe ──COM/DTE──► XAE Shell (TE1000)
(Node 20) named pipe (persistent, x64, running TwinCAT project
daemonClient.js net472, STA COM session)
toolSchemas.jsTwo cooperating processes:
Node MCP front (
index.js) — serves MCP/JSON-RPC over stdio through the SDK v2serveStdioentry (both the stateless 2026-07-28 era and the legacyinitializehandshake), validates input with zod, single-sources every tool's input schema fromtoolSchemas.js, enforces the confirmation-token guards, and maps each toolactiononto a fine-grained bridge action name. It routes those actions to the daemon over a named pipe (daemonClient.js).Native daemon (
daemon/Te1000Daemon.exe) — a persistent net472/x64 process that acquires the DTE +ITcSysManageronce and keeps them, runs the dialog watchdog on an internal thread, caches the System Manager tree and POU source text, and serves the front over the pipe. It implements the same 164 bridge actions and returns the same JSON, so the tool surface is unchanged.
Pipe protocol
The front and daemon exchange newline-delimited JSON over \\.\pipe\te1000-mcp
(name overridable via TE1000_DAEMON_PIPE):
request: {"id": "<n>", "action": "<bridge_action>", "payload": { … }}
response: {"id": "<n>", "ok": true, "result": { … }}
| {"id": "<n>", "ok": false, "error": "…", "errorKind": "com_error|dialog_blocked|timeout", "dialog": { … }}Responses are correlated by id. The daemon serializes every COM call through a single
STA worker thread, so concurrent pipe clients are safe (XAE serializes anyway). The
daemon also answers two COM-free meta actions used for health checks: ping and
list_actions.
Why a daemon — the performance win
An earlier model spawned a fresh 32-bit powershell.exe bridge (plus a second watcher
process) on every call. Each spawn re-acquired the DTE/ITcSysManager COM handles
(a Running-Object-Table walk + Marshal.GetActiveObject), JIT-compiled the inline
Add-Type Win32 helpers, and — for plc_pou.find/search — re-walked the entire
project tree from the root (O(tree-size), ~2,900 COM round-trips on a full project),
so latency grew with project size.
The persistent daemon removes all of that from the hot path:
Persistent COM session (
ComSession.cs) — the DTE + sysmanager are acquired once, health-checked with a cheap property read, and transparently reconnected if stale.Two-layer tree cache (
TreeCache.cs) — per-object decl/impl source text and a flat enumeration of the project's code objects are memoized, so a warm full-project search does zero COM tree-walk. This is the ~500× warm-searchspeed-up.Edit watcher (
EditWatcher.cs) — an on-demandDTE.Documents/.Saveddirty check plus aFileSystemWatcherover the project directory invalidate the cache so it never serves stale source for an object you are editing in the IDE (or that changed on disk).Internal dialog watcher (
DialogWatcher.cs) — runs on its own thread, so there is no per-call watcher process. See Reliability.
Build the daemon with daemon/build.ps1 (in-box .NET Framework MSBuild — no SDK/NuGet).
The daemon requires the 64-bit TcXaeShell (DTE.17.0).
See docs/architecture.md for the end-to-end design and
docs/csharp-daemon-validation.md for the
build/cut-over/validation guide.
Requirements
OS | Windows |
TwinCAT | TwinCAT 3 XAE Shell / XAE installed, with the TE1000 Automation Interface |
Node.js | 20 or newer (the MCP front; the daemon does not remove the Node dependency) |
A running XAE Shell | the server attaches to an already-open instance (it does not launch XAE) |
Daemon (required backend) | the 64-bit TcXaeShell, a .NET Framework 4.x install (for the in-box MSBuild + net472 runtime), and |
The XAE ProgID defaults to TcXaeShell.DTE.17.0. Override it with the TE1000_PROGID
environment variable if your installation differs.
The daemon is not auto-built — build it once with daemon/build.ps1 (see
Build the daemon); thereafter the MCP front auto-spawns the prebuilt
Te1000Daemon.exe on first use. If the exe is absent, build it before the front can serve
calls.
Install
git clone https://github.com/Edge-JB/TwinCAT-XAE-MCP.git
cd TwinCAT-XAE-MCP
npm installVerify the server starts:
node index.js
# -> te1000-mcp server running on stdio (native daemon mode; MCP 2026-07-28 stateless + legacy initialize) (Ctrl-C to exit)The server communicates over stdio and is normally launched by an MCP client, not by hand. Running it directly just waits for a client on stdin.
Build the daemon
The native daemon is the backend, which you build once:
powershell -ExecutionPolicy Bypass -File daemon\build.ps1
# -> daemon\bin\Release\Te1000Daemon.exe (Release, x64, net472)Uses the in-box .NET Framework MSBuild (
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe) — no .NET SDK, no NuGet, no internet. The csproj is old-style (non-SDK) and targetsnet472; the produced x64 exe also runs on the net48 runtime.References
TCatSysManagerLib.dll(embedded interop) for the handful of vtable-onlyIUnknowninterfaces that late-bounddynamiccan't reach (ITcPlcProject, etc.).build.ps1probes the known TwinCAT install paths; if yours differs, edit the<HintPath>indaemon/Te1000Daemon.csprojand rebuild. Pass-Debugfor a Debug build.After building, the MCP front auto-spawns the exe (detached,
windowsHide) on the first call and connects to its pipe. The daemon is single-instance per pipe name (named mutex), so duplicate spawns are harmless, and it is detached so it survives an MCP-front restart. Verify it independently with no XAE attached:node daemon\test-ping.js # spawns the daemon on a test pipe, round-trips ping
The running daemonlocks Te1000Daemon.exe. To rebuild after a code change, stop any
running instance first: Get-Process Te1000Daemon | Stop-Process.
Configure your MCP client
Point your client at the absolute path of index.js in your clone. Example
(Claude Desktop / Claude Code / any MCP client that reads this shape):
{
"mcpServers": {
"te1000": {
"command": "node",
"args": ["C:\\path\\to\\TwinCAT-XAE-MCP\\index.js"]
}
}
}A ready-to-edit copy lives at examples/mcp-config.json.
Environment variables
All optional. The first group is read by the Node front (index.js / daemonClient.js);
the second by the native daemon process. Defaults are from the source.
Read by the Node front:
Variable | Default | Purpose |
|
| XAE Shell COM ProgID to attach to (passed through to the daemon as |
|
| Named-pipe name. The front and the daemon it spawns share this, so a custom value applies to both. |
|
| How long the client waits to connect to (and, if needed, spawn) the daemon before failing the request. |
|
| Node-side per-request ceiling: if no matching response arrives, the request is failed. Set comfortably above the daemon's own ~180 s ceiling so it never pre-empts a legitimately long call. |
| on |
|
| on |
|
|
| How long a blocking dialog must persist before the daemon recycles its COM worker. |
Read by the daemon:
Variable | Default | Purpose |
| unset | When multiple XAE instances are running, prefer the one whose open solution's full path matches this (otherwise the daemon prefers any instance with an open solution). |
| unset |
|
| unset | Explicit path for the daemon diagnostic log (implies logging on, overrides the default location). |
The daemon also accepts CLI flags (
--pipe,--no-watch,--no-autodismiss,--grace-ms,--allowlist). The front sets these from the dialog-watch env vars above when it spawns the daemon — you normally don't pass them by hand.Caveat — spawn-time only.
TE1000_DIALOG_WATCH,TE1000_DIALOG_AUTODISMISS, andTE1000_DIALOG_GRACE_MSare read by the Node front only when it spawns the daemon, and are translated into the daemon's--no-watch/--no-autodismiss/--grace-msflags. The daemon is single-instance per pipe, so changing one of these has no effect on an already-running daemon — kill that daemon process and let the front re-spawn it before the new value takes effect.
Quickstart
With XAE Shell open on your solution, an agent can drive a full configure → build loop. A typical session (tool name + arguments shown):
# 1. Confirm the server is attached and a solution is open
xae action: "status"
# 2. Inspect the IO tree
tc_tree action: "children" path: "TIID^Device 2 (EtherCAT)"
# 3. Add a populated EtherCAT rack from its ESI (digital in/out + analog)
tc_ethercat racks: [{
parent: "TIID^Device 2 (EtherCAT)^R01.Main.N01 (EK1200)",
modules: [{ type: "EL1008" }, { type: "EL2008" }, { type: "EL3064" }]
}]
save: true
# 4. Link a PLC input to a terminal channel
tc_link action: "link"
a: "TIPC^MyPlc^MyPlc Instance^PlcTask Inputs^MAIN.bStart"
b: "TIID^Device 2 (EtherCAT)^Term 1^Channel 1^Input"
# 5. Build the solution
xae_build action: "build"
# 6. (Optional, guarded) download to the target runtime
plc_download confirm: "ALLOW_PLC_DOWNLOAD"More end-to-end recipes — bulk linking, parameter edits via set_xml, POU authoring —
are in examples/.
Tool reference
Paths into the System Manager tree use ^ separators, e.g.
TIID^Device 2 (EtherCAT)^Box 1^Term 5^Channel 1. The leading token is the tree root
(TIPC = PLC, TIID = IO, TINC = NC, TIRC = realtime/license, …).
Engineering & build
Tool | Purpose | Key actions |
| XAE shell & solution control |
|
| Compile the active configuration |
|
| Run a raw DTE command 🔒 | any command name (guarded) |
System Manager tree, IO & linking
Tool | Purpose | Key actions |
| Read/write any tree item (identity, XML params, rename, create, delete) |
|
| Build fully-populated EtherCAT boxes from their ESI |
|
| Link/unlink variables; verify existing links |
|
| Target & rescan helpers |
|
| Bulk variable mapping |
|
| NC motion tree |
|
PLC project & code
Tool | Purpose | Key actions |
| PLC project lifecycle |
|
| Author + surgically edit POUs/DUTs/GVLs (offline) | author ( |
| Library refs / placeholders / repos |
|
| Deploy the active PLC project 🔒 | boot-project (default) or legacy command route |
| Online-session control via UI Automation |
|
Realtime, fieldbus & platform
Tool | Purpose | Key actions |
| RT tasks / cores / linked tasks |
|
| ADS routes |
|
| Engineering settings & archives |
|
| Non-EtherCAT fieldbuses (PROFINET/PROFIBUS/CANopen/DeviceNet/EAP) |
|
| TcCOM module objects |
|
| TwinCAT C++ projects/modules |
|
| Scope + Analytics (TIAN) |
|
| TwinCAT licensing |
|
| Project variant management |
|
Runtime (guarded)
Tool | Purpose |
| Activate the configuration on the target |
| Start/restart the TwinCAT runtime |
🔒 = confirmation-gated. See Safety & guards. Full action signatures, batch semantics, and return shapes are documented in docs/tools.md.
Safety & guards
The server never auto-activates, auto-restarts, or auto-deploys. Any action that
changes the target runtime, deletes a node, or alters licensing is blocked unless you pass
the matching confirm token:
Confirm token | Unlocks |
|
|
|
|
|
|
|
|
|
|
| node/object deletes (or use |
| machine-wide library repository administration |
| ADS route writes |
| TcCOM context changes |
| C++ driver publish |
| live scope acquisition |
| license activation |
Safety project policy. Nothing in this toolchain writes toward the TwinSAFE safety
project. Every authoring tool refuses safety-rooted (TISC) paths via an internal guard.
Safety remains read-only/diagnostic.
Reliability: dialog watchdog & PLC session control
A synchronous DTE/COM call blocks inside XAE's modal message loop if XAE raises a modal dialog (save-changes, "file changed externally", activate confirm, license prompt) — which would hang the MCP call and the calling agent indefinitely.
Dialog watchdog. This runs as an internal thread of the daemon (
DialogWatcher.cs) that polls (~750 ms) for an application-modal dialog owned by the XAE process. It detects application-modal dialogs owned by XAE and either auto-dismisses them (if they match a rule indialog-allowlist.json) or reports the dialog's title, body, and buttons back to the agent and abandons the call. If a non-allowlisted modal persists pastTE1000_DIALOG_GRACE_MS, the daemon recycles its COM worker thread (re-acquiring the session on a fresh STA thread) without killing the daemon, so subsequent calls recover once the dialog is cleared. Detection is dialog-driven, not a wall-clock timeout, so long legitimate builds are never killed. The allowlist ships empty (report-only by default) and must never auto-answer Activate / Run-mode / restart / download / safety prompts.Interactive resolution. When a dialog is not in the allowlist, the reported error tells the agent to ask the user which button to press (and whether to remember it), then call
xae dialog_resolve {button, remember?}. That action clicks the chosen button on the live dialog; withremember:trueit appends an auto-dismiss rule todialog-allowlist.jsonand hot-applies it to the running watcher (no restart). Destructive prompts (activate / run-mode / restart / download / boot project / TwinSAFE / safety) are refused for auto-remember — the one-time chosen click still happens, but no rule is persisted (rememberRefusedis reported). Usexae dialog_probe(read-only) to inspect the current dialog first.PLC session control (
powershell/plc-session.ps1) uses UI Automation to read and toggle the Login/Logout state (the DTE Login/Logout commands are unreachable on the 64-bit shell).plc_downloadauto-logs-out first (by default) so deferred source edits compile before the boot project is generated. It never logs back in.
Full details: docs/operations.md.
Troubleshooting
Te1000Daemon.exe not found— build it:daemon\build.ps1. The front cannot serve calls until the daemon is built.Build fails on
TCatSysManagerLib— the DLL wasn't found at the probed TwinCAT paths. Edit the<HintPath>indaemon/Te1000Daemon.csprojto your install and rebuild.Rebuild fails with the exe locked — a daemon is still running. Stop it first:
Get-Process Te1000Daemon | Stop-Process, then rebuild.Daemon won't start / stale behavior — kill it (above) and let the front re-spawn a fresh one on the next call. Enable
TE1000_DAEMON_DEBUG=1to capture%TEMP%\te1000-daemon-<pipe>.log.Wrong XAE instance picked (several open) — set
TE1000_MCP_SOLUTION_PATHto the solution's full path to pin the daemon to that instance.A modal dialog is blocking calls — clear it on the machine, or add a rule to
dialog-allowlist.json(never for Activate / restart / download / safety prompts). The daemon picks up the allowlist on start.
Examples
The examples/ directory contains:
mcp-config.json— a drop-in client configuration.README.md— copy-pasteable recipes: building an EtherCAT rack, bulk-linking IO, editing terminal parameters viaset_xml, authoring a POU, and a safe build → activate → download flow.
Documentation
Document | What's in it |
The Node-front + persistent C#/.NET daemon design end to end — pipe protocol, COM session, caching, edit-watching | |
Complete tool & action reference — signatures, batch semantics, return shapes | |
Dialog watchdog, PLC session control, and the safety/guard model in depth | |
Survey of the full TE1000 Automation Interface surface (the menu these tools are carved from) | |
The 164-action port coverage checklist (bridge action → C# handler) | |
Build, cut-over, and live-XAE smoke-test guide for the daemon | |
Running engineering notes / backlog discovered on real projects | |
Version history |
Contributing
Contributions are welcome — see CONTRIBUTING.md for the architecture, the daemon build/dev loop, the action-handler contract, and the safety rules every change must respect. In short:
npm run check # node --check index.js — syntax-validate the front
node daemon\test-ping.js # daemon process + pipe + JSON round-trip (no XAE)
powershell -ExecutionPolicy Bypass -File daemon\build.ps1 # rebuild the daemon after a C# changeLicense
MIT © Edge Automation.
Legal & trademarks
This is an independent, third-party project. It is not affiliated with, endorsed by, sponsored by, or supported by Beckhoff Automation GmbH & Co. KG.
All product names, logos, and brands are the property of their respective owners:
Beckhoff®, TwinCAT®, TE1000, and XAE Shell are trademarks or registered trademarks of Beckhoff Automation GmbH & Co. KG.
EtherCAT® is a registered trademark and patented technology, licensed by Beckhoff Automation GmbH, Germany.
These names are used for identification and descriptive purposes only; their use does not imply any affiliation with or endorsement by the trademark holders.
This project does not include, bundle, or redistribute any Beckhoff software. It automates a separately installed and licensed TwinCAT 3 / TE1000 environment that you must obtain from Beckhoff yourself. You are responsible for complying with all applicable Beckhoff license terms and for any action this tool performs against your engineering or runtime systems.
The software is provided "AS IS", without warranty of any kind, under the MIT License. See NOTICE for the full attributions.
Available Tools
25 toolsncC
NC motion tree: tasks (list under TINC), axes (path = task, default first task), axis (path = full axis path, returns info + children).
Inputs: action (required), path.
plc_downloadB
Deploy the active PLC project. Guarded: confirm="ALLOW_PLC_DOWNLOAD" (deploys a boot project to the live target). method "bootproject" (default): headless via ITcPlcProject — writes the boot project to the target boot dir; twincat_restart_runtime loads and runs it. method "command": legacy DTE command route (needs a shell with window automation). autoLogout (default true): if the IDE is logged into the PLC, log out first via UI Automation so any source edits deferred by the online lock are applied before deploy. Never logs back in.
Inputs: method, confirm, treePath, autostart, +2 more.
plc_libraryA
PLC library references / placeholders / repositories via ITcPlcLibraryManager on the References node (TIPC^^ Project^References). referencesPath defaults to the first PLC under TIPC. READ (no side effects): list (References → name/kind library|placeholder/displayName/distributor/version), scan (ScanLibraries → installed libs name/version/distributor/displayName; pass filter to avoid the full catalogue dump), repos (Repositories → name/folder). WRITE — OFFLINE .plcproj edits, NO runtime impact (not confirm-gated): add_library (name, version?, company?), add_placeholder (name, defLib?/defVer?/defDist? — omit defLib for the name-only form), set_resolution (placeholder, lib, version?, dist?), freeze (name? — omit to freeze ALL), remove_reference (name = library or placeholder). Each accepts save:true to File.SaveAll after the edit. LANDMINE: a .plcproj library-reference edit (add/remove/repin a library or placeholder, set resolution) requires a full solution close+reopen in XAE before it takes effect; adding source files alone does not — the response surfaces this note. REPO ADMIN — GUARDED, mutates the machine-wide TwinCAT library store (no runtime change, but shared-machine state): install_library (repo, libPath, overwrite?), uninstall_library (repo, lib, version?, dist?), insert_repository (name, folder, index?), remove_repository (name), move_repository (name, index). These require confirm="ALLOW_PLC_LIBRARY_REPO". Nothing here targets the safety system (References live only under TIPC).
Inputs: action (required), lib, dist, mode, +16 more.
plc_pouA
PLC object authoring + code edit on the open solution (OFFLINE engineering only — edits land in-memory and reach a runtime only via a later guarded plc_download + twincat_restart_runtime). Tree paths use ^ separators; safety (TISC-rooted) paths are rejected by policy. CREATE — create / create_batch (parent, name, subType, language?, returnType?, extends?, implements?, declText?, before?): CreateChild sub-types 602 Program, 603 Function (returnType required), 604 FunctionBlock, 605 Enum, 606 Struct, 607 Union, 608 Action, 609 Method, 611 Property (returnType required), 615 GVL, 616 Transition, 618 Interface, 619 Visualization, 623 Alias, 629 ParameterList, 631 UML. language IECLANGUAGETYPES 0 NONE/1 ST/2 IL/3 SFC/4 FBD/5 CFC/6 LD (default 1). extends/implements for FB 604 / Program 602 derivation (618 uses extends as its base); declText seeds DUT/GVL decl. For code POUs prefer set_decl after create. FOLDERS — create_folder (parent, name, before?) creates a PLC folder (sub-type 601) under parent (a PLC subtree node, POUs/DUTs/GVLs container, or another folder); returns the same shape as create. create_folder_batch (creates:[{parent,name,before?}], save?) loops continue-on-error, returns {count,succeeded,failed,results} KEEPING success rows (each carries the created child identity) — list a parent-folder entry before its child (array order). create / create_batch already author INTO a folder when parent is the folder's path — no separate action needed. TEMPLATE — import_template (parent, paths[]) imports POU template file(s) (CreateChild sub-type 58). READ (cheap-first: outline for structure; get_decl/get_impl with grep{} or range{} to slice; full text only when the whole body is needed — a large full read returns a hint nudging the next call to slice) — get_decl / get_impl / get_document / get_graphical (path). get_decl/get_impl take an optional range {start,end} (1-based inclusive line slice, clamped) OR grep {pattern, context?} (regex over lines + context each side); mutually exclusive; default full text. Both report lineCount; get_impl also returns language (textual 1 ST/2 IL; graphical 3 SFC/4 FBD/5 CFC/6 LD -> lineCount:0 + {graphical:true, hint}). get_graphical (path) READ-ONLY inspects a graphical (LD/FBD/SFC/CFC) body: returns {language,languageName,itemType,source,readOnly,xml} where xml is the object's network XML (NWL 'BoxTree' for LD/FBD/IL, or the SFC/CFC archive), pulled live from the POU document (for an Action/Method/Transition it reads the PARENT POU's document, since get_document/GetDocumentXml only work on a top-level POU). Diagnostic only — graphical bodies are NOT text-editable; change them in the XAE GUI. Refuses textual languages (use get_impl). outline (path) returns structure WITHOUT full text: header + varBlocks + child code items. WRITE — set_decl / set_decl_batch (path, declText); set_impl / set_impl_batch (path, exactly one of implText|implXml — implXml is TwinCAT object XML, round-trip only, for graphical languages); set_document (path, documentXml). SURGICAL TEXT EDIT (read-modify-write, returns ONLY the changed region +/-2 ctx; target decl|impl, CRLF/LF preserved; refuses graphical impl): replace (find literal substring, replaceWith, expectCount? default 1 — fails without writing on 0 or count mismatch); replace_lines (start, end, text — 1-based inclusive span, OOB throws); insert (exactly one of at|after|before, text); insert_in_var_block (block e.g. VAR_INPUT, text, occurrence? — inserts before that block's END_VAR); append (text — default target impl). All surgical writes accept validate:true to run CheckAllObjects after (default off). DISCOVER (cheap-first: find for path-by-name; search only for content patterns) — tree (plcPath?, path? subtree root, depth?, typeFilter?) does a read-only recursive Child() walk of the IEC project and returns {plcPath,projectPath,rootPath,count,tree:[{path,name,type,itemType,subType?,childCount,children?,truncated?}]} (type is a normalized label: Program/FB/Function/FunctionBlock/Struct/Enum/Union/Alias/GVL/Interface/Method/Property/Action/Transition/Visualization/ParameterList/UML/Folder/Project/Task/Unknown; depth 1 = direct children only; typeFilter is a comma list of type labels to KEEP, ancestors retained as scaffolding). find (plcPath?, path?, name? substring or /regex/, typeFilter?; at least one of name/typeFilter) returns a FLAT {plcPath,projectPath,count,matches:[{path,name,type,itemType,subType?,childCount}]} so a caller can resolve a ^ path from a name without the whole nested blob. GREP — search (pattern [regex/.NET or substring], ignoreCase?, declOnly?|implOnly? [mutually exclusive], plcPath?, path? subtree root, maxResults? default 50/max 5000 — raise for exhaustive scans; truncated:true signals the cap was hit) is a project-wide find-in-code: walks every code object under the IEC project, greps DeclarationText + (ST-only) ImplementationText line-by-line, returns {pattern,plcPath,scanned,searched,count,truncated,matches:[{path,section:'decl'|'impl',line,text}]}; graphical bodies are scanned-but-not-searched. Read-only/offline. Decl/impl text is CACHED (warm repeat sub-100ms vs ~16s cold); the cache self-invalidates on edits through this tool, dirty-checks open IDE editors, and is backstopped by a file-save watcher — pass refresh:true to force a full live re-pull. DELETE — delete (path OR parent+name) GUARDED offline delete of one PLC object via parent.DeleteChild; dryRun:true previews {wouldDelete,target}, confirm="ALLOW_TWINCAT_DELETE" to actually delete; verifies the child exists first, refuses TISC. LIFECYCLE (OFFLINE, unguarded, refuses TISC) — rename (path, newName = bare name) renames in place, returns {path,newName,newPath}. move (path, newParent, before?) reparents one object preserving decl/impl/document/sub-objects via export-import-delete in ONE attach (no native reparent exists); refuses no-op/into-self/into-own-descendant moves; returns {path,newParent,newPath,name,via}. BUILD-CHECK — check_objects (plcPath?, default first PLC under TIPC) runs CheckAllObjects on the nested IEC project (no download). Mutating batch verbs (create_batch, set_decl_batch, set_impl_batch) accept save:true to save once after the batch.
Inputs: action (required), at, end, find, +42 more.
plc_projectA
PLC (IEC) project lifecycle on the open solution. Tree paths use ^ separators; the PLC ROOT node is TIPC^, the nested project INSTANCE node is TIPC^^ Project. NODE MATTERS: ITcPlcProject (boot flags / generate_boot) is on the ROOT; ITcPlcIECProject* (plcopen_export/import / save_as_library) is on the INSTANCE node. Actions: create_from_template (name, template, before?, save?) — new PLC project from a stock template; open (name, file=.plcproj/.tpzip, subType 0 copy/1 move/2 use-in-place, before?, save?) — import an existing project; info (treePath? default first under TIPC) — read identity (nestedProjectName/instanceName/childCount); set_boot_flags (treePath? = ROOT, autostart?, tmcFileCopy?) — config-only boot flags; plcopen_export (file, treePath? = INSTANCE, selection?) — write PLCopen XML; plcopen_import (file, treePath? = INSTANCE, options 0 NONE/1 RENAME/2 REPLACE/3 SKIP, selection?, folderStructure? default true, save?) — import PLCopen XML; save_as_library (file, treePath? = INSTANCE, install? default false — install:true mutates the local library repository) — save project as .library. GUARDED (live runtime/target writes), require confirm="ALLOW_PLC_DOWNLOAD" and default to no-op: generate_boot_project (treePath? = ROOT, autostart? default true) — generates the boot project to the target boot dir (restart runtime to load); online (command login/logout/start/stop/reset_cold/reset_origin, treePath? — changes live online/runtime state; the ConsumeXml envelope is UNVERIFIED on this build and surfaces GetLastXmlError verbatim, reset_* need a prior login, build>=4010). Safety projects are deliberately out of scope.
Inputs: action (required), file, name, save, +12 more.
plc_sessionA
PLC online-session control via UI Automation (the DTE Login/Logout commands are unavailable on the 64-bit shell). action "status" (read-only): reports { loggedIn }. action "logout": logs the IDE out of the PLC — this also applies any source edits the online lock deferred ("loaded after logout"). Never logs back in. Guarded: logout needs confirm="ALLOW_PLC_LOGOUT".
Inputs: action (required), confirm.
tc_cppA
TwinCAT C++ projects/modules under TIXC (paths use ^ separators). C++ ONLY — TISC (safety) untouched; runtime needs a later activate/download. VS-hosted-safe: create/open go purely through ITcSmTreeItem.CreateChild on TIXC (no New/Open/SaveConfiguration). Actions: create_project (name, template, before?) — CreateChild a new C++ project node under TIXC from a wizard; template = "TwinCAT C++ Project Wizard" | "TcVersionedDriverWizard" | "TcModuleCyclicCallerWizard" (or a full template .vcxproj/.tczip path — if a wizard NAME is rejected, fall back to the file path). create_module (projectPath = TIXC^, name, template? default "TwinCAT Class Wizard", before?) — CreateChild a module/class on an existing C++ project. open (file = existing .vcxproj/.tczip, subType? 0 copy into solution dir (default) /1 move/2 use-in-place, before?) — import an existing C++ project; the project is NOT renamed (CreateChild name is empty). tmc_codegen (projectPath) — offline StartTmcCodeGenerator (regenerates C++ from the .tmc; no runtime impact). set_props (projectPath, bootProjectEncryption? None|Target, saveProjectSources?) — offline config edit via ConsumeXml (at least one prop required). build (projectName = the .vcxproj DTE project Name/UniqueName, config? default "Release|TwinCAT RT (x64)", waitForFinish? default true, timeoutMs? default 1800000) — compile a single C++ project via SolutionBuild2.BuildProject; compiles only, does NOT deploy. publish (projectPath, confirm) — GUARDED, requires confirm="ALLOW_CPP_PUBLISH" and defaults to no-op: builds the module for ALL platforms and exports the deployable/shippable driver artifacts (long-running); does NOT itself activate/restart the runtime. CAVEAT: the ConsumeXml wrapper element for C++ project params is from a doc summary (Set-TreeItemXml surfaces GetLastXmlError, so a wrong element fails loudly); ProduceXml the project node once to confirm element names before relying on tmc_codegen/set_props/publish.
Inputs: action (required), file, name, before, +10 more.
tc_ethercatA
Create EtherCAT IO boxes (terminals/couplers) NATIVELY. Each module is added via the GUI's own "Add Box" route — ITcSmTreeItem.CreateChild(name, 9099, before, "") — so TwinCAT expands the box FROM ITS OWN ESI: a fully populated, non-hollow box (correct identity, SyncManagers, FMMUs, full mailbox/CoE/FoE element, complete PDOs+entries) for ANY class — digital, analog (in AND out), IO-Link, mailbox, DC, couplers. createInfo is the PLAIN PRODUCT STRING (the bare type = latest revision, or a revision-pinned form), NOT identity XML/numbers. ONE unified shape — a single box and a whole multi-coupler design are the SAME operation: racks:[{ parent:"<EtherCAT coupler/master tree path>", modules:[{ type:"EL1008", name?:"Term 7 (EL1008)", revision?, before?:"" }] }]. A single box is just racks:[{parent, modules:[{type}]}]. Modules are created in array order (left-to-right terminal order); before inserts ahead of a named sibling; name omitted defaults to type. Revision pinning: pass revision as the full Beckhoff product string suffix "-" (decimal), e.g. type:"EL1008" revision:"0000-0017" → RevisionNo #x00110000; you may also pass the whole pinned string in revision (e.g. "EL1008-0000-0017"). Bare type = latest revision. NO fallback — if CreateChild produces a ghost/unknown type, that ONE module is a clean ok:false (any stray child is cleaned up) and the rest continue. Optional save:true saves the solution once after everything. Returns a flat roll-up {count, succeeded, failed, results:[{parent, type, name, ok, error?}]}.
Inputs: racks (required), save.
tc_fieldbusA
Create + configure NON-EtherCAT fieldbus masters/slaves/boxes (PROFINET / PROFIBUS / CANopen / DeviceNet / EAP net-vars) via ITcSmTreeItem CreateChild + ClaimResources + ConsumeXml. OFFLINE CONFIG ONLY — no confirm token needed; runtime needs a later activate/restart; TISC (safety) paths refused. For EtherCAT terminals/boxes use tc_ethercat instead. BATCH-FIRST: for more than one device use create_batch (N ops in ONE DTE attach, continue-on-error roll-up {count,succeeded,failed,results:[{parent,name,ok,child?,claimed?,error?}]}). SubType cheat-sheet — PROFINET ctrl 113/119/126/140, dev 115/118/142/143; PROFIBUS master 86 slave 97; CANopen master 87 slave 98; DeviceNet master 41/73/88 slave 62/74/99 monitor 59 box 5203; EAP device 112 publisher 9051 subscriber 9052. Actions: create_device (parent? default TIID / EAP device path, name, subType, before?, vInfo?, claimIndex?, save?) — CreateChild a master/slave/box; claimIndex immediately ClaimResources to bind underlying hardware; a wrong subType/vInfo ghost is cleaned up and reported as failure; create_batch (creates:[{parent?,name,subType,before?,vInfo?,claimIndex?}], save?); list_resources (path) — read-only; probes ITcSmTreeItem5.ResourcesCount then ResourceCount (Beckhoff pages disagree on the name) and reports which answered; claim_resources (path, index [1-based per Beckhoff examples], save?) — bind the node to underlying FC/EL hardware (offline config edit, NOT a runtime write); create_gsd_box (controllerPath, name, gsdPath, moduleIdentNumber, subType [REQUIRED — PN device subType, depends on controller variant], boxFlags? [GENERATE_NAME_FROM_PAB 0x0004 / GET_STATIONNAME 0x0400 / SET_NOT_IP_TO_OS 0x4000], dapNumber?, before?, save?) — PROFINET GSD/GSDML box; vInfo = gsdPath#moduleIdentNumber#boxFlags#dapNumber. CAVEAT: GSD box subType + vInfo format from a doc summary, confirm against infosys 1041677067 before relying on it; add_netvar (boxPath = EAP publisher/subscriber box, name, dataType [IEC type as vInfo, e.g. BOOL/INT], before?, save?) — EAP pub/sub variable (SubType 0; resulting ItemType 35 publisher / 36 subscriber); set_station_address (path = PROFIBUS slave/box, address, save?) — discovers the address element via ProduceXml then ConsumeXml a minimal envelope (the bare-number form is unverified and NOT shipped); if discovery fails, use get_xml + set_xml; import_dbc (masterPath = CANopen master, fileName [.dbc], importExtendedMessages?, importMultiplexedDataMessages?, keepUnchangedMessages?, communicateWithSlavesFromDbcFile?, save?) — CanOpenMaster/ImportDbcFile config import (requires TC3.1 build >= 4018); get_xml (path) — raw ProduceXml passthrough for discovering real param elements; set_xml (path, xml = partial XML, returnXml?, save?) — generic ConsumeXml escape hatch for any fieldbus param not covered above.
Inputs: action (required), xml, mode, name, +24 more.
tc_licenseA
TwinCAT licensing on the TIRC^License node (requires TC3.1 >= 4022.4; older targets have no AvailableLicenseDevices/ActivateResponseFile support and ProduceXml/ConsumeXml return empty or error — the HRESULT is surfaced, not masked). Nothing here touches the safety system (TIRC^License is real-time/licensing config). Actions: list (read-only) — discover available dongle license devices via ProduceXml; returns {treePath, devices:[{name,pathName,typeName,objectId}]} (pass raw:true to also include the full License-node ProduceXml blob). add (name, device) — OFFLINE config edit: CreateChild a license-device child under License bound to a dongle that MUST already exist in the I/O tree (device = its display-name e.g. "Term 2 (EL6070)" OR its ObjectID e.g. "50462722" from list). This only links the License node to existing hardware; it does NOT create the dongle terminal — add the EL6070 (etc.) first via tc_ethercat/tc_tree. Not confirm-gated (config-only). activate_response (confirm, path, oemGuid?) — GUARDED, requires confirm="ALLOW_LICENSE_ACTIVATE" and defaults to no-op: ConsumeXml the ActivateResponseFile command to activate an OEM license response file (path = absolute path to the .tmc/.reresponse file). oemGuid is "only required in special cases" and accepts any value; defaults to 0 when omitted. This is a license-activation state change.
Inputs: action (required), raw, name, path, +3 more.
tc_linkA
Variable links (producer↔consumer); dot-form PLC subfields auto-resolve to XAE ^ subitem form. BATCH-FIRST: for more than one link use link_batch/unlink_batch — N ops in ONE DTE attach, failures-only roll-up by default ({count,succeeded,failed}; details:true for every row incl. resolved paths), instead of an attach per link. Actions, grouped single / batch: LINK — link (a=source, b=destination) / link_batch (links:[{a,b}]); UNLINK — unlink (a, optional b; a alone removes all its links) / unlink_batch (links:[{a,b?}]); resolve (report valid path forms for a — compact {originalPath,resolvedPath,resolved} on success; verbose:true for the attempts[] permutation list); links (a=item path; reports that item's current variable links — closes the discover→act→verify loop). link/resolve drop resolution detail by default — verbose:true restores it. Mutating batch verbs accept save:true to save once after the batch.
Inputs: a (required), action (required), b, save, +4 more.
tc_mappingA
Bulk variable-mapping (ALL links) ops on the loaded TwinCAT project via ITcSysManager2/3, whole-project (no tree path): produce (read-only) — ProduceMappingInfo serializes every current variable link/mapping to ONE XML blob (the IDE's "Export Mapping Information"); the blob is returned as raw XML. consume (xml) — ConsumeMappingInfo re-applies/merges a previously produced blob, ADDING links; MUTATES the offline config only (no runtime impact until a later twincat_activate_configuration); optional save:true saves the solution after. clear — ClearMappingInfo deletes ALL variable links project-wide; destructive, GUARDED: requires confirm="ALLOW_TWINCAT_DELETE" (reuses the existing delete token); optional save:true. These are PROJECT-WIDE config-tree ops, NOT runtime writes. SAFETY: the mapping blob spans the whole project and CAN include TwinSAFE I/O image links — produce/consume/clear may touch safety-related links; by policy nothing should write toward safety, so run produce FIRST as a backup and treat the blob as opaque (export -> store -> consume round-trip). The exact XML schema is undocumented; test-import hand-edited blobs in the IDE before relying on them.
Inputs: action (required), xml, save, confirm.
tc_measurementA
Measurement (TE130X Scope View) projects + TwinCAT Analytics (TIAN) logger/stream config. Scope/Analytics PROJECTS are separate EnvDTE.Project nodes (AddFromTemplate), NOT System Manager tree nodes; TIAN logger/stream nodes ARE System Manager children (CreateChild/DeleteChild under TIAN). Requires the respective products installed (scope + analytics templates) — if absent the action fails with a clear 'tooling not installed' message rather than a raw COM HRESULT. Actions: scope_create (name, template? = full .tcmproj path [default: first installed under TE130X-Scope-View\Templates\Projects], destination? = folder [default: solution dir]) — AddFromTemplate a new Scope project; scope_add_child (project, parentPath? = ^-path of names from scope root, name?, elementType? default 0) — CreateChild(out item,name,elementType); only elementType 0 is VERIFIED, non-zero values are EXPERIMENTAL; deep parentPath resolves by enumerating existing children by name; scope_rename (project, path, newName) — ChangeName on the element at path; scope_record (project, state 'start'|'stop') — StartRecord/StopRecord; GUARDED: state='start' performs LIVE data acquisition and requires confirm="ALLOW_MEASUREMENT_RECORD" (state='stop' needs no confirm); analytics_create (name, template? = full Analytics project template path [must resolve or pass explicitly], destination? = folder) — AddFromTemplate a new Analytics project (project creation ONLY; network/function wiring is UNVERIFIED and not implemented); logger_create (name, before?) — CreateChild a DataLogger (subType 1) under TIAN (config edit, no confirm); logger_delete (name, dryRun?, confirm) — DeleteChild under TIAN, GUARDED confirm="ALLOW_TWINCAT_DELETE" (dryRun:true previews existence without deleting); stream_create (name, before?) — CreateChild a StreamHelper (subType 0) under TIAN (config edit, no confirm); stream_delete (name, dryRun?, confirm) — DeleteChild under TIAN, GUARDED confirm="ALLOW_TWINCAT_DELETE"; the actual node name is '_Obj1 (StreamHelper)' (the suffix is appended for you). For raw ProduceXml/ConsumeXml on a TIAN logger/stream node (e.g. 'TIAN^') use tc_tree get_xml/set_xml. OMITTED as UNVERIFIED: Scope data-export (SaveSVD/ExportCSV/ExportTDMS/ExportBinary/ExportDAT), Scope-Server (ShowControl/CloseControl/Disconnect), LookUpChild, and all Scope/Analytics enums. Nothing here targets the safety system.
Inputs: action (required), xml, mode, name, +13 more.
tc_moduleA
TcCOM module objects under TIRC^TcCOM Objects (paths use ^ separators). CONFIG-TIME ONLY — no INIT/PREOP/SAFEOP/OP transitions (not an Automation Interface feature); nothing here activates config, downloads, or touches the runtime/safety system. Actions: list (read-only) — enumerate module instances via ITcModuleManager3, returns {count,modules:[{moduleTypeName,moduleInstanceName,classId,oid,objectId,parentOid}]} (oids are DECIMAL; XAE shows hex). create (name, by="classid"|"name", id, before?) — CreateChild under TcCOM Objects: by=classid -> subType 0, id = module GUID/ClassID e.g. {8f5fdcff-...}; by=name -> subType 1, id = registered module type name e.g. "NewModule"; a malformed/ghost child is cleaned up and reported as an error. get_xml (path) — ProduceXml of the instance (Parameters / DataAreas / Symbols, with current CreateSymbol/CreateSymbols flags). set_xml (path, xml, returnXml?) — ConsumeXml escape hatch for parameters not exposed as typed properties. enable_symbols (path, parameters?, dataAreas?, returnXml?) — convenience toggle: sets CreateSymbol=true on Parameter nodes and/or CreateSymbols=true on DataArea AreaNo nodes via ProduceXml/ConsumeXml. CAVEAT: the XPath/attribute names are from a how-to summary, NOT verified against a literal ProduceXml dump — call get_xml on a real module first and fall back to set_xml if the toggle reports changed:false. To wire module DataArea symbols to PLC/IO/other-module variables (symbols must already exist via enable_symbols), use tc_link link/unlink. set_context (path, taskObjectId, contextId?) — assign the instance to a task's execution context; taskObjectId/contextId are DECIMAL oids (XAE shows hex). GUARDED: changes the activated mapping/runtime context, requires confirm="ALLOW_TWINCAT_MODULE_CONTEXT" and defaults to no-op.
Inputs: action (required), by, id, xml, +9 more.
tc_routeA
ADS routes via the System Manager TIRR (Routes) node, ConsumeXml/ProduceXml. READ (unguarded — a transient search trigger, never persists a route): list — existing static routes under RemoteConnections (best-effort name/netId/address); broadcast_search — LAN-wide UDP discovery (timeoutMs settle wait, default ~4000ms) → targets [{name,netId,ipAddr}]; search_host — direct by host (hostname or IP; needs TwinCAT 3.1 build>=4020.10, older builds return found:false) → {found, target:{name,netId,ipAddr,version,os}}. WRITE (GUARDED, require confirm="ALLOW_TWINCAT_ROUTE_WRITE", default NO-OP): add_route — credentialed route to a remote target (remoteName, remoteNetId, one of remoteIpAddr|remoteHostName; optional userName/password/noEncryption/localName); add_project_route — lighter project-local entry (name, netId, one of ipAddr|hostName). NOTE: route changes via TIRR ConsumeXml take effect in the engineering project; whether they propagate to the live target depends on the current target connection — this does NOT auto-activate. Nothing here targets the safety system (config/engineering-side only).
Inputs: action (required), host, name, netId, +12 more.
tc_settingsA
XAE engineering settings & packaging. OFFLINE/engineering-only: NONE of these write toward a runtime or change runtime state (their runtime effect, if any, lands only on a SEPARATE later activate/download), so none are confirm-gated. Tree paths use ^ separators; safety (TISC-rooted) paths are rejected by policy in set_disabled/set_independent_file/save_plc_archive. Actions: get_silent_mode / set_silent_mode (enabled) — TcAutomationSettings.SilentMode; suppresses AI message-box dialogs (TC3.1>=4020.0; older builds throw). A good companion to the dialog watchdog. get_target_platform / set_target_platform (platform = "TwinCAT RT (x86)" | "TwinCAT RT (x64)") — ITcSysManager7.ConfigurationManager.ActiveTargetPlatform; switching platform invalidates prior build output, so rebuild (xae_build) before activate/download. save_solution_archive (file = absolute .tszip) — ITcSysManager9.SaveAsArchive, whole solution; parent dir must exist (not created). save_plc_archive (file = absolute .tpzip, name? = PLC child under TIPC, default first child) — ExportChild of the PLC project. get_independent_file / set_independent_file (path, enabled) — ITcSmTreeItem6.SaveInOwnFile (store node settings in its own file vs inline in .tsproj). get_disabled (path) — reads ITcSmTreeItem.Disabled, returns {disabled:0|1|2, state:SMDS_NOT_DISABLED|SMDS_DISABLED|SMDS_PARENT_DISABLED}; SMDS_PARENT_DISABLED(2) is a derived read-only state. set_disabled (path, disabled) — sets 0/1 only (2 is never settable).
Inputs: action (required), file, name, path, +3 more.
tc_systemD
System Manager: get_netid, set_netid (netId), errors (latest messages), rescan_plc (path, default TIPC), scan_io_boxes (path = IO device node).
Inputs: action (required), path, netId.
tc_taskA
RT tasks under TIRT (+ RT-core settings under TIRS, + a PLC project's LinkedTask under TIPC). CONFIG-ONLY — no confirm token needed; runtime needs a later activate/restart; TISC (safety) untouched. Tree paths use ^ separators (e.g. TIRT^PlcTask). Actions: list (tasks under TIRT); get (path; summary:true -> identity + parsed TaskDef tags instead of full XML); create (name; withImage default true = SubType 0 / false = SubType 1 no image; before?; cycleTimeUs?/priority? applied after create via ConsumeXml; save?); set_params (path; cycleTimeUs [us, converted to 100ns ticks = us*10] / priority [0-255] / autoStart; OR xml = raw .. escape hatch [mutually exclusive with the typed fields]; returnXml?; save?). CAVEAT: the TaskDef tag names for cycle/priority/autostart are UNCONFIRMED against the AI docs — prefer the xml escape hatch and verify with get(summary) before trusting the typed fields; add_image_var (path = a with-image task's Inputs/Outputs node; varName; dataType e.g. BOOL/INT/DINT; startAddress? default -1 = append; save?); get_rt_settings (TIRS; summary:true -> parsed MaxCPUs/Affinity/per-CPU LoadLimit/BaseTime/LatencyWarning); set_rt_settings (maxCPUs / affinity [TwinCAT hex token e.g. #x0000000000000007] / cpus [{id,loadLimit?,baseTimeNs?,latencyWarningUs?}]; OR xml escape hatch; returnXml?; save?); bind_cpu (path; affinity = a name [CPU1..CPU8, MaskSingle/Dual/Quad/Hexa/Oct/All, None] OR a raw #x.. token; returnXml?; save?); get_linked_task (path? = PLC root under TIPC, default first child of TIPC); set_linked_task (path? = PLC root; linkedTask = XAE tree path of the RT task, e.g. TIRT^PlcTask; save?).
Inputs: action (required), xml, cpus, name, +15 more.
tc_treeA
TwinCAT System Manager tree items; paths use ^ separators (e.g. TIPC^MyPlc, TIID^Device 2 (EtherCAT)^Box 1). BATCH-FIRST: for more than one item use the matching *_batch action — N ops in ONE DTE attach, continue-on-error roll-up {count,succeeded,failed,results:[{...,ok,error?}]}, instead of an attach per call. Actions, grouped single / batch: READ identity — get / get_batch (paths:[...]); TEST existence — exists / exists_batch (paths:[...]); READ xml — get_xml (ProduceXml raw XML; summary:true for a compact identity + slot-module list); WRITE params — set_xml / set_xml_batch (items:[{path,xml}]) (ConsumeXml; compact unless returnXml:true); RENAME — rename / rename_batch (renames:[{name|path,newName}]) (keeps IO links intact); CREATE — create / create_batch (creates:[{parent,name,subType,before?,createInfo?}]); create VALIDATES the child and errors clearly on a malformed/ghost result instead of silently succeeding — adding an EtherCAT box needs a proper ESI-based createInfo (bare subType 9099 with no createInfo produces a blank-named ghost), recorded per-entry as ok:false; to ADD EtherCAT terminals/boxes from the ESI prefer the dedicated tc_ethercat tool; DELETE — delete / delete_batch (deletes:[{parent,name}], GUARDED: dryRun:true previews which children exist, confirm="ALLOW_TWINCAT_DELETE" to actually delete). Mutating *_batch verbs accept save:true to save once after the batch. No batch form: children (lists child items, incl. CPX-AP/Festo sub-modules), import (.xti under path), export (name to file), focus (Solution Explorer).
Inputs: action (required), xml, file, name, +16 more.
tc_variantA
Project VARIANT management on the open solution (needs TCatSysManagerLib >= 3.3.0.0; older installs get_* return empty and set_*/disable surface a clear COM error). OFFLINE CONFIG ONLY — no confirm token needed; runtime needs a later activate/download; per-item disable/enable refuses TISC (safety) paths. Optional save:true does File.SaveAll once after a write. Actions: get_config (read-only) — returns the raw XML (round-trip this FIRST to capture the live shape before editing). get_current (read-only) — active variant name; empty string => no variant active / not configured. set_config (xml, save?) — replaces the WHOLE variant definition: a document with ..VariantName.. and/or standalone .. children (raw XML verbatim, schema not validated). select (variant, save?) — sets the active variant by name (e.g. "Variant3") or a group in bracket form (e.g. "[Group1]"); must already exist in the config. disable / enable (path, save?) — sets disabled state on a tree item FOR THE ACTIVE VARIANT; path uses ^ separators (e.g. TIID^Device 2 (EtherCAT)^Box 1, TIPC^MyPlc) and MUST NOT be under TISC. The readback disabled int may report SMDS_PARENT_DISABLED=2 (read-only state: disabled because an ancestor is) — never written, only echoed.
Inputs: action (required), xml, path, save, +1 more.
twincat_activate_configurationB
Activate the TwinCAT configuration on the target. Guarded: confirm="ALLOW_TWINCAT_ACTIVATE".
Inputs: confirm (required).
twincat_restart_runtimeA
Start/restart the TwinCAT runtime on the target. Guarded: confirm="ALLOW_TWINCAT_RESTART".
Inputs: confirm (required).
xaeB
XAE shell: status, open_solution (solutionPath; closeExisting:true reopens, discardChanges:true closes the current solution WITHOUT saving before reopening), save_all, active_document, selected_items, error_list (default 50, in Error List order; pass limit to widen, severityFilter:'errors'|'warnings' to filter before the cap — count still reports the true matching total), clear_error_list, list_commands (filter regex, limit), dialog_probe (read-only: is a modal dialog blocking XAE right now? returns its title/text/buttons; never clicks anything), dialog_resolve (button, remember) — click a chosen button on the live modal dialog and optionally remember it in the allowlist; pair with dialog_probe. Destructive prompts (activate/restart/download/safety) are refused for auto-remember (the click still happens once).
Inputs: action (required), mode, limit, button, +6 more.
xae_buildC
Clean/Build/Rebuild the active solution configuration; waits for completion by default.
Inputs: action (required), timeoutMs, waitForFinish.
xae_commandB
Execute a raw XAE/DTE command by name (e.g. View.SolutionExplorer). Guarded: confirm="ALLOW_XAE_COMMAND_EXEC".
Inputs: confirm (required), commandName (required), args.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 25 tools
Each tool targets a distinct TwinCAT subsystem or lifecycle stage: PLC authoring (plc_pou) vs project lifecycle (plc_project) vs deployment (plc_download) vs online session (plc_session); EtherCAT (tc_ethercat) vs non-EtherCAT fieldbus (tc_fieldbus) vs generic tree ops (tc_tree); individual links (tc_link) vs project-wide mapping (tc_mapping). Overlapping boundaries are explicitly negotiated in the descriptions themselves — tc_tree directs EtherCAT creation to tc_ethercat, tc_fieldbus excludes EtherCAT, and the twincat_activate/restart/plc_download trio is clearly separated as a pipeline. Even the potentially confusable pairs (xae vs xae_command, tc_task vs tc_module) are sharply differentiated by scope.
Snake_case is used throughout with recognizable subsystem prefixes: tc_* for System Manager domains, plc_* for PLC engineering, twincat_* for lifecycle actions, xae_* for IDE shell. Internal action names follow a consistent verb_noun pattern (create_batch, set_xml, get_decl, link, unlink). Deviations exist: bare 'nc' breaks the prefix convention, 'xae' sits alongside 'xae_build' and 'xae_command' at uneven granularity, and tool suffixes mix nouns (tc_system, tc_ethercat) with verbs (plc_download, xae_build).
At exactly 25 tools, the server sits at the top of the heavy range. However, the domain is genuinely enormous — full TwinCAT XAE engineering covering IDE shell, PLC authoring/lifecycle, fieldbus, runtime tasks, TcCOM modules, linking, licensing, C++, measurement, and variants — and each tool maps to a major subsystem rather than a single operation. Some tools are thin (nc exposes only three read actions; tc_license is minimal) and could arguably have been folded into tc_task or tc_tree, but the overall decomposition is defensible given the breadth.
The surface covers the full engineering workflow: build, activate configuration, download boot project, restart runtime, online session control, PLC object CRUD with batch and surgical operations, EtherCAT and non-EtherCAT fieldbus creation, variable linking and bulk mapping, tasks, TcCOM modules, C++ projects, libraries, routes, variants, and licensing. The gaps are mostly deliberate and documented: graphical POU bodies are read-only (diagnostic only), solution creation from scratch is absent (only open_solution exists), measurement data export is explicitly omitted as unverified, and safety (TISC) is intentionally out of scope. Agents can work around these, but a fresh-solution workflow and graphical PLC editing would require external tooling.
Maintenance
Related MCP Connectors
Twinfield AI Connect: hosted MCP server for Twinfield.
MCP server for generating rough-draft project plans from natural-language prompts.
MCP Server for Slima - AI Writing IDE for Novel Authors with AI Beta Reader.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceA Model Context Protocol server that enables seamless interaction between MCP clients (like Claude Desktop) and CODESYS V3 programming environments, allowing automation of project management, POU creation, code editing, and compilation tasks.87 npm130MIT
- AlicenseBqualityDmaintenanceAn MCP server for validating, auto-fixing, and scaffolding TwinCAT 3 XML files using deterministic code quality tools and IEC 61131-3 OOP checks. It enables AI assistants to perform structural validation, apply safe fixes, and generate canonical code skeletons for industrial automation projects.1640MIT
- FlicenseNot gradedqualityCmaintenanceMCP 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.60-
- AlicenseBqualityDmaintenanceMCP server that enables AI agents to read, edit, and create PLC projects in EcoStruxure Control Expert via COM automation.524MIT