TIA Portal MCP server
MCP server for Siemens SIMATIC TIA Portal V21. It lets MCP clients and AI agents inspect a running TIA Portal project through the Siemens Openness API.
The current implementation covers project discovery and lifecycle operations, PLC block export/import, tag table reads and guarded tag mutations, hardware/network discovery, cross-reference diagnostics, hardware catalog search, guarded network-device provisioning, and compile/check diagnostics.
Tools
The server currently exposes 14 tools in read-write mode and 4 tools in read-only mode.
Batch operations
execute_read_batch- run up to 50 retained generic read operations in one call. Each item carries anoperationId, anoperationname (e.g.get_block_content,list_tag_tables), and that operation's parameters. Reads run independently, so a failing item does not stop the others. Boundread_cross_referenceswithmaxResults; oversized batch responses are truncated or omitted server-side with explicit markers.preview_write_batch/apply_write_batch- preview up to 50 retained generic data writes and receive one batch-levelsafetyTokenbound to the exact ordered operation list and the combined current state, then apply them. Apply runs sequentially, stops on the first failure, and marks later itemsskipped(no transaction or rollback). Requiresconfirm=trueand thesafetyToken. Project-lifecycle and network writes stay dedicated.
The generic batch tools are the path for retained block, PLC type, tag-table, tag, and user-constant operations. Each operation name carries that operation's parameters as one item; a single operation is just a one-item batch.
Every operation result may carry a warnings array — non-fatal degradation notes captured from the TIA Openness worker. A populated warnings array means the payload may be partial.
Available read operations for execute_read_batch: read_cross_references, get_block_content, list_tag_tables, and get_type_content.
Available write operations (for preview_write_batch / apply_write_batch): update_block_logic, update_type_content, create_block / delete_block, create_block_group / delete_block_group, create_tag_table / delete_tag_table, create_tag / update_tag / delete_tag, create_user_constant / update_user_constant / delete_user_constant.
get_block_content / update_block_logic and get_type_content / update_type_content accept a format field. format=source is available for global data blocks, PLC data types, and SCL-language FB/FC/OB. Every other block language stays on format=xml.
withDependencies (reads only, default false) asks TIA Portal to include the object's dependency closure. The resulting document declares several objects and is context only — a write refuses any source declaring more than one object, and the read carries a warning saying so. Omit the field to get a document you can edit and submit back.
Network operations
network_read and network_write both declare an MCP output schema and return one canonical JSON document identically as the content text block and as structuredContent — never a nested JSON string inside an outer envelope. This is the Phase 2 JSON contract; see docs/SupportedOperations/NETWORK_OPERATIONS_SUMMARY.md for the exact envelopes.
network_read- run up to 50 dedicated network reads:read_hardware_config,search_equipment_catalog,list_network_objects, andinspect_network_object. Reads run independently. Bound catalog searches withqueryandmaxResults;read_hardware_configaccepts an optionaldeviceNamefilter, an optionalplcNamefor tag matching, and opt-in structured I/O extraction (includeIoDetailsfor addresses and channels,includeTagMatchesfor exact PLC tag matches per channel). Detailed I/O output can be large, so filter bydeviceNameor split into separate calls.network_write- preview or apply up to 50 dedicated network writes:add_network_device(flattypeIdentifier/deviceName, since it names something that does not exist yet),configure_network_device(nestedtarget: { deviceName, nodeId }pluschanges: { ipAddress?, subnetMask?, pnDeviceName?, subnet?: { subnetId }, ioSystem?: { subnetId, number } }— a nullchangesmember means leave that setting unchanged),create_subnet(subnet: { name, networkType }, plus PROFIBUS-onlyhighestAddress/transmissionSpeed),update_subnet(target: { subnetId }plussubnetChangeswith at least one member), anddelete_subnet(target: { subnetId }— connected or not). Call withconfirm:falseand no token for a preview, then call the same tool withconfirm:true, the unchanged ordered list, and the returnedsafetyToken. Apply is sequential, stops on the first failure, marks later items skipped, and does not roll back completed writes:network_writeattaches an explicit warning to the failed item that this operation and any earlier operation in the same call may already have changed TIA state, and that you should re-read withnetwork_readbefore retrying rather than blindly re-running the batch.
configure_network_device targets one exact existing node: target.deviceName (case-insensitive) plus the exact target.nodeId reported by a prior network_read — never the first interface or first node on a device. changes.subnet.subnetId and changes.ioSystem.subnetId/changes.ioSystem.number are similarly exact, subnet-scoped selectors. Selector resolution is fail-closed: a selector that matches zero, more than one, or an unreadable candidate always fails with postcondition_failed rather than falling back to a guess. This is what makes it safe to target one port on a multi-homed device (a PC station with several network interfaces, for example) — configuring one node's exact nodeId changes only that node; every other node on the device is left byte-for-byte unchanged. Always follow a network_write apply with a network_read (read_hardware_config) post-read to confirm the outcome — the response never echoes back a re-read of the written value.
read_hardware_config additionally reports unreadable members in a payload-level messages array; device/module name and type-identifier fields omit values that could not be read instead of returning 0/empty-string placeholders (a few secondary name fields still fall back to an empty string, with the failure noted in messages). Hardware configuration data is engineering evidence, not certification that a physical installation has been commissioned.
read_hardware_config supports opt-in structured I/O extraction. With includeIoDetails: true, each device item carries an ioDetails object with addresses[] (Openness Address evidence: ioType, byte-based startAddress, length, dynamic context, and ordinal controllerNames) and channels[] (number, ioType, type, bit-based channelAddressBits, channelWidthBits, and a formatted logicalAddress such as %I4.0/%IW64 emitted only when the evidence is present and aligned). With includeTagMatches: true (which requires includeIoDetails), each channel carries tagMatches[] resolved deterministically against one selected PLC's tag tables — plcName selects the PLC by exact name, otherwise tag matching applies only when exactly one PLC exists. A tag matches a channel only when its normalized absolute I/O interval is identical to the channel's, and tags are never matched across controllers. A default read (no flags) returns no ioDetails and is byte-identical to earlier versions. See docs/SupportedOperations/NETWORK_OPERATIONS_SUMMARY.md for the exact request/response shapes and unit semantics.
A separately authorized, PowerShell 7 live-TIA acceptance harness for this contract lives at scripts/live-test-network-phase2.ps1. It is never run by any automated test; see the script's own comment-based help for its Read/Preview/Apply modes and required confirmation gates. The read-only I/O-map harness lives at scripts/live-test-network-io-map.ps1 and is equally never run by automated tests.
Available write operations (for preview_write_batch / apply_write_batch): update_block_logic, update_type_content, create_block / delete_block, create_block_group / delete_block_group, create_tag_table / delete_tag_table, create_tag / update_tag / delete_tag, create_user_constant / update_user_constant / delete_user_constant, add_network_device, configure_network_device, start_plc / stop_plc.
Project tools
get_project_status— read active project metadata without opening or switching projects.browse_project_tree— browse a bounded project subtree with optionaldepthandstartPath.compile_check— compile a PLC or selected block and return compiler messages; available only in read-write mode.open_project/create_project/save_project/save_project_as/archive_project/close_project- project lifecycle writes. These stay single-tool only (not batchable) and are self-previewing: call the tool WITHOUTsafetyTokento get a preview plus a single-use token, then call it again withconfirm=trueand the token to apply.
Write safety
Every MCP write operation uses a preview-then-apply workflow. Generic batch data writes preview with preview_write_batch and apply with apply_write_batch. Network and project lifecycle writes are self-previewing: call the same write tool WITHOUT safetyToken (with confirm:false for network_write) to get the preview (summary, currentStateHash, requestedInputHash, a fresh single-use safetyToken, and instructions), review it, then call the same tool again with the same arguments plus confirm=true and the safetyToken.
Safety tokens are single-use, expire 10 minutes after preview, and are bound to the exact tool name, normalized project path, target, requested input, and current project state. The server rejects missing, expired, reused, mismatched, or stale-state tokens. Successful write attempts append audit JSONL records under %LOCALAPPDATA%\TiaMcpServer\audit.
preview_write_batch issues one token for the whole batch, bound to the exact ordered operation list and the combined current state. Reordering items, changing any item's input, retargeting the project path, or a change in project state all invalidate the token. apply_write_batch re-reads the combined current state once before consuming the token, then applies items sequentially and stops on the first failure.
network_write snapshots topology once for preview and once for apply-time token validation. Its token is bound to the exact ordered network operation list and project state; successful apply attempts append an audit record.
Every failed write reports a categorized failureCategory field alongside its human-readable error message, so a caller can branch on the exact failure without parsing text: validation_error, binding_conflict, state_changed, worker_operation_failed, worker_timeout, worker_crashed, or postcondition_failed. save_project_as requires rebind:true; calling it with rebind:false is rejected up front with validation_error before any preview, safety-token issuance, Siemens SaveAs call, or audit write, so it has no side effects. Warnings are always reported in a separate warnings array from the primary success/failure outcome — a populated warnings array never turns a failure into a success, and a categorized failure is never masked by an accompanying warning.
Architecture
TIA Portal V21 ships its Openness API as .NET Framework 4.8 assemblies. Those assemblies use .NET Framework remoting APIs that cannot run correctly inside a .NET 8 process.
This project therefore uses two processes:
TiaMcpServer- the .NET 8 MCP stdio server and .NET global tool host.TiaMcpServer.OpennessWorker- a .NET Framework 4.8 worker process that loadsSiemens.Engineering.*and talks to TIA Portal.
The MCP host keeps one persistent .NET Framework 4.8 worker process attached to TIA Portal and exchanges newline-delimited JSON over stdin/stdout. Requests are serialized, and the worker restarts automatically after a crash or timeout. Siemens DLLs are never copied into this repository or the NuGet package; the worker resolves them from the local TIA Portal V21 installation.
Quick start
Install the server as a .NET global tool, check your environment, and register it with an MCP client:
dotnet tool install -g TiaMcpServer
tia-mcp doctor
tia-mcp install claude-code
tia-mcp doctor validates Windows version, .NET runtimes, the TIA Portal installation, Openness
assemblies, user-group membership, and host/worker compatibility before you connect anything. Run it
first — it reports exactly which prerequisite is missing.
| Prerequisite | Notes |
|---|---|
| Windows | Windows-only; the Openness API has no other host |
| Siemens TIA Portal V21 | with Openness installed and enabled |
Siemens TIA Openness group |
the current Windows user must be a member |
| .NET SDK 8.0 or newer | required by dotnet tool install |
| .NET Framework 4.8 runtime | required by the Openness worker process |
Supported clients for tia-mcp install: Claude Code, Codex, OpenCode, MiMoCode. Servers register in
read-only mode by default; add --access-mode read-write to expose the write tools.
Binding to a specific project, every install option, and the full access-mode reference are in the installation guide. To build from source instead of installing the published tool, see building from source.
Documentation
Using the server
- Installation — install, verify with
doctor, register with a client, access modes - MCP client configuration — client config reference and block path addressing
- Troubleshooting — common failures and verified TIA Portal V21 behavior
- Supported operations — every operation by area, with parameters
Understanding the design
- Architecture — two-process topology, access enforcement, write safety, the canonical JSON seam
Building and contributing
- Contributing — workflow, branch and commit conventions
- Building from source — build, test, coverage, run locally
- Local MCP sandbox testing — the MCP Inspector test loop
- Packaging — build the package, install a local build as
tia-mcp
Direction
- Roadmap — directional priorities
- Network operations roadmap — phased network tool delivery
- Export/import format roadmap — source-format exchange
- Improvement log — open follow-ups and completed engineering work
Contributing
Contributions are welcome. See CONTRIBUTING.md for the development workflow and how to set up your environment. For architecture and build reference, see AGENTS.md.
Security
For how to report security vulnerabilities, see SECURITY.md.
No comments yet
Be the first to share your take.