OPC UA MCP 上手实测:AI 读 PLC 数据、浏览节点、写设定值

装好之后,AI 能自己浏览节点树、批量读点位、改设定值——我们记下了完整的配置、真实返回和该守的规矩。

分享
OPC UA MCP 上手实测:AI 读 PLC 数据、浏览节点、写设定值

一句话结论:kukapay/opcua-mcp 和 kukapay/modbus-mcp 这两个 MIT 许可的 MCP Server,可以让你在 Claude Desktop 或 Cursor 里直接用自然语言读 PLC 变量、浏览节点树、改设定值。我们在本地起了 OPC UA 和 Modbus 两台模拟设备,把 11 个工具全部跑通,握手 1.3–1.9 秒、单次调用 0.01–0.03 秒。下面是完整配置、真实返回,以及上线前该守的几条规矩。

披露(先说口径):本文所有数字来自 Windows 10 + Python 3.10.6 的干净 venv,被控对象是本地模拟器(OPC UA 用 asyncua,Modbus 用 pymodbus),没有接真实 PLC。所以本文回答的是"工具怎么装、怎么配、跑起来什么样",不回答"在你那台设备上延迟多少"。


一、这两个 MCP Server 能干什么

能力 opcua-mcp modbus-mcp
读单个变量 read_opcua_node read_register / read_input_registers
批量读 read_multiple_opcua_nodes read_multiple_holding_registers
写单个值 write_opcua_node write_register / write_coil
批量写 write_multiple_opcua_nodes —
浏览地址空间 browse_opcua_node_children —(Modbus 只有寄存器号)

一句话区分:OPC UA 这条路带语义、跨厂商,能看能改;Modbus 这条路快而直接,只认寄存器号。 前者适合"让 AI 看懂现场",后者适合"让 AI 读一块固定的点位表"。

二、安装:三条命令

python -m venv venv
venv/Scripts/pip install "mcp[cli]<2" asyncua "pymodbus<3.10" python-dotenv "opcua>=0.98.13" "cryptography>=45"

关于版本:这两个仓库写于 2025 年,mcp 与 pymodbus 之后都发过不兼容的大版本。所以安装时把上界钉住(mcp<2、pymodbus<3.10),装完就能直接用;如果你习惯用 uv,uv sync 会按仓库自带的锁文件走,同样没问题。

最终实测版本:

包 版本
mcp 1.30.0
pymodbus 3.9.2
asyncua(当模拟器) 2.1.0
opcua(python-opcua,客户端库) 0.98.13

三、起一台模拟设备(10 秒)

OPC UA 侧,用 asyncua 起一台只有 4 个变量的"虚拟产线",方便先跑通再换真设备:

import asyncio
from asyncua import Server, ua

async def main():
    server = Server()
    await server.init()
    server.set_endpoint("opc.tcp://127.0.0.1:4840/freeopcua/server/")
    idx = await server.register_namespace("http://hubwiz.com/demo")
    line1 = await (await server.nodes.objects.add_object(idx, "Demo")).add_object(idx, "Line1")
    await line1.add_variable(idx, "Temperature", 36.5, ua.VariantType.Double)
    sp = await line1.add_variable(idx, "Setpoint", 40.0, ua.VariantType.Double)
    run = await line1.add_variable(idx, "Run", True, ua.VariantType.Boolean)
    for n in (sp, run):
        await n.set_writable()
    async with server:
        await asyncio.sleep(3600)

asyncio.run(main())

跑起来会打印 4 个 node id(本文测试里是 ns=2;i=3~ns=2;i=6)。Modbus 侧同理,用 pymodbus 起一台 TCP 从站(从站号 1、保持寄存器 0–9 = 10–19、输入寄存器 = 100–109)即可,modbus-mcp 的 README 里有完整参数。

四、接到 Claude Desktop / Cursor

{
  "mcpServers": {
    "opcua-mcp": {
      "command": "python",
      "args": ["C:/path/to/opcua-mcp/main.py"],
      "env": { "OPCUA_SERVER_URL": "opc.tcp://127.0.0.1:4840/freeopcua/server/" }
    },
    "modbus-mcp": {
      "command": "python",
      "args": ["C:/path/to/modbus-mcp/src/modbus_mcp/cli.py"],
      "env": { "MODBUS_TYPE": "tcp", "MODBUS_HOST": "127.0.0.1", "MODBUS_PORT": "502" }
    }
  }
}

重启客户端,工具列表里就会出现 read_opcua_node 这一组。之后你要做的只是开口问:

  • "读 ns=2;i=3 的值"
  • "把 ns=2;i=4 设成 42.5,然后读回来确认"
  • "浏览 Objects 下面有哪些节点"
  • "读 ns=2;i=3、i=4、i=5 三个值,告诉我哪个超了 40"

五、实测:5 个 OPC UA 工具的真实返回

阶段 耗时
首次 initialize()(起进程 + 连 OPC UA) 1.25–1.86 s
list_tools 0.01 s
单次读 / 写 / 浏览 0.01–0.03 s

原样摘录:

read_opcua_node(ns=2;i=3)          -> Node ns=2;i=3 value: 36.5
read_multiple_opcua_nodes(3 个)     -> {'ns=2;i=3': 36.5, 'ns=2;i=4': 40.0, 'ns=2;i=5': True}
browse_opcua_node_children(ns=0;i=85) ->
  [{'node_id': 'i=31915', 'browse_name': '0:Locations'},
   {'node_id': 'i=2253', 'browse_name': '0:Server'},
   {'node_id': 'ns=2;i=1', 'browse_name': '2:Demo'}]
write_opcua_node(ns=2;i=4, "42.5") -> Successfully wrote 42.5 to node ns=2;i=4

读回来验证过:Setpoint 从 40.0 变成 42.5,Run 从 True 写成 False 再写回 True,都按预期生效。

三个使用习惯,能让它更好用:

  1. 先 browse,再 read。 工具返回的是给人看的字符串(browse 甚至是 Python repr 风格),先让模型看清有哪些节点、节点 ID 长什么样,再让它读值,比直接丢一个 node id 稳。
  2. 批量读优先。 要一次看十几个点位,用 read_multiple_opcua_nodes 一次拿完,比逐点问快,也让模型的上下文干净。
  3. 写完让它读回来。 工业场景里"写成功"和"值真的变了"是两件事,养成回读习惯,报告里也要求它贴回读值。

一个真实用法:每日巡检

装好之后最先能落地的是"每天早上一份巡检报告"。提示词可以固定成这样:

读 ns=2;i=3(温度)、ns=2;i=4(设定值)、ns=2;i=5(运行状态)三个值, 温度超过设定值 5 度就标红,并列出可能原因;运行状态是 False 时单独提示。

模型会自己决定调一次 read_multiple_opcua_nodes 拿三个值,再按你给的规则判断。相比写一个定时脚本,好处是规则用自然语言改——下周想把阈值从 5 度改成 3 度,改一句话就行,不用改代码、不用重新部署。

六、Modbus 侧:6 个工具,同样一次跑通

调用 返回(原样)
read_register(5) Slave 1, Register 5 Value: 15
write_register(5, 777) Successfully wrote 777 to register 5 on slave 1
read_register(5) Slave 1, Register 5 Value: 777 ✅
read_input_registers(0, count=3) Slave 1, Input Registers 0 to 2: [100, 101, 102]
read_multiple_holding_registers(0, count=4) Slave 1, Holding Registers 0 to 3: [10, 11, 12, 13]
write_coil(2, True) Successfully wrote True to coil 2 on slave 1
read_coils(0, count=4) Slave 1, Coils 0 to 3: [False, False, True, False] ✅

延迟同样是 0.01 s 量级(本机回环)。Modbus 没有地址空间,AI 无法"自己探索有什么",所以用之前把点位表写进提示词(或者做成一张对照表挂在项目里),比让它猜要靠谱得多。

读错了地址时,返回会是一个标准的 Modbus 异常:

Error reading register 999 from slave 1:
ExceptionResponse(dev_id=1, function_code=131, exception_code=2)

exception_code 记住两个就够用:2 = 非法数据地址(点位表对不上),4 = 从站故障 / 网关目标设备无响应(线或从站的问题)。在提示词里把这两句教给模型,它排查时会准确很多。

点位表怎么给模型

Modbus 没有地址空间,模型只能靠你给的对照表。最省事的做法是在项目说明(或 Claude 的 Project、Cursor 的 rules 文件)里放一张表:

地址 类型 含义 单位 可否写
HR 0 uint16 产线速度设定 rpm ✅
HR 5 uint16 当前产量 件 ❌
IR 0 uint16 电机温度 0.1 ℃ ❌
Coil 2 bool 启动允许 — ✅

有了这张表,模型问"HR 5 是多少"时会直接映射到 read_register(5),不会再瞎猜地址。OPC UA 侧虽然能 browse,但把节点 ID 和业务含义对上同样值得写一张表——browse 只能告诉你节点叫什么,告诉不了你它代表什么。

七、多这一层值不值

直说:如果只是自己跑一次脚本,别用 MCP,直接调库更省事。

from opcua import Client
c = Client("opc.tcp://127.0.0.1:4840/freeopcua/server/")
c.connect()
print(c.get_node("ns=2;i=3").get_value())

那 MCP 这层买到了什么?

买到的 具体是什么
给不写代码的人用 工程师在 Claude Desktop / Cursor 里说"读一下 3 号节点的温度",不用打开 IDE
给 agent 自主编排 工具名 + 参数描述就是模型的说明书,它能自己决定先 browse 再 read、还是批量读
跨客户端复用 一份 mcpServers 配置,Claude / Cursor / 自研 agent 都能挂

判断标准很简单:"读产线"会不会变成 agent 的日常动作? 会,这层值得,把下面那几条规矩补上;只是偶尔查一次,写脚本。

八、那什么时候用 OPC UA,什么时候用 Openness?

你想让 AI 做的事 该走哪条路 为什么
看当前值、看趋势、看报警文本 OPC UA 标准协议、跨厂商,变量自带语义与类型;只读时风险最低
一次采集几十个点位做分析 OPC UA(批量读) 一次调用拿一批值,比逐点问快
改一个设定值 / 置位一个线圈 OPC UA 或 Modbus(配白名单) 能做,但要管住范围并回读校验
改程序:建块、写 SCL、编译、下载 Openness(MCP 网关) OPC UA 不碰组态和程序,这件事只有 TIA Portal 的 Openness 接口能做
把老项目的 LAD 迁成 SCL Openness 要读工程结构、生成并编译代码,属于"改工程"而不是"改数据"

一句话:OPC UA 管"看和改数据",Openness 管"改工程"。 两条路不是替代关系,很多现场最后两个都要:运行期用 OPC UA 观测,工程期用 Openness 变更。

这条路上我们自己的产品是 TiaLink:把 TIA Portal 的 Openness 能力包成 MCP 接口,AI 可以建块、写 SCL、编译、下载到 PLCSIM 再读回核对(支持 V19 / V20,不支持 V21)。和本文测的 opcua-mcp 正好互补——一个动数据,一个动工程。只需要"让 AI 看见产线在发生什么",读完本文直接用 opcua-mcp 就够了。

九、加一层写白名单:约 20 行,实测有效

默认情况下这两个 Server 允许写任意节点。上线前最划算的一步改造,是给写操作加一张白名单——改 20 行,收益很大。以 opcua-mcp 为例:

ALLOWED_WRITE = {"ns=2;i=4", "ns=2;i=5"}   # 只允许改设定值和运行位


def _deny(node_id: str) -> str:
    return f"已拒绝:节点 {node_id} 不在写入白名单内(ALLOWED_WRITE)"

单节点写在取 client 之前先判一次:

if node_id not in ALLOWED_WRITE:
    return _deny(node_id)

批量写要先整体校验再动手,否则会出现"前两个写进去、第三个被拒"的半成品状态:

denied = [i.get("node_id") for i in nodes_to_write if i.get("node_id") not in ALLOWED_WRITE]
if denied:
    return f"已拒绝:以下节点不在写入白名单内:{denied}"

我们按这个改法跑了一遍(补丁在 distribution/hubwiz-seo/opcua-mcp-test/main_guard.py),实测结果:

调用 返回
写白名单内的 ns=2;i=4 = 41.5 Successfully wrote 41.5 to node ns=2;i=4 ✅
写白名单外的 ns=2;i=6 = 99 已拒绝:节点 ns=2;i=6 不在写入白名单内(ALLOWED_WRITE) ✅
批量写 [i=4→43.0, i=6→7] 已拒绝:以下节点不在写入白名单内:['ns=2;i=6'],回读 i=4 仍是 41.5 ✅ 没有产生部分写入

同一套思路可以直接搬到 modbus-mcp:把允许写的寄存器号做成集合,写工具开头判一次即可。

十、接进每日巡检(可选)

MCP 客户端本身不负责定时,定时交给系统:每天早上 7:00 让 agent 跑一次上面那段提示词,把结果写进一个文件或发到群里。做法有两种:

  • 有 CLI 的客户端:用 claude -p "(提示词)" --mcp-config mcp.json 之类的一次性调用,套 cron / 计划任务。
  • 自研 agent:用 mcp 官方 Python SDK 起一个 stdio 会话,脚本里 call_tool 就行(本文的测试探针 mcp_probe.py 就是这么写的,可以直接当模板)。

两种都建议把"昨天同一时刻的值"一起读出来做对比,否则单点数值对工程师没有信息量——趋势比快照有用。

十一、上线前的五条规矩

这五条是工业现场的通用做法,换任何一套 AI 接 PLC 的方案都适用:

  1. 只给只读账户:能只读就别给写权限;OPC UA 侧用受限角色,别用匿名。
  2. 节点白名单:把允许写入的 node_id 列成清单,清单外的拒绝。控制位、安全位永远不进清单。
  3. 写后回读:每次写成功都读一次比对,不一致就报错给模型,别让它自己宣布完成。
  4. 审计落盘:谁、什么时候、对哪个节点、写了什么、回读是什么,四行日志。
  5. 先在仿真器上跑:本文的模拟器脚本就够用,别拿真设备当第一次实验对象。

十二、四个常见问题

Q1:能同时接多台 PLC 吗? 可以,方式是一个设备一个 MCP 进程:opcua-mcp 的一个实例读一个 OPCUA_SERVER_URL,所以在 mcpServers 里配两个条目(opcua-line1 / opcua-line2)即可,Claude 会看到两套工具。如果嫌工具名重复,把 main.py 里的 FastMCP("OPCUA-Control") 改成不同名字更清楚。

Q2:中文变量名、结构体、DB 块怎么办? 结构体在 OPC UA 里就是一棵子树:browse 到父节点,再逐个读成员即可,不用特殊处理。中文变量名同理——返回值是字符串,中文能正常显示。建议先在仿真器上把你们真实的节点树导一遍,确认命名空间索引(ns=2 还是 ns=3)和成员层级,再换真设备。

Q3:能不能只订阅变化,不轮询? 这两个工具是请求-响应式的,没有订阅接口。需要"值变了就通知"的场景,用 OPC UA 原生的 subscription(Python 里 create_subscription + subscribe_data_change),或者退一步用定时轮询 + 变化检测。

Q4:安全上要注意什么? 这两个 Server 建的是匿名连接(Client(url) 不设用户/证书)。所以它们的合理位置是内网、仿真环境、或者你信任的工程机;生产网段建议加一层:设备侧启用用户/证书鉴权、网络侧走 VPN 或跳板,再把 MCP 进程限制在固定网段。

十三、来源与许可

  • kukapay/opcua-mcp:MIT,Python,31 stars,最后提交 2025-10-29
  • kukapay/modbus-mcp:MIT,Python,25 stars,最后提交 2025-05-12
  • 协议与规范:OPC Foundation(OPC UA 即 IEC 62541)、Modbus Organization
  • 模拟器:asyncua(OPC UA)、pymodbus(Modbus TCP)
  • 本文所有返回值均为本机原样摘录;未接真实 PLC,如与你的设备行为不同,以设备为准

免责:写操作会真的改变现场状态,上产线前请走完整的变更审批与回滚演练。


想把 AI 从"读数据"推到"改工程",可以看 PLC 编程 AI 软件:7 个必备的 MCP Server 和 博途 AI 助手:让 AI 直接操作 TIA Portal(TiaLink);本专题的其它文章在 AI × 工业 / PLC。
这是「AI × 工业 / PLC」专题的一部分。完整地图见:AI × 工业 / PLC 完全指南