TwinCAT-XAE-MCP
MCP server for Beckhoff TwinCAT 3 (TE1000 / XAE Automation Interface) — drive the System Manager tree, PLC authoring, IO/EtherCAT, linking, builds, and deployment from an AI agent.
Install / Use
claude mcp add Edge-JB -- npx -y github:Edge-JB/TwinCAT-XAE-MCPIf the server publishes to npm under a different name, use that package instead — check the repo README.
MCP Server
Model Context Protocol server
Quality Score
Category
AutomationSupported Platforms
Tags
Skill content
View source on GitHubte1000-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.
[!IMPORTANT] This server drives a real 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
- Highlights
- How it works
- Requirements
- Install
- Build the daemon
- Configure your MCP client
- Environment variables
- Quickstart
- Tool reference
- Safety & guards
- Reliability: dialog watchdog & PLC session control
- Troubleshooting
- Examples
- Documentation
- Contributing
- License
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,resultType- cache-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.
- cache-hint stamping) and the legacy
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.js
Two 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 TCatSysManagerLib.dll (ships with TwinCAT) |
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 install
Verify 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
[!NOTE] The running daemon locks
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 |
|---|---|---|
| TE1000_PROGID | TcXaeShell.DTE.17.0 | XAE Shell COM ProgID to attach to (passed through to the daemon as progId). |
| TE1000_DAEMON_PIPE | te1000-mcp | Named-pipe name. The front and the daemon it spawns share this, so a custom value applies to both. |
| TE1000_DAEMON_CONNECT_MS | 20000 | How long the
Truncated for display — read the full file on GitHub.
Related Skills
momen-cursurrules-prompt-file
40.6kCursor rules for building custom frontends with Momen.app as headless BaaS with GraphQL API, actionflows, AI agents, and Stripe integration.
pyspark-etl-best-practices-cursorrules-prompt-file
40.6kCursor rules for PySpark ETL development with code style, joins, window functions, map operations, and Iceberg patterns.
semiotic-react-dataviz-cursorrules-prompt-file
40.6kCursor rules for Semiotic data visualization library with 30+ chart types, MCP server, and AI-assisted chart generation.
Agent-Reach
72.5kGive your AI agent eyes to see the entire internet. Read & search Twitter, Reddit, YouTube, GitHub, Bilibili, XiaoHongShu — one CLI, zero API fees.
