MHO98 MCP Server
# MHO98 MCP Server
Rigol **MHO98** 示波器的 Model Context Protocol (MCP) 服务器。
基于官方《MHO98 Programming Guide》(PGA45102-1110) 做全量参数化封装,
通过 **PyVISA (raw TCP socket 5555 / USBTMC)** 控制仪器。
- 手册摘要:`docs/NOTES.md`;功能规划:`docs/PLAN.md`;手册原文:`docs/MHO98_ProgrammingGuide_EN.pdf`
- **默认暴露 18 个工具(动作/配置三层结构)**(`MHO98_TOOL_PROFILE=full` 可全量暴露 142 个);手册全部 28 个命令子系统逐命令核对覆盖——**改状态走 `configure(scope, target, params)`,查状态走 `get_config(subsystem, target, full)`,其余是动词**(run/autoscale/measure/capture/screenshot/save/navigate)
- **波形数据只落盘**(CSV/NPY),绝不在 MCP 返回值中内联采样数组,防止撑爆大模型上下文;返回值为统计摘要 + 文件路径,供后续脚本按协议解析
## 快速开始
```bash
cd /Users/andylos/mcp_gen/rigol/mho98
python3 -m venv .venv
.venv/bin/pip install -e . # 或 pip install mcp'>=1.2,<2' pyvisa pyvisa-py numpy
.venv/bin/python -m pytest tests/ # 离线单元测试(mock 仪器)
.venv/bin/python scripts/probe_device.py # 真机全功能自检
```
## 配置(环境变量)
| 变量 | 默认 | 说明 |
|---|---|---|
| `MHO98_RESOURCE` | `TCPIP0::169.254.112.67::5555::SOCKET` | VISA 资源串;USB 形如 `USB0::0x1AB3::0x****::***::INSTR` |
| `MHO98_SOURCE_IP` | 自动检测 | macOS 直连 link-local 网段时绑定本机源地址(en 口 169.254.*) |
| `MHO98_DATA_DIR` | `./data` | 波形 CSV/NPZ、截图、设置文件的保存目录 |
| `MHO98_TOOL_PROFILE` | `lean` | 工具暴露面:`lean`(18 个三层结构,省上下文)/ `full`(142 个全量);旧值 `core` 为弃用别名 |
> **macOS 直连网线注意**:仪器使用 link-local 地址(169.254.x.x)时,系统默认把流量
> 路由到主网卡导致连不上。本服务会自动发现本机 169.254.* 地址并绑定;若仍有问题,
> 可 `sudo route -host add 169.254.112.67 -interface en7` 持久修复。
## 接入 MCP 客户端
ZCode(`.zcode/settings.json`)或 Claude Desktop(`claude_desktop_config.json`):
```json
{
"mcpServers": {
"mho98": {
"command": "/Users/andylos/mcp_gen/rigol/mho98/.venv/bin/python",
"args": ["-m", "mho98_mcp.server"],
"cwd": "/Users/andylos/mcp_gen/rigol/mho98",
"env": {
"MHO98_RESOURCE": "TCPIP0::169.254.112.67::5555::SOCKET",
"MHO98_DATA_DIR": "/Users/andylos/mcp_gen/rigol/mho98/data",
"MHO98_TOOL_PROFILE": "lean"
}
}
}
}
```
## 工具暴露面:lean(默认,18 个)/ full(142 个)
142 个工具的全量 schema 对大模型上下文负担太重。默认 **lean** profile 按
「动作 / 配置」结构轴切分,规则一句话:**改状态走 `configure`,查状态走
`get_config`,其余是动词**(`MHO98_TOOL_PROFILE=full` 切回全量暴露,功能等价)。
### ① 配置写(1 个)
```python
configure(scope, target=None, params=None, extra=None)
```
`scope` 覆盖全部 32 个配置域:channel / timebase / acquire / autoset / display /
trigger / trigger_common / bus / math / reference / awg / awg_modulation /
awg_output / bode / thresholds / statistics / cursor / counter / dvm / measure /
histogram / mask / search / navigate / record / la / system / lan / save / smb /
ieee4888 / datetime。`target` 是二级选择器(触发类型、`"1/CAN"` 式总线+协议、
通道/槽位号、measure 小节名)。示例:
```python
configure("channel", "1", {"scale": 0.05, "coupling": "AC", "probe": 10})
configure("trigger", "CAN", {"baud": 500000, "when": "ID"})
configure("bus", "1/SPI", {"sclk": "CH1", "miso": "CH2"})
configure("awg", "1", {"function": "SINusoid", "frequency": 10e3})
configure("awg_output", "1", {"state": True, "sync_phase": True})
```
`params` 的键按底层包装函数的签名校验:拼错键名报 ValueError 并**列出全部
合法键**——错误信息即完整参数文档。
### ② 配置读(1 个)
`get_config(subsystem, target, full)` ——32 个子系统读回合一(通道/时基/任意
触发类型/总线/数学/AWG/录制/LA/LAN/SMB/IEEE488.2/选件等;选件查询带挂死守卫,
默认跳过)。
### ③ 动作(16 个)
| 类别 | 工具 |
|---|---|
| 会话/状态 | `connect` `disconnect` `list_resources` `get_idn` `get_status` `reset` `wait_complete` `scpi`(任意 SCPI 逃生舱) |
| 采集动作 | `run`(RUN/STOP/SINGLE/FORCE/CLEAR) `autoscale` `capture`(波形落盘 CSV/NPY+统计,绝不内联) `measure`(44 项,单项/批量/统计值) `screenshot` |
| 存储/导航 | `save_on_instrument(kind, path)` `load_on_instrument(kind, path)` `navigate_to_event` |
长尾/组合参数仍可通过 `configure(..., extra={"KEYWORD": value})` 白名单透传到
`:子系统:KEYWORD value`(白名单按手册逐命令核对,非法关键字报 ValueError)。
## 已验证固件特性(00.01.00,实机 MHO9A274501356)
- `:SYSTem:OPTion:STATus?` / `:OPTion:VALid?` 会挂死(不响应),`get_system_info` 已跳过;`get_option_status` 默认跳过,仅 `opt_in=True` 才真正查询
- `:DVM:CURRent?` 在 DVM 关闭时挂死,`read_dvm` 已做使能守卫
- `:CURSor:MANual:TYPE` 合法值为 `TIME|AMPLitude`(非 X/Y)
- `:TRIGger:PATTern:PATTern` 需要 `H,H,L,L` 逗号分隔(工具自动转换 `HHLL`)
- `:DISPlay:GRADing:TIME` 只接受 `MIN|数值秒|INFinite`(不接受 `1S`/`1.0`)
- `:NAVigate:MODE` 需要 STOP 状态(`configure_navigate(stop_first=True)`)
- SCPI 错误入队有延迟,写后错误检查带 50 ms 稳定期防止误归属
## 手册逐条核对带来的修正(v0.1 → v0.2)
- `:CHANnel<n>:POSition` 是偏置电压(V)而非垂直格数
- 测量阈值命令为 `:MEASure:SETup:MAX/MID/MIN`(非 `:MEASure:THReshold:*`);统计查询必带类型参数 `:MEASure:STATistic:ITEM? <type>,<item>`
- PATTern/DURation 触发的 `LEVel` 需 `<源>,<电平>` 二元形式;`holdoff` 下限 8 ns
- 此型号无 `:MATH<n>:DATA?`,数学波形经 `:WAVeform:SOURce MATH<n>` 读取;REF 插槽为 1–10;FFT 窗函数集为 {RECTangle|BLACkman|HANNing|HAMMing|FLATtop|TRIangle}
- AWG 调制仅支持 AM/FM/PM(无 SWP/Burst);AM 深度 0–120%;频率 2 mHz–100 MHz
- Bode:`sweep_type` 合法值 `LOG|LINE`;`:BODeplot:VOLTage` 需 `<量程>,<幅值>` 双参数
- LA 阈值按 POD 组设置(POD1=D0–D7,POD2=D8–D15),非逐通道
- 录制帧间隔命令为 `:RECord:WRECord:FINTerval`(非 INTerval);搜索类型仅 EDGE|PULSe
- `power_on` 合法值 `LATest`;语言设置用 13 种长格式
- `:SAVE:IMAGe:DATA?` 截图与 `:SAVE:*`/`:LOAD:*` U 盘路径、SMB 共享均已封装
## 数据文件格式
- 波形 CSV:两行 `#` 注释头(含 preamble 换算参数)+ `t_s,v_V` 两列
- 波形 NPY:`np.load(f)["t_s"]` / `["v_V"]`(npz,RAW 大采集推荐)
- 电压换算:`v = (code - yorigin - yreference) × yincrement`
TDQS
Scored across 142 tools
There are many overlapping families: get_channel vs get_channel_full, get_timebase vs get_timebase_full, get_math_config vs get_math_config_full, configure_bus vs configure_bus_rs232/iic/spi/etc., set_counter vs set_measure_counter, and multiple screenshot/save-image tools. Descriptions often clarify differences, but the set has several tool pairs whose boundaries an agent could easily misselect among.
Mostly consistent snake_case verb_noun naming (get_, set_, configure_, measure_, save_, load_, read_) is used throughout. Minor deviations exist, such as bare verbs reset/autoscale, noun-like scpi, and get_screenshot vs get_screenshot_data, but the overall convention is readable and predictable.
142 tools is an extreme mismatch for an MCP server, far beyond the typical 3-15 range. The surface is effectively a full SCPI wrapper, creating excessive cognitive load and making tool selection impractical for an agent despite the exhaustive coverage.
The server covers nearly every major oscilloscope subsystem: system, LAN, save/load, triggers, channels, timebase, acquisition, measurements, cursors, counter/DVM, waveform, math, reference, bus decode, AWG, Bode, display, histogram, mask, search, record, logic analyzer, and connection management. The scpi escape hatch also prevents dead ends for undocumented or unexposed operations.