mcp server
rigol-mcp
MCP server for Rigol DS1000Z/MSO1000Z oscilloscopes over raw SCPI — no VISA, UltraSigma, or drivers. Drive your scope from Claude Code.
Set up
Runs on your computer. Pick your agent and operating system.
Before you start
- uv (`curl -LsSf https://astral.sh/uv/install.sh | sh`)
{
"mcpServers": {
"dvsproductions-rigol-mcp": {
"command": "uvx",
"args": [
"rigol-mcp"
]
}
}
}same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12
Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs
README
synced Oct 17, 2026Rigol DS1054Z MCP server
Drive a Rigol DS1054Z (or any DS1000Z / MSO1000Z series scope) from Claude Code over Ethernet — no UltraSigma, no NI-VISA, no drivers. The scope is an LXI instrument that accepts plain SCPI on a raw TCP socket (port 5555); this server wraps that as MCP tools.
Tools
| Tool | What it does |
|---|---|
scope_info | *IDN? + timebase, sample rate, memory depth, trigger, per-channel state |
get_screenshot | live display as a PNG (Claude can see the trace/menus) |
get_stats | measurements: Vpp, Vmax/min/avg/rms, freq, period, width, duty, rise/fall |
get_waveform | capture a trace (NORM screen ~1200 pts, or RAW deep memory) → stats + CSV |
plot_waveform | capture + render a PNG plot (needs matplotlib) |
run_control | run / stop / single / force / autoscale |
set_channel | per-channel scale / offset / coupling / display / probe |
set_timebase | timebase scale + offset |
set_trigger | edge source / level / slope / sweep |
set_acquire | memory depth |
scpi | raw SCPI escape hatch for anything else |
Setup
Clone the repo, then either install the dependencies directly:
pip install "mcp[cli]" # required
pip install matplotlib # optional, only for plot_waveform
…or install the package itself (adds a rigol-mcp console script):
pip install . # or: pip install ".[plot]" to include matplotlib
Find the scope's IP on the unit: Utility → IO Setting → LAN Conf
(this one is 192.168.178.96). Sanity-check the link with a browser at
http://<scope-ip> — it serves an LXI welcome page.
Register with Claude Code
claude mcp add --env RIGOL_HOST=192.168.178.96 --scope user rigol -- python D:/ryzzenDL/rigol-mcp/rigol_mcp.py
The
--envflag must have another option (here--scope user) between it and the server name, or the CLI mis-parses the name. Ifpythonisn't on PATH, use the fullpython.exepath or-- cmd /c python D:/....
If you pip installed the package, use the console script instead of a path:
claude mcp add --env RIGOL_HOST=192.168.178.96 --scope user rigol -- rigol-mcp
Or add it to a .mcp.json (project scope) — see .mcp.example.json:
{
"mcpServers": {
"rigol": {
"type": "stdio",
"command": "python",
"args": ["D:/ryzzenDL/rigol-mcp/rigol_mcp.py"],
"env": { "RIGOL_HOST": "192.168.178.96" }
}
}
}
Then claude mcp list to confirm it connects, and ask Claude things like
"screenshot the scope", "what's the frequency on channel 1?", or
"capture CH1 deep memory and plot it".
Environment variables
| Var | Default | Meaning |
|---|---|---|
RIGOL_HOST | 192.168.178.96 | scope IP |
RIGOL_PORT | 5555 | raw SCPI socket port |
RIGOL_TIMEOUT | 10 | socket timeout (seconds) |
RIGOL_OUT_DIR | ./captures | where get_waveform writes CSVs |
Notes / gotchas
- RAW captures stop the scope. Internal memory is only readable in STOP state; the deep-memory read also chunks at 250k points (a hard per-read limit) and reassembles — handled for you.
- Asking for the whole multi-megapoint buffer in one shot makes the scope reply "Memory lack in waveform reading!"; always chunk (this server does).
- VXI-11 (
pyvisa/python-vxi11) also works but is slower and flakier on old firmware; the raw socket used here is the robust path. - A single TCP session is opened per tool call and closed after — stateless and resilient. (Port 5555 is single-client, so don't fire scope tools concurrently.)
get_statsmutates the display. On the DS1000Z, querying:MEAS:ITEM? Xalso adds X to the on-screen measurement bar — there is no pure-read variant. Reading stats will reshuffle the scope's displayed measurement list (cosmetic).- Changing a channel's
scalealso moves itsoffset. The scope keeps the trace's division position, so V/div and offset-in-volts track together — passoffsetexplicitly alongsidescaleif you need an exact vertical position. set_acquire AUTOonly reverts to AUTO mode while the scope is running. Sent while stopped it's ignored and:ACQ:MDEP?keeps the last fixed depth.RTIMe/FTIMereturn9.9E37(→null) on ringy/overshooting edges — the scope's built-in rise/fall measurement gives up when the signal crosses the 90% threshold multiple times. Compute 10–90% fromget_waveformsamples instead.- First
plot_waveformcall can take 2–3 minutes on a fresh machine while matplotlib builds its font cache (one-time, looks like a hang). Subsequent calls are fast. To avoid it, pre-warm once:python -c "import matplotlib.pyplot". scpiquery detection: a command is treated as a query if it contains?(queries may carry args after the?, e.g.:MEAS:ITEM? VPP,CHAN1).
Contributing
Issues and PRs welcome — see CONTRIBUTING.md. CI lints with ruff and runs an import smoke test on Python 3.10–3.13 (no scope required).
License
MIT © Valentino Saitz