关于 Modbus TCP/RTU Device
Modbus TCP/RTU Device 是用于通过以太网或串行连接读取和控制 Modbus 设备的 MCP 服务,面向仪器与设备自动化场景。它通过 YAML 或 JSON 寄存器映射提供带类型、单位和限制的命名点,并支持安全状态操作;目前仅在模拟环境中测试。
主要功能
- 01支持 Modbus TCP、RTU 和 ASCII
- 02用寄存器映射定义类型化测点
- 03按测点限制写入范围
- 04写入后读取并核对结果
- 05审计日志与只读模式
- 06按映射执行安全状态操作
适用场景
- 01读取温控器的工程单位测点
- 02在范围限制内调整控制器设定值
- 03调试串行或 TCP Modbus 连接
- 04请求设备进入映射定义的安全状态
MCP 相关
这是一个 MCP 服务,向 AI Agent 提供 Modbus 连接信息、测点读取与写入等工具。客户端可读取映射、查询命令日志,并在映射定义安全状态时调用相应工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
uvx labmcp-modbusLABMCP_ADDRESSInstrument address, e.g. serial:///dev/ttyUSB0 or tcp://192.168.1.50:5025LABMCP_SIMULATESet to 1 to use the built-in simulator (no hardware)LABMCP_READ_ONLYSet to 1 to disable all state-changing toolsLABMCP_LIMITSSafety limit overrides, e.g. max_temperature_c=80本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
12收录的工具列表 · 更新于 2 周前
此工具无需参数。
详细介绍
Modbus TCP/RTU 设备 — MCP 服务
让 AI Agent 通过以太网(Modbus TCP)或 RS-485/RS-232(Modbus RTU/ASCII)读取和控制 Modbus 设备:PID 温度控制器、循环冷却器、PLC、过程传感器、VFD 和数据记录仪。只需在 寄存器映射(YAML 或 JSON)中描述一次设备。之后,Agent 就可以使用带名称的点,以实际单位进行操作,例如 process_temperature = 37.2 °C 或 setpoint,而无需处理原始寄存器。服务会强制执行映射中的规则:只有 writable 点才能通过名称写入;每个可写数值都必须有 min 和 max,并且在发送任何内容前都会拒绝超出范围的值。映射还可以定义一个 安全状态,即一组点写入操作(关闭加热器、让控制器进入待机状态),供 SAFETY 工具按需执行。
| Package | labmcp-modbus |
| 仪器 | 任意 Modbus 设备:温度控制器(Watlow、Eurotherm、Omega、Autonics 等)、冷却器和循环器、PLC 和远程 I/O、传感器/变送器、VFD、电表、数据记录仪 |
| 接口 | 以太网(Modbus TCP,端口 502)、RS-485 / RS-232(Modbus RTU 或 ASCII)、串口转以太网网关(RTU over TCP) |
| 协议 | MODBUS 应用协议规范 V1.1b3、串行线路上的 MODBUS V1.02、TCP/IP 上的 MODBUS 消息传递实施指南 V1.0b;客户端通过 pymodbus 3.8+ 实现 |
| 状态 | 🧪 模拟:已通过内存模拟控制器和本地 pymodbus 服务端测试,尚未在硬件上验证。报告硬件测试结果 |
与基础的寄存器读写型 Modbus MCP 相比,此服务增加了:
- 带类型的寄存器映射: int16/uint16/int32/uint32/float32/float64/bool、字和字节顺序、
scale和offset、单位、枚举(0 = 待机,1 = 自动)和描述。 - 逐点限值: 可写点需要
min/max或枚举,并且发送前会检查数值。映射点所属地址上的原始写入会被拒绝,因此无法绕过限值。 - 读回验证: 每次
write_point都会读取回数值,并报告是否匹配。 - 安全状态: 在映射中定义的
apply_safe_stateSAFETY 工具,即使处于--read-only模式也能运行。 - 只读模式(
--read-only)、记录每个请求和响应的 审计日志,以及有 上限 的请求大小。
无需硬件即可试用
uvx labmcp-modbus --simulate --check
模拟器是一个通用的单回路 PID 温度控制器。其布局采用随附的示例寄存器映射。该示例仅供说明,并非任何厂商寄存器映射的副本。加热器以全功率运行时的升温速率为 3 °C/s;它会加热热容体,而热容体会逐渐冷却至 22 °C 的室温(τ = 90 s)。PID 回路具有斜坡变化的工作设定点和报警位。对于未实现的地址,模拟器会返回异常 02;对于超出自身范围的数值(例如 500 °C 的设定点),会返回异常 03;对于错误的单元 ID,会返回异常 0B。使用 --simulate --option register_map=my_map.yaml 时,示例布局中没有的点会获得零初始化存储,因此你可以测试自己的映射。
连接设备
- **设备设置:**在设备的通信菜单中启用 Modbus,并记下相关设置。
- **RTU(RS-485):**设置单元(从站)地址(1–247)、波特率、校验位和停止位。服务默认采用 Modbus 串行通信默认值:19200 波特、8 个数据位、偶校验、1 个停止位。许多设备出厂时采用其他设置(例如 9600 8N1);请在地址中填写设备的设置。将 USB-RS485 适配器的 A/B(D−/D+)线路连接到设备;不同厂商对 A/B 标记的定义不一致,因此如果没有响应,请交换线路。连接信号地,并在较长总线的两端各安装一个 120 Ω 终端电阻。
- **TCP:**设置 IP 地址。除非更改,否则端口为 502。许多 TCP 设备会忽略单元 ID。如果你的设备不会忽略,通常应设为 1;TCP 实现指南建议直接寻址的设备使用 255。
- 根据设备的 Modbus 手册编写寄存器映射。复制示例,并参阅下文的寄存器映射。
- 测试连接:
uvx labmcp-modbus --address tcp://192.168.1.20:502 --option register_map=~/maps/oven.yaml --check uvx labmcp-modbus --address "serial:///dev/ttyUSB0?baudrate=9600&parity=N" --option unit_id=3 \ --option register_map=~/maps/controller.yaml --check uvx labmcp-modbus --address "serial://COM4?baudrate=19200&parity=E" --option register_map=C:/maps/chiller.json --check uvx labmcp-modbus --address "tcp://192.168.1.30:4001?framer=rtu" --check # RTU through a transparent gateway--check会建立连接,将映射中的第一个点作为探测点读取,并输出probe: ok: …或错误信息。其他地址参数:stopbits=2、bytesize=7、framer=ascii(Modbus ASCII)、timeout=1、retries=2。
寄存器映射
device: # optional
name: Incubator oven
manufacturer: Example
model: X-100
unit_id: 1 # --option unit_id overrides it
word_order: big # default for 32/64-bit points
byte_order: big
points:
chamber_temperature:
table: input # holding | input | coil | discrete
address: 0 # 0-based protocol address ("30001" in the manual -> input 0)
type: int16 # uint16 | int16 | uint32 | int32 | float32 | float64 | bool
scale: 0.1 # value = raw * scale + offset
unit: °C
description: Chamber temperature
setpoint:
table: holding
address: 0 # "40001" -> holding 0
type: int16
scale: 0.1
unit: °C
writable: true
min: 20 # required for writable numbers: checked before sending
max: 60
mode:
table: holding
address: 1
writable: true
enum: {0: standby, 1: run}
heater_enable:
table: coil
address: 0
writable: true
safe_state: # optional: run in order by apply_safe_state
- {point: heater_enable, value: false}
- {point: mode, value: standby}
| 字段 | 默认值 | 含义 |
|---|---|---|
table | (必填) | holding(FC03/06/16)、input(FC04,只读)、coil(FC01/05)、discrete(FC02,只读) |
address | (必填) | 从 0 开始的协议(PDU)地址。手册编号:4xxxx → holding xxxx − 1,3xxxx → input,1xxxx → discrete,0xxxx → coil |
type | uint16(线圈/离散输入使用 bool) | 32 位类型使用 2 个寄存器,float64 使用 4 个。寄存器中的 bool 表示 0 = false,其他值 = true |
word_order / byte_order | big / big | big/big = ABCD(最高有效寄存器在前)。little/big = CDAB(PLC 中常见)。big/little = BADC。little/little = DCBA |
scale、offset | 1、0 | 工程值 = 原始值 × scale + offset。写入值会按寄存器分辨率取整,并重新检查 |
unit、description | "" | 显示给 Agent |
writable | false | 只有保持寄存器和线圈可以设为可写 |
min、max | 无 | 工程单位范围。可写数值点必须设置此项(除非使用 enum) |
enum | 无 | 整数点的 {number: label}。写入时可使用标签或数字 |
服务启动时会验证映射。未知字段(例如拼错的 maximum)和重复键(第二个 max,或重复定义某个点)都会报错,而不会被静默忽略,因此拼写错误不会导致限制失效。两个可写点不得共用同一个寄存器(写入其中一个会绕过另一个的限制);对可写寄存器提供只读视图则没有问题。如果映射无效,list_points 会显示错误,设备工具也会拒绝运行。服务会以失败关闭方式运行。
添加到 MCP 客户端
Claude Code
claude mcp add oven -- uvx labmcp-modbus --address tcp://192.168.1.20:502 --option register_map=/home/me/maps/oven.yaml
Claude Desktop / Cursor / Windsurf(claude_desktop_config.json、.cursor/mcp.json 等)
{
"mcpServers": {
"controller": {
"command": "uvx",
"args": [
"labmcp-modbus", "--address", "serial:///dev/ttyUSB0?baudrate=9600&parity=N",
"--option", "unit_id=1",
"--option", "register_map=/home/me/maps/controller.yaml",
"--option", "raw_writes=false"
]
}
}
}
实用标志:
--read-only仅允许读取。apply_safe_state仍可使用。--option raw_writes=false会移除write_register、write_registers和write_coil,因此 Agent 只能写入映射允许的内容。完成映射后,建议启用此选项。
工具
| 工具 | 类型 | 描述 |
|---|---|---|
apply_safe_state | 🛑 安全 | 将设备置于寄存器映射定义的安全状态(例如,关闭加热器输出、将控制器切换到待机状态),按顺序写入每个步骤并读取回读值。即使前面的步骤失败,也会尝试执行每个步骤。如果发现任何异常,请立即调用。只有在每个步骤都已写入并读取回读值,且回读值与请求值一致时,all_ok 才为 true;如果设备确认了写入却未执行(例如处于本地模式),该步骤仍不算成功。 |
get_command_log | 👁 读取 | 返回最近发送到仪器的原始命令和从仪器收到的回复(最新的排在最后)。可用于调试和记录已执行的操作。 |
get_connection_info | 👁 读取 | 报告已连接的仪器(标识、地址、模拟或真实)、服务是否为只读,以及当前生效的安全限制。请先调用此工具。 |
list_points | 👁 读取 | 描述已加载的寄存器映射:设备、每个命名点(表、从 0 开始的地址、类型、缩放、单位、是否可写、最小值/最大值、枚举)以及安全状态步骤。无需连接即可使用。 |
read_coils | 👁 读取 | 读取线圈(FC01):单比特输出,例如运行/停止状态或继电器状态。 |
read_discrete_inputs | 👁 读取 | 读取离散输入(FC02):单比特只读状态,例如报警或限位开关。 |
read_points | 👁 读取 | 从寄存器映射读取命名点,并解码、缩放为工程单位(例如 process_temperature = 25.1 °C)。无法读取的点会返回 error,而不会导致整个调用失败。 |
read_registers | 👁 读取 | 读取原始保持寄存器或输入寄存器(无符号 16 位),可选择解码为 int16、32 位或 64 位值。用于探索设备;如果寄存器映射中描述了某个寄存器,优先使用 read_points。 |
reconnect | 🛑 安全 | 关闭并重新打开与仪器的连接(例如,仪器断电重启或重新插拔电缆后)。 |
write_coil | ⚠️ 危险 | 切换一个原始线圈(FC05)。线圈通常会启动或停止设备(加热器、泵、电机);此处不允许写入已映射的线圈(请使用 write_point)。 |
write_point | ⚠️ 危险 | 写入寄存器映射中的命名点(设定值、模式、输出启用状态等)。发送前会检查该点的可写标记、最小值/最大值或枚举,将值转换为原始寄存器,写入后再读取回读值。更改设定值和输出会影响真实设备。如果写入成功但回读失败,read_back_error 会说明原因。 |
write_register | ⚠️ 危险 | 写入一个原始保持寄存器(FC06)。不会应用缩放或限制,因此仅用于寄存器映射未描述的地址(已映射的地址不允许写入,请使用 write_point)。如果寄存器可读,则返回回读值。 |
write_registers | ⚠️ 危险 | 写入连续的原始保持寄存器(FC16),例如写入一个 32 位值的两半。不会应用缩放或限制;已映射的地址不允许写入(请使用 write_point)。 |
get_connection_info、get_command_log 和 reconnect 内置于每个 LabMCP 服务中。只有当寄存器映射定义了 safe_state 时,才会列出 apply_safe_state。
安全限制
此服务没有全局 --limit 值:通用 Modbus 客户端无法知道寄存器的含义。保护措施来自寄存器映射和服务端:
| 保护措施 | 位置 |
|---|---|
writable: false(默认值) | write_point 会拒绝该点 |
每个点的 min / max / enum | write_point 会在发送前拒绝超出范围的值,并在按寄存器分辨率(整数或 float32)舍入后再次检查 |
| 映射地址保护 | write_register(s) / write_coil 会拒绝任何与映射点重叠的地址 |
--option raw_writes=false | 完全移除原始写入工具 |
--read-only | 移除所有写入工具。保留 apply_safe_state |
映射中的 safe_state | apply_safe_state 按顺序写入每个步骤并读回。即使某一步失败,也会尝试所有步骤。只有读回值匹配时,步骤才算 ok,因此设备虽确认但忽略写入(本地/键盘模式)时,会报告这一情况,而不会将其视为可信 |
| 请求边界 | 每个请求最多 1–125 个寄存器(FC03/04)、1–2000 个位(FC01/02)、1–123 个寄存器(FC16),符合规范 |
| 不支持广播 | 串行线路上的单元 ID 0 会被拒绝:它会向所有设备写入,而且设备不会回复广播 |
提示词示例
- “现在的腔体温度和输出功率是多少?请列出每个点及其单位。”
- “以 2 °C/min 的速率将控制器升至 55 °C,切换到自动模式,启用加热器,并在温度与设定值相差不超过 0.5 °C 时告诉我。”
- “将高报警设为 70 °C,然后检查是否有任何报警或传感器故障位处于激活状态。”
- “读取保持寄存器 100–109,并尝试按两种字顺序解码为 float32。我正在映射一台新冷水机。”
- “出了点问题:将控制器置于安全状态,并确认加热器已关闭。”
说明
- 线路上的地址从 0 开始。 手册通常使用从 1 开始的“Modicon”编号(40001 = 保持寄存器 0)。如果每次读取都偏移一个寄存器,说明映射使用了错误的编号约定。
- 32 位值的字顺序没有标准化:Modbus 规范只定义寄存器内的字节顺序。如果浮点数或 32 位值看起来不合理,请尝试
word_order: little。read_registers配合decode_as可帮助你找到正确的顺序。 - 如果浮点点位读到 NaN 或无穷大(许多设备用 NaN 表示“无值”,例如传感器故障),返回结果中的
value会是null,并包含error;read_registers配合decode_as会将其显示为"nan"/"inf"。 write_point对单寄存器点使用 FC06,对多寄存器点使用 FC16,对线圈使用 FC05。少数设备即使写入单个寄存器,也只接受 FC16。对于这些设备,请将该值映射为多寄存器点的一部分,或报告此情况,以便我们添加相应选项。- 有些控制器会将写入的参数保存到非易失性存储器中,而该存储器只能承受有限次数的写入循环。不要让 Agent 在短时间内反复改写设定值。请检查手册是否提供仅写入 RAM 的寄存器或远程设定值寄存器。
- 读取通常没有副作用,但少数设备会在读取时清除计数器或报警。请勿将此类寄存器纳入映射,或在
description中标明。 - Modbus TCP 设备通常只接受少量并发连接。如果连接被拒绝,请关闭其他 SCADA 或日志记录客户端。
- 超时几乎总是意味着单元 ID、波特率、奇偶校验或 A/B 接线错误。异常 02 表示设备未实现该地址。异常 03 表示设备根据自身内部限制拒绝了该值。
- 兼容 pymodbus 3.8 或更高版本(单元参数关键字在 3.10 中从
slave=改为device_id=;两者均受支持)。 - 随附的映射仅为示例。欢迎贡献真实设备映射:请注明地址所依据的手册标题和修订版本。
硬件验证
| 型号 | 固件 | 接口 | 验证者 | 日期 |
|---|---|---|---|---|
| 尚无: 成为首位验证者 |
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。