WinCC Unified HMI

Inspect, edit, compile and deploy WinCC Unified HMIs with plccheck hmi and the MCP server.

plccheck mcp is a stdio MCP server. It shares the HMI application service with plccheck hmi. The existing plccheck tia rpc remains the internal TIA JSON-RPC interface. One MCP process owns one persistent bridge session, attached to an already-open project. Operations within that process are serialized.

Start

Build the Go CLI and Windows bridge from the same checkout:

dotnet build tia_openness_bridge/TiaOpennessBridge.csproj -c Release
go -C go_lsp build -o ../out/plccheck.exe ./cmd/siemens-lsp

Use the existing tia host installation workflow for a packaged bridge. During development, pass the built executable with --bridge; that exact executable must be trusted by TIA Openness. The existing tia trust --bridge <path> --install command handles the Windows trust setup. Rebuilding changes the trusted hash.

Configure an agent's stdio MCP connection to run this command:

plccheck mcp --project "Open project name" --allow-project-writes --allow-runtime-control

Add --bridge <absolute-executable-path> for a development build. Project-write and runtime-control flags are optional; omit them to inspect those resources. Host setup is enabled by default. HMI engineering operations use public Openness APIs and do not require allowDeviceActions or private reflection. Runtime control is separate and addresses only a local simulation GUID.

When several open copies have the same project name, select the full .ap21 path. The service attaches using that exact path and checks it after attachment; an ambiguous name fails before engineering operations start.

The MCP SDK is pinned to v1.3.1, which supports the repository's Go 1.24 toolchain. MCP protocol negotiation, schemas and stdio transport use the official SDK.

Operations

CLI MCP tool Behavior
hmi list hmi_list Find Unified software, including nested device groups
hmi inspect hmi_inspect Properties, writable flags, creation signatures and paginated collections
hmi set hmi_set Set a scalar, translated text or complete event script with a fingerprint precondition
hmi create hmi_create Call a collection's public Create, including generic control creation
hmi delete hmi_delete Check object name/type and delete; known child collections require explicit recursive
hmi export Use hmi_inspect and save the returned value Export a selected script or JSON inspection to a new file
hmi script-read hmi_script_read Export one global JavaScript module through Siemens' native API and return its source and fingerprint
hmi script-write hmi_script_write Create or replace one global module with a fingerprint precondition and native readback
hmi compile hmi_compile Compile the HMI device and return diagnostic messages, counts and timing
hmi deploy hmi_deploy Inspect compiled files, then transfer their checked snapshot to the local simulator and verify runtime startup
hmi save hmi_save_project Explicitly save the entire attached TIA project
hmi runtime hmi_runtime List, status, start or stop through SIMATIC Runtime Manager
hmi diagnostics hmi_diagnostics List recent WinCC CSV logs or read a bounded log tail
hmi tags hmi_read_tags Read values, quality codes and timestamps through local Siemens Open Pipe
hmi assert-tags hmi_assert_tags Poll tag values and Good quality until all conditions pass or the test deadline expires
hmi network hmi_network Inspect Ethernet interfaces and assign an existing Siemens access point, including S7ONLINE
hmi certificates hmi_certificates List WinCC certificates or trust an exact PLC leaf certificate by SHA-256 fingerprint and IP address

CLI commands accept --request <json-file>. Engineering commands also accept --project, --target and --bridge. Request paths are relative to the caller's working directory, including the npm wrapper's development mode.

--timeout bounds one CLI operation. The default is 2 minutes, or 10 minutes for deploy. An interrupted write can have an unknown outcome. MCP tool calls do not use this flag; hmi_deploy applies the same 10-minute default.

--runtime-manager <exe> selects the Runtime Manager executable for hmi runtime and hmi_runtime. The default is %ProgramFiles%\Siemens\Automation\WinCCUnified\bin\SIMATICRuntimeManager.exe. Deployment always uses the default installation path.

Compile, deploy and test without desktop automation

Use hmi_compile, then take the selected directory and projectId from its compiledDirectories result. Inspect the compiled download set:

{
  "action": "inspect",
  "directory": "C:/Projects/MyProject/IM/HMI/I/0/Context",
  "projectId": "bd5d011c-d07f-f2a9-ebac-19bc146f032b",
  "mode": "delta"
}

Send the same request to hmi_deploy with action: "deploy" and expectedFingerprint from plan.fingerprint. Deployment requires --allow-runtime-control. Use mode: "full" for the first download or an incompatible delta; the tool never silently switches a delta into a full update. Full updates restart the selected simulation. Runtime control remains local.

plccheck hmi deploy --request inspect-build.json --bridge <bridge.exe>
plccheck hmi deploy --request deploy-build.json --bridge <bridge.exe> --allow-runtime-control

The current adapter supports TIA V21 with Unified panels configured for V20 device firmware, including the tested MTP1200 Unified Basic. It requires exactly one registered local runtime project, the requested simulation. Other target types and multiple registered projects produce explicit errors. The adapter loads Siemens' installed transfer libraries in a separate process and uses 127.0.0.1:20008. It uses internal Siemens APIs, so each additional TIA version needs validation. No Siemens binaries are included in the package.

Inspection checks the native manifest, project GUID, files, paths and hashes. Deployment copies and rechecks an immutable snapshot before connecting. The response separates preflight, transfer, activation and stable runtime timing. Three successive Runtime Manager Running responses are required for success. This establishes runtime availability; verify rendered UI behavior in the browser and PLC communication through tag quality. Browser automation is the supported UI testing route. Native desktop computer use is unnecessary.

After a confirmed download, a receipt is stored under %LOCALAPPDATA%/plccheck/hmi-deployments. Before the next hmi_compile, the bridge checks that the compiled files still match that receipt and finalizes the old generated delta files under TIA exclusive access. This step is necessary for successive delta downloads. Use this compile tool throughout an iteration loop; compiling through another route before finalization can invalidate the receipt. Changed files are preserved and reported as an error.

If activation succeeds but startup fails, the response has activated: true and success: false. Inspect hmi_diagnostics; do not treat accepted transfer as a working UI. Interrupted deployment has an unknown outcome and must be checked before retrying. A stale or incompatible generated build can require a clean project copy and full compilation. The tool does not remove an open project's complete build cache automatically.

WinCC rolls back a delta when available memory is below its warning level; WCCILdata(0).Core.csv records the rejection. A stale generated build can fail runtime startup with an RDF consistency error, such as a duplicate ID. If a delta or its rollback leaves the runtime inconsistent, the recovery tested through TIA's own simulation download was: Compile > Software (rebuild all) for the HMI, a full download, a runtime health check and a browser reload. Later compatible edits could use deltas again. With hmi deploy, the equivalent is a rebuild followed by mode: "full". A rebuild outside hmi_compile can invalidate the deployment receipt described above.

Measurements on one workstation with an MTP1200 Unified Basic simulation:

Operation Measured time
Compile a clean project copy 27.3 s
Full download through activation 11.4 s
Full download including stable runtime checks 28.1 s
Compile a warm label edit 1.5–1.6 s
Warm delta through activation 3.55–4.16 s
Warm delta including stable runtime checks 7.7–8.3 s

The runtime keeps its process during successful deltas. WebRH can keep showing an already-open screen until the browser reloads.

Building a new bridge changes its executable hash. The normal tia trust --install provisioning step must refresh Siemens' Openness trust before the new engineering methods can attach. Deployment itself does not attach to TIA and does not require that trust entry. Existing host network/certificate tools retain their normal unelevated operation on the configured workstation.

Configure the local S7 interface and certificate trust

Host setup is enabled by default and runs without TIA, computer use or interactive prompts. CLI commands use the same JSON requests as MCP.

First call hmi_network with:

{"action":"inspect","accessPoint":"S7ONLINE"}

The response includes the current assignment, installed Siemens interfaces with their TCP/IP protocol and adapter GUID, and Windows adapter names, indices and IP addresses. Match the GUID to the adapter that reaches the simulator. Select an exact interface with selectable: true, then use the inspected assignment as expectedDevice (an empty string for an unassigned access point):

{
  "action":"set-access-point",
  "accessPoint":"S7ONLINE",
  "device":"VMware Virtual Ethernet Adapter for VMnet1.TCPIP.1",
  "expectedDevice":""
}

This example assigns an initially empty access point. A stale expectedDevice fails. Repeating the current assignment is an idempotent operation. Success includes native API readback and verified: true. Changes apply to new connections. This configures Siemens' Ethernet interface selection; Windows IP/subnet/gateway settings are currently inspection-only.

The Windows amd64 implementation calls the installed Siemens S7Epa API (s7epaapi64x.dll), including LogNameSetLogDevice. It does not edit the OAM XML configuration. Requests from one MCP process are serialized. The precondition and write are separate native calls, so another process can still race the change. Readback checks the resulting assignment. These API bindings were tested with the locally installed V21 software; missing DLLs/exports produce explicit errors.

List certificates with hmi_certificates:

{"action":"list"}

Files that cannot be parsed as one certificate, such as PEM bundles, are listed with an error and no fingerprint. They do not block listing or trust, and neither do untrusted files that cannot be read. A trusted file that cannot be read, for example because access is denied, fails the call: it could be the existing trusted copy of the requested fingerprint. Trust results list untrusted files it could not read in unreadableUntrusted, since one of them may be an untrusted copy of the same certificate that was left in place.

Use the fingerprint of the simulator's public certificate and its IPv4 address:

{
  "action":"trust",
  "sha256":"<64 hexadecimal characters from the simulator certificate>",
  "peerAddress":"192.168.126.1"
}

To prepare trust before WinCC first connects, include certificatePem with one public PEM certificate. The tool checks its exact fingerprint, validity period and IP subject alternative name, then publishes and reads back the DER bytes. It accepts leaf certificates and preserves unrelated entries. Matching entries move from untrusted/certs to trusted/certs; repeat trust returns alreadyTrusted: true. This follows Siemens' documented application certificate store behavior.

The default store is %ProgramData%/SCADAProjects/certstore. Use the startup flag --certificate-store <directory> for an existing nondefault store. Writes are limited to that application's public certificates; no private key is needed. Trust readback confirms the store contents. Use runtime diagnostics and tag quality to establish an actual PLC connection.

Both operations use the current Windows account. On the tested workstation, Siemens' existing OAM permissions and SIMATIC HMI group access allow them from an ordinary, unelevated process. Other installations must grant the account the corresponding access during provisioning. Permission failures return an error; the tools never launch a UAC prompt or change ACLs.

CLI equivalents, with the requests saved as JSON files:

plccheck hmi network --request inspect-network.json
plccheck hmi network --request assign-network.json
plccheck hmi certificates --request trust-certificate.json

Inspect and edit

An inspection request for a complete screen event script:

{
  "target": "HMI_RT_1",
  "path": [
    {"member": "Screens"},
    {"name": "Home"},
    {"member": "EventHandlers"},
    {"index": 0},
    {"member": "Script"}
  ],
  "member": "ScriptCode"
}
plccheck hmi inspect --project "Open project name" --request read.json
plccheck hmi export --project "Open project name" --request read.json --out Home.Loaded.js

Each path step has exactly one selector. Use name for named collection members and index for unnamed objects such as events and translation entries. Inspect the collection and event type first. Paths cannot traverse Parent.

To edit, keep target, path, and member, then add value and the expectedFingerprint returned by inspection. Run hmi set --request edit.json --allow-project-writes. The bridge holds TIA exclusive access while comparing and writing. A mismatch rejects the edit. It returns the actual readback and previous value. Each edit is independent; there is no multi-property transaction. An interrupted write can have an unknown outcome: inspect before retrying.

Property discovery lists metadata without evaluating scalar value getters; scalars are marked readSeparately. Supplying member reads that value and its fingerprint, subject to the bridge's response-size limit. Complete scripts are returned or oversized results fail explicitly. Password properties and password-valued driver properties are redacted and cannot be set with hmi_set.

Create and delete

Create a screen:

{"target":"HMI_RT_1","path":[{"member":"Screens"}],"arguments":["TestScreen"]}

Create a button on it:

{
  "target":"HMI_RT_1",
  "path":[{"member":"Screens"},{"name":"TestScreen"},{"member":"ScreenItems"}],
  "type":"Siemens.Engineering.HmiUnified.UI.Widgets.HmiButton",
  "arguments":["TestButton"]
}

Use hmi inspect on a collection to obtain its createMethods before supplying arguments. type selects the public Unified type for a generic Create method. For example, an event collection takes an event enum such as "Loaded".

Deletion requires expectedName and expectedType. Deleting a screen with controls or events also requires recursive: true. This identity check is not a full subtree fingerprint. Review the selected object before deletion.

Edit global modules as JavaScript files

Global modules such as Diag do not expose a writable ScriptCode property. The bridge uses public HmiScriptModule.Export and HmiScriptModuleComposition.Import instead. This is a tested use for export alongside MCP: agents can review/edit ordinary files, while TIA imports and compiles them using its own document format.

Read request (module.json):

{"target":"HMI_RT_1","module":"Diag"}
plccheck hmi script-read --project "Open project name" --request module.json --out Diag.hmi.js

The JSON response contains a fingerprint. --out creates a new UTF-8 file and preserves an existing destination. Edit the source, keeping Siemens' generated document markers. Create a write request (edit-module.json):

{
  "target":"HMI_RT_1",
  "module":"Diag",
  "expectedFingerprint":"<fingerprint from script-read>"
}
plccheck hmi script-write --project "Open project name" --request edit-module.json --source Diag.hmi.js --allow-project-writes
plccheck hmi compile --project "Open project name" --target HMI_RT_1 --allow-project-writes

MCP uses the same requests, with a code string instead of a source filename. The CLI also accepts inline code; supplying both code and --source fails. Source files must be UTF-8 and at most 1 MiB; a UTF-8 BOM is accepted and removed. Module names must be safe Windows document basenames, at most 128 characters. An existing name with different casing is rejected, rather than treated as an absent module that could be overwritten during import.

To create a module, first read its intended name. An absent module returns exists: false and fingerprint: "absent"; use that precondition for the write. TIA may insert its document marker and normalize formatting. The write response returns TIA's actual source and new fingerprint. These commands cover global modules; screen/control event scripts continue to use hmi inspect/set/export.

The bridge compares the current native export and imports while holding TIA exclusive access. After import it resolves new script objects before exporting the result: Siemens invalidates the replaced module and cached collection. Each request uses fresh temporary directories. Existing Siemens manifests are preserved; creation uses the tested V21 version-2 manifest with quoted names. No caller-provided YAML or filesystem path reaches the native importer.

Import does not save or deploy the project. A false Siemens result is an error. A failed/interrupted import can leave changes, so read the module before retrying. This is one-module editing, without multi-module rollback.

Runtime and diagnostics

hmi runtime request examples:

{"action":"list"}
{"action":"status","projectId":"00000000-0000-0000-0000-000000000000"}
{"action":"start","projectId":"00000000-0000-0000-0000-000000000000"}

Use the actual GUID from the list. Start/stop require --allow-runtime-control. Commands include elapsed time, Siemens' output and its return code. Siemens uses nonzero informational success codes. Runtime Manager success does not establish browser readiness or good PLC tag quality.

Run hmi diagnostics with no request to list logs; pass {"file":"WCCILdata(0).Core.csv"} to read its last 64 KiB. Results indicate truncation. Logs may contain application data. No trace flags are changed.

The logs are read from %ProgramData%\Siemens\Automation\Logfiles\WinCC_Unified_PC. Instance numbers in the file names vary. Useful files:

  • WCCILScs(1).SCS.csv: download validation, activation, rollback and startup.
  • WCCILdata(0).Core.csv: data manager errors, including low-memory delta rejection.
  • WCCILpmon(1).Core.csv: driver process startup attempts and timeouts.
  • GfxRTS(22).ScriptFW.csv: script context, module, function and trace messages.

CSV records can contain quoted multiline messages. Parse a tail as CSV rather than treating each line as one event.

Siemens' screen script debugger listens on a local port chosen when it is enabled in Runtime Manager. Port 9222 can already be owned by another Chromium process; choose a free port. These commands do not attach a debugger.

hmi tags --request tags.json accepts {"tags":["MotorSpeed","Ready"]}. It uses the local HmiRuntime pipe and requires the existing Windows SIMATIC HMI group membership. allGood is false for uncertain/bad quality or a per-tag error. A successful read with allGood: false remains useful diagnostic evidence; test scenarios must assert the required quality themselves. The command does not write tags or change HMI connection settings. See Siemens' Open Pipe overview and ReadTag contract.

Assert runtime behavior after browser actions

Use hmi_assert_tags or plccheck hmi assert-tags --request assertions.json:

{
  "conditions": [
    {"tag": "MainNavigationState", "equals": "1"},
    {"tag": "MotorSpeed", "minimum": 99.5, "maximum": 100.5},
    {"tag": "Ready"}
  ],
  "timeoutMs": 5000,
  "pollIntervalMs": 250,
  "stableForMs": 500
}

Every condition requires Good quality and no tag error. equals compares the exact scalar text returned by Open Pipe; inspect it with hmi_read_tags first. Numeric bounds are inclusive and reject nonnumeric, NaN and infinite values. Bounds use double precision; use equals for exact large integers. Omitting value constraints checks quality only. Each tag may occur once, with at most 64 tags per request. All conditions must pass together.

The default deadline is five seconds, bounded to one minute. Stability means that every sampled read matched over the requested duration; it cannot detect changes between polls. A bad sample or read failure resets that duration. The window starts at the first matching sample, so stableForMs must leave at least one pollIntervalMs before timeoutMs. Each read also takes time, so leave further margin: a window close to that limit can still time out. Results include the latest values, quality codes, timestamps, mismatch reasons, attempt count and elapsed time. Timeout returns success: false, MCP isError: true, and CLI exit code 1. Cancellation does not pass a late sample. Diagnostic hmi_read_tags calls still succeed when they report bad quality.

This operation only reads the local runtime. It can verify browser navigation and, once the PLC connection is working, the results of program scenarios. It does not write or force PLC outputs. No TIA attachment or write permission is needed.

A tag that has not been written yet can report Uncertain quality (76) with the expected value. The assertion fails for it.

Tested configuration

The commands were tested with TIA Portal V21 and an MTP1200 Unified Basic panel in the local simulation. A stdio MCP client discovered all 16 tools and ran inspect, set, compile and deploy cycles, global module read and write, host network and certificate setup, and tag assertions after browser clicks. None of this needed desktop automation. Tag reads and assertions were tested without a working PLC connection.

Automated tests cover MCP discovery, validation and errors, write permissions, runtime command isolation and success codes, bounded UTF-16 log tails, Open Pipe fragmentation, correlation, quality and cancellation, host setup and certificate validation, CLI behavior and npm path handling.

Siemens V21 tag getter failure

Reading HmiTag.ConfirmationType terminates TIA V21. Its crash report identifies AuditConfirmationTypeAdapter.DoGetPropertyValue() and "Property not found : ConfirmationType". GetAttributeInfos nevertheless advertises that attribute.

The HMI API blocks that accessor for the V21 HmiTag type and lists property metadata without evaluating every value. A regression test uses getters that throw to enforce that behavior. Individual reads must specify member. Collection names and driver-property names are still read for identification and secret redaction. Broad discovery of other untested getters should use a disposable project copy.

Openness-owned sessions

Opening a project copy in an Openness-owned WithoutUserInterface session lost its connection after opening. The same copy worked when opened in a separately launched Portal process and attached through Openness. The cause is unresolved. It is not established that TIA can run without a desktop session.

Remaining work, in priority order

  1. Extend deployment validation to other firmware/PC targets and multiple registered projects. Add unattended clean-build preparation and recovery. The implemented local transfer route avoids the public DownloadProvider failure (HMI_TRANSFER_OFFLINE_FULL_DOWNLOAD/HmiScsDownloadFeedback occurs before its configuration callback). ZIP archive creation remains unverified; the working command streams native compiled files through Siemens' transfer facade instead.
  2. Add complete scoped snapshots, native tag import/export, typed secret updates and broader creation coverage. Current JSON inspections are not complete project backups or round-trip imports.
  3. Finish native S7 communication with the generated PLC while keeping the production driver. Status and next steps are in docs/wincc-unified-s7-simulation.md.
  4. Export complete screens, events, tags and modules to editable files, and add hmi check, hmi plan, hmi apply and hmi test with matching MCP tools. The file format and apply rules are in the design notes, docs/wincc-unified-agent-workflow.md.
  5. Extend the offline script test spike into scenario tests for the HMIRuntime calls a project uses.
  6. Add a limited browser preview once export exists.