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
- 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
DownloadProviderfailure (HMI_TRANSFER_OFFLINE_FULL_DOWNLOAD/HmiScsDownloadFeedbackoccurs before its configuration callback). ZIP archive creation remains unverified; the working command streams native compiled files through Siemens' transfer facade instead. - 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.
- 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. - Export complete screens, events, tags and modules to editable files, and add
hmi check,hmi plan,hmi applyandhmi testwith matching MCP tools. The file format and apply rules are in the design notes,docs/wincc-unified-agent-workflow.md. - Extend the offline script test spike into scenario tests for the
HMIRuntimecalls a project uses. - Add a limited browser preview once export exists.