构建TIA Portal MCP服务器
本文是一份实践指南,介绍如何构建一个建立在TIA Openness V21之上的MCP服务器,以便像Claude这样的助手可以通过调用类型良好的工具来列出您的块、从Excel导入标签、导出代码和组织项目。
AI模型价格对比 | AI工具导航 | ONNX模型库 | Vibe Coding教程 | PLC在线仿真器 | Tripo 3D | Meshy AI | ElevenLabs | KlingAI | ArtSpace | Phot.AI | InVideo
在TIA Portal中设计S7程序充满了重复性、定义明确的工作:创建标签表、构建全局DB、从源导入块、将块移动到组中、编译和读取错误。西门子已经通过Openness API暴露了所有这些功能。缺失的是一个干净的方式让AI助手直接驱动该API,而无需您在聊天窗口中复制粘贴代码。
模型上下文协议(MCP)填补了这一空白。本文是一份实践指南,介绍如何构建一个建立在TIA Openness V21之上的MCP服务器,以便像Claude这样的助手可以通过调用类型良好的工具来列出您的块、从Excel导入标签、导出代码和组织项目。我们将介绍该协议、架构、容易让人困惑的V21特定Openness更改、带有示例负载的完整工具目录、安全模型以及如何扩展它。
1、MCP实际上是如何工作的
MCP是Anthropic在2024年底引入的一个开放标准,现在已被AI生态系统广泛采用。其思想很简单:服务器宣传一组工具,客户端(AI应用程序)通过JSON-RPC发现并调用它们。您需要关心三条消息:
- initialize:客户端和服务器就协议版本达成一致并交换能力。
- tools/list:客户端请求可用工具。服务器返回每个工具的名称、人类可读的描述以及其参数的JSON Schema。
- tools/call:客户端通过名称调用工具,并接收结构化结果。
与普通命令行工具的关键区别在于发现和类型化。使用CLI时,您必须已经知道标志。使用MCP时,模型在运行时读取工具列表和模式,并决定调用什么。在本地设置中,传输是stdio:客户端启动服务器进程,它们通过标准输入和输出交换换行符分隔的JSON-RPC消息。这就是我们在这里使用的传输。
2、整体架构
服务器被有意分为两层。困难的部分——与Openness通信——位于.NET命令行引擎中。MCP层是一个轻量级的Python进程,将工具调用转换为引擎调用。
MCP客户端(Claude Desktop / Cursor / 您的代码)
| JSON-RPC over stdio
v
server.py (Python, FastMCP)
| 子进程: ControlByteTiaCli.exe --attach-open --json
v
ControlByteTiaCli.exe (.NET Framework 4.8)
| Openness API
v
TIA Portal V21 (运行中,项目已打开)
为什么是两层而不是一个进程内服务器?两个原因。首先,Openness需要完整的.NET Framework(4.8),这是托管工程逻辑最可靠的地方。其次,将引擎作为独立CLI意味着它是可独立测试和脚本化的,而MCP服务器变成了几十行组装标志并调用的代码。引擎连接到已打开项目的正在运行的TIA实例,执行其工作,并保持您的TIA窗口不变。
3、Openness V21:您必须了解的程序集拆分
如果您为V20或更早版本构建了Openness工具,V21中最重要的更改是结构性的。单一的整体Siemens.Engineering.dll已被拆分为模块化程序集,并移至新的子文件夹:
C:\Program Files\Siemens\Automation\Portal V21\PublicAPI\V21\net48\
Siemens.Engineering.Base.dll (TiaPortal, Project, HW, Compiler)
Siemens.Engineering.Step7.dll (PlcSoftware, Blocks, Tags, Types)
Siemens.Engineering.WinCC.dll (HMI Classic)
Siemens.Engineering.WinCCUnified.dll
Siemens.Engineering.Safety.dll
...
在V20中,您引用一个程序集。在V21中,专注于PLC的工具引用Base加上Step7。命名空间未更改(Siemens.Engineering、Siemens.Engineering.SW、Siemens.Engineering.HW、Siemens.Engineering.Compiler),因此大多数现有代码只需更新引用和运行时解析器路径即可编译。
在项目文件中,您引用两个程序集并关闭Copy Local:
<ItemGroup>
<Reference Include="Siemens.Engineering.Base">
<HintPath>C:\Program Files\Siemens\Automation\Portal V21\PublicAPI\V21\net48\Siemens.Engineering.Base.dll</HintPath>
<Private>false</Private>
<SpecificVersion>false</SpecificVersion>
</Reference>
<Reference Include="Siemens.Engineering.Step7">
<HintPath>C:\Program Files\Siemens\Automation\Portal V21\PublicAPI\V21\net48\Siemens.Engineering.Step7.dll</HintPath>
<Private>false</Private>
<SpecificVersion>false</SpecificVersion>
</Reference>
</ItemGroup>
因为Copy Local关闭,程序集不在可执行文件旁边,所以您必须在运行时解析它们。在使用任何Openness类型之前注册AssemblyResolve处理程序:
AppDomain.CurrentDomain.AssemblyResolve += (_, args) =>
{
var name = new AssemblyName(args.Name).Name;
if (name is null || !name.StartsWith("Siemens.Engineering", StringComparison.OrdinalIgnoreCase))
return null;
var path = Path.Combine(
@"C:\Program Files\Siemens\Automation\Portal V21\PublicAPI\V21\net48",
name + ".dll");
return File.Exists(path) ? Assembly.LoadFrom(path) : null;
};
还有一些V21说明。程序集的公钥令牌已更改,但如果您使用SpecificVersion false按简单名称解析,这不会影响您。注册表根目录移至21.0路径。由于二进制文件在版本之间不兼容,请为每个TIA版本保留单独的构建:V20构建将无法加载V21程序集,反之亦然。
4、引擎及其JSON信封
.NET引擎是一个多模式CLI。每个模式映射到一个操作:从Excel导入标签和DB、从SCL导入块、批量导入SimaticML XML、将块导出为SCL或XML、组织块、列出项目、列出块。对于自动化,它还有一个–json标志。在JSON模式下,所有日志都转到标准错误,标准输出携带单个结构化信封:
{
"ok": true,
"mode": "list-blocks",
"project": "MyMachine",
"plc": "PLC_1",
"dryRun": true,
"result": { "blocks": [ ... ], "types": [ ... ] },
"compile": null,
"error": null
}
错误以相同的形状返回,ok设置为false,error中包含消息,因此调用者永远不必从文本中刮取堆栈跟踪。这个信封使工具结果对模型有用:它读取result.blocks而不是四十行时间戳。
5、Python中的MCP层
服务器使用官方的mcp SDK及其FastMCP辅助程序。单个辅助程序使用–json运行引擎,解析标准输出并返回它。每个工具都是一个小函数,其签名成为客户端看到的JSON Schema。以下是它的形状:
from mcp.server.fastmcp import FastMCP
import subprocess, json, time
mcp = FastMCP("controlbyte-tia-mcp")
def _run(args: list[str]) -> str:
cmd = [EXE] + args + ["--json"]
proc = subprocess.run(cmd, capture_output=True, text=True,
encoding="utf-8", timeout=240)
return (proc.stdout or "").strip()
@mcp.tool()
def list_blocks(plc: str | None = None, project_name: str | None = None) -> str:
"""列出打开项目中PLC的所有块(OB/FB/FC/DB)和UDT。"""
return _run(["--list-blocks", "--dry-run", "--attach-open"])
if __name__ == "__main__":
mcp.run() # stdio传输
文档字符串很重要:它是模型在决定是否调用该工具时读取的描述。类型提示成为参数模式。可选参数使用None作为默认值。
从一开始就值得构建两个健壮性细节。首先,写操作默认为试运行,因此工具仅在调用者明确选择加入时才更改项目。其次,如果应用程序暂时繁忙或显示模式对话框,连接到TIA可能会暂时失败,因此辅助程序会在返回干净的JSON错误之前对"操作超时"或"RPC忙"等错误重试几次。
6、工具目录
该演示暴露了八个工具。只读工具从不修改项目;写工具默认为试运行。
list_projects. 列出正在运行的TIA实例及其中打开的项目。完全只读。
{ "ok": true, "mode": "list-projects",
"result": { "instances": [ { "pid": 12612,
"projects": [ { "name": "MyMachine", "path": "C:\\...\\MyMachine.ap21" } ] } ] } }
list_blocks. 返回PLC的块和UDT树,每个条目包含其组路径、类型和名称。有助于在不熟悉的程序中指导助手。
import_tags_db. 从带有Tags和DB工作表的Excel文件导入PLC标签表和全局DB。参数:excel_path、可选的plc和project_name、dry_run(默认为true)和overwrite_db。试运行报告计数而不写入:
{ "ok": true, "mode": "excel", "dryRun": true,
"result": { "tags": { "TablesCreated": 3, "TagsCreated": 19, "TagsSkipped": 0, "TagsFailed": 0 },
"db": { "Created": 3, "Skipped": 0, "Failed": 0 } } }
import_scl. 从.scl文件作为外部源导入块,并生成FB、FC或DB。
import_xml. 从文件或文件夹批量导入UDT和块的SimaticML XML,具有多遍依赖重试功能,以便被其他类型引用的类型按正确顺序导入。
export_scl. 将块或UDT导出为.scl文件。这仅适用于用SCL或STL编写的块,因为TIA无法从LAD或FBD块生成SCL源。
export_xml. 将块或UDT导出为SimaticML XML。这适用于任何语言,因此是通用导出。
organize_blocks. 按命名约定将块和UDT移动到子组中,例如将库块移动到PackML组,将机器人类型移动到Robot组。
7、连接到Claude Desktop
您构建引擎,为服务器创建一个小型Python环境,并将其注册到客户端。
# 1. 构建引擎
dotnet build .\src\ControlByteTiaCli\ControlByteTiaCli.csproj
# 2. MCP服务器的Python环境
cd .\mcp-server
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install "mcp[cli]"
然后将条目添加到%APPDATA%\Claude\claude_desktop_config.json:
{
"mcpServers": {
"controlbyte-tia-mcp": {
"command": "C:\\path\\to\\mcp-server\\.venv\\Scripts\\python.exe",
"args": ["C:\\path\\to\\mcp-server\\server.py"],
"env": {
"TIA_MCP_EXE": "C:\\path\\to\\src\\ControlByteTiaCli\\bin\\Debug\\ControlByteTiaCli.exe"
}
}
}
}
重启客户端,在TIA Portal V21中打开您的项目,controlbyte-tia-mcp工具就会出现。诸如"列出打开的TIA项目中的块"之类的提示将调用list_blocks,模型将读回结构化树。
8、编写自己的客户端
您不需要桌面应用程序。mcp SDK从您自己的代码连接,列出工具并调用它们:
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
params = StdioServerParameters(command="python", args=["server.py"])
async with stdio_client(params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
print([t.name for t in tools.tools])
result = await session.call_tool("list_projects", {})
print(result.content[0].text)
asyncio.run(main())
9、用于标签和DB导入的Excel合同
import_tags_db期望一个带有两个区分大小写的工作表的.xlsx文件。
Tags工作表按顺序使用以下列:Name、DataType、LogicalAddress、Comment、TagTable。例如:
| Name | DataType | LogicalAddress | Comment | TagTable |
|-------------|----------|----------------|----------------|------------|
| iStart_PB1 | Bool | %I0.0 | 启动按钮 | TT_Inputs |
| qMotor1 | Bool | %Q0.0 | 电机1输出 | TT_Outputs |
| wStep | Int | %MW12 | 步骤编号 | TT_Memory |
DB工作表使用:DbName、MemberName、DataType、InitialValue、Comment、Retain。共享DbName的行进入一个全局DB。如果标签表不存在,则会自动创建它们,已存在的标签将被跳过而不是重复。该工具遵循简单的命名约定:i表示输入,q表示输出,x表示布尔标志,w表示word和int,d表示double word和dint,r表示real,DB_前缀表示数据块,TT_前缀表示标签表。
10、安全模型
从LLM驱动工程工具需要防护措施。该演示使用保守模型:
- 默认试运行。除非调用者将dry_run设置为false,否则每个写工具都会模拟。任何模式的第一次运行都应该是试运行,这样您就可以在它接触项目之前准确地读回将要更改的内容。
- 第一次写入前备份。引擎可以在保存之前将项目文件夹复制到备份目录中。
- 连接,不要接管。服务器连接到已打开的项目并保持打开状态。它永远不会关闭您的TIA会话。
- 临时重试。如果由于TIA暂时繁忙而导致连接调用失败,则会自动重试,只有持续失败才会作为JSON错误返回。
更强的控制是合理的下一步:显式的每次写入确认、专用的故障安全(F)块处理以及工具调用的审计跟踪。将这些视为路线图,而不是今天的内置功能。
11、性能和限制
引擎连接到实时进程,因此大多数调用大约在一两秒内返回。打开新项目或编译大型程序需要更长的时间,这就是为什么引擎允许宽松的超时时间,而MCP辅助程序每次调用使用240秒的时间预算。需要记住两个结构限制:该工具在TIA中已经打开的项目上运行(它不会自行打开离线文件),列出或导出非常大的块的速度受Openness序列化它们的速度限制。
12、故障排除
- TIA必须正在运行且项目已打开。服务器连接到实时进程。如果未找到任何内容,请先启动TIA并打开项目。
- 以相同的Windows用户身份运行。Openness仅查看由同一用户启动的TIA进程。不要将提升权限的TIA与未提升权限的工具配对,反之亦然。
- Openness许可证和组。您需要Openness许可证和本地Siemens TIA Openness组的成员资格,否则API会抛出安全异常。
- FileNotFoundException for Siemens.Engineering.Base。解析器找不到V21 net48文件夹。修复路径;不要将DLL复制到可执行文件旁边。
- InvalidProjectVersionException。通过V21打开的旧项目需要升级。在TIA V21中打开一次,保存,然后使用该工具,或连接到已打开的项目。
- XML导入时的区域设置错误。XML中的注释引用了项目中未配置的语言。导入器会清理区域设置,但如果仍然失败,请检查项目语言。
13、使用您自己的工具扩展它
添加功能遵循两层拆分。如果该操作已作为引擎标志存在,您只需添加一个构建标志并调用辅助程序的Python工具,然后重启客户端。如果确实是新的,请先在.NET引擎中添加标志和逻辑,重新构建,然后在Python中公开它。一个好的首次添加是只读工具,例如以文本形式读取单个块的源代码,或返回编译器消息作为结构化JSON的编译和报告工具。该模式是可移植的:任何具有自动化API的工程系统都可以位于相同的轻量级MCP层之后。
14、路线图
该演示已经返回结构化JSON并重试临时连接错误。自然的下一步是更丰富的工具集(交叉引用、只读块源、编译和报告)、用于远程使用的可选HTTP传输、每次写入确认以及每种模式的结构化结果。这些都不会改变核心思想:一个小型、类型良好的Openness表面,助手可以安全地使用。
15、常见问题
此目标适用于哪个TIA版本?V21及其拆分程序集。对于V20,您需要单独的构建,因为Openness二进制文件在版本之间不兼容。
它适用于离线.ap21文件吗?不。它连接到已打开项目的正在运行的TIA Portal实例。
是否有任何内容发送到云?只有您的MCP客户端发送到其模型的内容。如果您使用托管模型,则通过它传递的工具输入和输出将离开您的计算机;对于隔离网络工作,请使用本地模型。MCP服务器和TIA连接是本地的。
成本如何?服务器和引擎只是Openness之上的代码。真正的先决条件是有效的TIA Portal Openness许可证,西门子单独授权。
原文链接: Building a TIA Portal MCP Server on Openness V21: A Hands-On Guide
汇智网翻译整理,转载请标明出处。