Work / tovex-cli-showcase

tovex-cli-showcase: Siemens TIA Portal CLI for AI agents — S7 PLCs, GT Designer3

tovex-cli: command-line control of Siemens TIA Portal and live S7 PLCs, built for AI agents. Public overview; source is private.

Open Source · 2026 · Active

Source on GitHub ↗ · Need one like it? →

How does it work?

Command-line control of Siemens TIA Portal and live S7 PLCs, engineering software that was only ever built for a mouse. Every action is a one-shot command with JSON output, so an AI agent such as Claude Code can create, compile, download and test a PLC program the way an engineer would, and every step can be scripted, logged and repeated.

Source lives in a private repository. This repo is a public overview.

What it covers

tovex-cli-tiaportal drives Siemens TIA Portal V21 and V17, S7-PLCSIM, and S7-1200 and S7-1500 PLCs: projects, hardware, the PLC program, compile, download, online compare, WinCC Comfort/Advanced objects, libraries, any object attribute, the simulator, and live PLC values by address or tag name.

A short session

Commands typed on a Mac. TIA Portal runs on a Windows PC elsewhere, with the PLC on that PC's Ethernet port.

$ tovex-cli-tiaportal portal attach            # use the TIA Portal already open on the PC
$ tovex-cli-tiaportal plc source-import conveyor.scl
$ tovex-cli-tiaportal compile                  # exit 1 on compile errors
$ tovex-cli-tiaportal download                 # PLCSIM by default; a real PLC needs --yes

$ tovex-cli-tiaportal --json live read '"TestData".Sum' TestData.Scaled TestData.Enable MW100
{
  "cpu": "RUN",
  "values": [
    {"address": "\"TestData\".Sum", "value": 1166},
    {"address": "TestData.Scaled", "value": 12.34},
    {"address": "TestData.Enable", "value": false},
    {"address": "MW100", "value": 0}
  ],
  "target": "ssh://user@plant-pc"
}

$ tovex-cli-tiaportal --json live write TestData.A 500
{"error": "writing TestData.A changes a running PLC; pass --yes to confirm", "type": "refused", "target": "ssh://user@plant-pc"}
$ echo $?
2

Built for agents

its own; the state lives on the PC, next to the software.

(compile errors, a failed download), with the full result still in the JSON; 2 invalid input or a refused action; 124 timeout; 255 the PC could not be reached.

pitfalls without reading the source.

How it works

flowchart LR
    subgraph WS["Your machine: macOS, Linux or Windows"]
        AG["AI agent or engineer"] --> CLI["tovex-cli-tiaportal"]
    end
    subgraph PC["Windows engineering PC"]
        SSH["SSH session<br/>(a service, no desktop)"]
        subgraph DESK["The logged-on user's desktop session"]
            TH["TIA helper<br/>C#, compiled on the PC"]
            TIA["TIA Portal<br/>(Openness API)"]
            SIM["S7-PLCSIM"]
        end
    end
    PLC[("S7 PLC")]
    CLI -- "SSH over Tailscale" --> SSH
    SSH -- "port forward" --> TH
    TH --> TIA
    TH -. "UI Automation" .-> SIM
    SSH -- "S7 protocol, tunnelled" --> PLC

Windows keeps the desktop away from SSH. Windows starts SSH sessions in a service session that has no desktop, but a TIA Portal with a window, S7-PLCSIM and UI Automation all live in the logged-on user's desktop. So the CLI keeps a small helper there, started on demand, as that user and without admin rights.

Built on the PC, from source. The helper is C#, compiled on the PC by the .NET compiler that ships with Windows, so nothing has to be installed. It carries a digest of the source it was built from, so host start can tell in one round trip whether it is current, and rebuilds and restarts it only when the source changed.

TIA Portal stays open. The helper holds the Openness connection and the open project between commands. A command costs one request, about 15 ms on the PC, instead of a TIA Portal start of about 12 s.

No request is ever sent twice. Requests travel over a kept-open SSH port forward, and the helper greets every connection before it reads a request. The CLI can therefore tell "never arrived" (safe to retry) from "arrived, then the link dropped" (reported, never retried), so a download or a PLC write cannot run twice by accident.

Live values bypass TIA Portal. The CLI speaks the S7 protocol itself, in pure Python, through the same SSH link to the PLC on the PC's Ethernet port. The PLC needs no route to your machine, and all reads in one command are packed into as few protocol requests as fit. Tag names are looked up in the open TIA project and cached; the values themselves never pass through TIA Portal.

Remote by default. The PC is reached over a private Tailscale network, so the CLI behaves the same at the desk or away from it. One defaults file points the CLI at the PC and the PLC.

Commands

Live values

tovex-cli-tiaportal live read I0.0 QB0 MW100 MD20:real DB1.DBW10
tovex-cli-tiaportal live read '"TestData".Sum' TestData.Scaled MyTag
tovex-cli-tiaportal live write TestData.A 500 --yes

Addresses cover inputs, outputs, memory and data blocks. Names are PLC tags from the tag tables and data-block members, looked up in the project open in TIA Portal; their layouts are cached until the block changes. Types: Bool, Byte, SInt, USInt, Char, Int, UInt, Word, DInt, UDInt, DWord, Real and Time. Values can be written as 42, -5, 16#2A, 2#101, 1.5 or true.

Speed

Measured end to end from the workstation, median of three runs, with the helper attached to an open project:

Most of that is Python starting; the exchange with the PLC takes about 10 ms. The first command after a pause adds about half a second while the SSH connection opens. Before the work in the engineering notes below, a read by name took 5.5 s and a helper command 2.6 s.

Accuracy

write, not a read-modify-write of the whole byte that could undo a bit the PLC changed in between.

scan, the command fails and says so: wrote 999 but read back 466.

Int, 2 for a Bool: refused with exit 2.

is refused as ambiguous, and quoting follows TIA Portal's own rules.

block was changed but not downloaded, those offsets point at other data, so before reading by name the CLI checks that the PLC's copy of the block has the project's length, and refuses with layout-mismatch if it doesn't.

state query, so the CLI reads RUN or STOP from the CPU's status list.

Safety

--yes, because a download may stop it.

or when you say so with --trust-certificate.

Read the full README on GitHub →

What do people ask about it?

Which Siemens software and PLCs does tovex-cli drive?

TIA Portal V21 and V17 through TIA Openness (the Openness API), S7-PLCSIM, and S7-1200 and S7-1500 CPUs: projects, hardware, the PLC program, compile, download, online compare, WinCC Comfort and Advanced HMI objects, libraries, any object attribute, the simulator, and live values by absolute address or tag name. A companion covers Mitsubishi GT Designer3.

Why a command line instead of an MCP server?

One command, one process: the CLI opens and reuses its own connections, every command takes --json and names the machine it ran on, and the exit code means something — 0 done, 1 the software reported a failure with the full result still in the JSON, 2 invalid input or a refused action, 124 a timeout. An agent such as Claude Code, or a shell script, can call it without a long-lived server.

Can an AI agent download to a real PLC by mistake?

No. Download goes to PLCSIM by default; a real PLC needs --yes, and so does any live write that would change a running PLC — otherwise the CLI refuses with exit code 2 and says why.

Does TIA Portal have to run on the agent's machine?

No. TIA Portal stays installed and licensed on its Windows PC, with the PLC on that PC's Ethernet port. The commands are typed on a Mac or anywhere else and reach the PC over SSH (over Tailscale in the reference setup). Live values bypass TIA Portal: the CLI speaks the S7 protocol itself, in pure Python, through the same link.

Is it tested on real equipment?

Yes — unit tests, then end to end against TIA Portal V21 on the PC: 49 and 22 passed, with the simulator suite needing S7-PLCSIM. Python 3.10+ with Click and pytest, C# on .NET Framework 4.8 compiled on the PC, PowerShell 5.1 and UI Automation. The source is in a private repository; the GitHub page is the public overview.

What else did I build?

View all →

GXBridge-MCP

Control Mitsubishi GX Works3 and your PLC with AI — an MCP server for any agent GX Works3 · Industrial Automation

eplanbridge

Drive EPLAN Pro Panel 2022.0.3 from outside the application — and build a 6-axis robot-arm control schematic with it. C# · EPLAN

picw-overview

Replacing a Mitsubishi PLC with a Raspberry Pi 4B as master of a dynamic checkweigher (Thames Side T16 / XT1-SO)

/ Work / {{ project.name }} {{ project.category }}

{{ project.name }}{{ project.headline }}

{{ project.tagline }}

{{ m }}

What is it?

{{ project.description }}

npm i {{ project.npm }}

What does it do?

{{ f.n }}

{{ f.text }}

What is it made of?

{{ t }}

How does it work?

{{ b.s }}

{{ b.s }}

{{ b.s }}

{{ b.s }}
{{ b.s }}
  • {{ it }}

Read the full README on GitHub →

What do people ask about it?

{{ f.q }}

{{ f.a }}

What did I write about it?

What else did I build?

View all →

/ Work

Where did
it go?

That project doesn't exist. Head back to the full list.