Rigol Oscilloscope — MCP Server
Let an AI agent read the setup, adjust channels, timebase and trigger, run single acquisitions, take the scope's own measurements, capture calibrated waveforms and grab screenshots on Rigol digital oscilloscopes over SCPI.
| Package | labmcp-rigol-scope |
| Instruments | DS1000Z / MSO1000Z (DS1054Z, DS1074Z, DS1104Z, MSO1104Z, "Plus" models), DS1000Z-E (DS1102Z-E, DS1202Z-E), MSO5000 (MSO5072 … MSO5354), DHO800 / DHO900 (DHO802, DHO804, DHO914, DHO924), DHO1000 / DHO4000 (DHO1072 … DHO4804) |
| Interfaces | USB (USB-TMC), LAN (VXI-11/LXI, raw socket) |
| Protocol | SCPI per Rigol's programming guides: MSO1000Z/DS1000Z PGA19109-1110, DS1000Z-E PGA27100-1110, MSO5000 PGA25104-1110, DHO800/DHO900, DHO1000/DHO4000 PGA34101-1110 |
| Status | 🧪 simulated: tested against a wire-level SCPI simulator (including binary waveform and screenshot blocks), not yet verified on hardware. Report a hardware test |
Try it without hardware
uvx labmcp-rigol-scope --simulate --check
uvx labmcp-rigol-scope --simulate --option sim_model=DHO804 --check # also DS1202Z-E, MSO5074, DHO1204 ...
The simulated scope has a 1 kHz, 0-3 V square wave on CH1 and a 2 kHz, 1 V sine on CH2 (CH3/CH4 grounded). It answers :WAVeform:DATA? and :DISPlay:DATA? with real IEEE 488.2 binary blocks, keeps an SCPI error queue, and its trigger only fires when the level is inside the signal, so you can try single with a bad trigger level.
Connect your oscilloscope
- USB: plug in the USB device port. The server uses PyVISA with the pure-Python
pyvisa-pybackend, which needslibusbon the system (brew install libusb,apt install libusb-1.0-0; on Windows use NI-VISA with?backend=@ivi). Find the resource withpython -m pyvisa infooruvx labmcp ports. - LAN: in the scope's Utility I/O / LAN settings enable DHCP or set a static IP, and note the address.
- Test the connection:
uvx labmcp-rigol-scope --address USB0::0x1AB1::0x04CE::DS1ZA123456789::INSTR --check # USB-TMC uvx labmcp-rigol-scope --address TCPIP0::192.168.1.50::INSTR --check # LAN (VXI-11) uvx labmcp-rigol-scope --address tcp://192.168.1.50:5555 --check # LAN raw socket
--checkprints the*IDN?identity and the detected command family.
Add to your MCP client
Claude Code
claude mcp add scope -- uvx labmcp-rigol-scope --address TCPIP0::192.168.1.50::INSTR
Claude Desktop / Cursor / Windsurf (claude_desktop_config.json, .cursor/mcp.json, …)
{
"mcpServers": {
"scope": {
"command": "uvx",
"args": ["labmcp-rigol-scope", "--address", "TCPIP0::192.168.1.50::INSTR"]
}
}
}
Add --read-only to allow reading settings, measurements, waveforms and screenshots while blocking every tool that changes the scope setup. For other clients, generate the snippet with uvx labmcp config rigol-oscilloscope --address TCPIP0::192.168.1.50::INSTR --client vscode (also cursor, codex, claude-code).
Tools
| Tool | Kind | Description |
|---|---|---|
autoscale |
🎛 control | Run the scope's automatic setup (AUTO key): it picks vertical scales, timebase and trigger for the connected signals. This overwrites the user's current setup - only use it when asked. Needs signals of roughly >20 mVpp and >40 Hz. |
capture_waveform |
👁 read | Capture a channel's waveform, scaled to volts and seconds with the scope's waveform preamble, and return statistics plus a downsampled trace; optionally save all points to CSV. |
force_trigger |
🎛 control | Force one trigger (FORCE key). Only has an effect in NORMal or SINGle sweep while waiting. |
get_command_log |
👁 read | Return the most recent raw commands sent to / replies received from the instrument (newest last). Useful for debugging and for recording what was done. |
get_connection_info |
👁 read | Report which instrument is connected (identity, address, simulated or real), whether the server is read-only, and the active safety limits. Call this first. |
get_device_info |
👁 read | Identify the oscilloscope (model, serial, firmware), the command family used for it, the number of analog channels, and the current sample rate and memory depth. |
get_settings |
👁 read | Read the current vertical settings of every channel (on/off, V/div, offset, coupling, probe ratio, bandwidth limit), the timebase, the trigger (type, sweep, status, edge source/level/slope) and the acquisition sample rate / memory depth. |
measure |
👁 read | Read the scope's automatic measurements for one channel: voltages (Vpp, Vmax, Vmin, Vtop, Vbase, Vamp, Vavg, Vrms, overshoot, preshoot) and timing (period, frequency, rise/fall time, +/- width, +/- duty). Values the scope cannot determine are null. |
reconnect |
🛑 safety | Close and re-open the connection to the instrument (e.g. after it was power cycled or a cable was re-plugged). |
run |
🎛 control | Start continuous acquisition (RUN). |
screenshot |
👁 read | Save a screenshot of the oscilloscope display (PNG; BMP on the MSO5000) and return its path. For PNG screenshots the image is also returned so the model can look at the screen. |
set_channel |
🎛 control | Change a channel's vertical settings; unspecified settings are left alone. The probe ratio is applied first because it changes the valid scale range. Returns the settings the scope actually applied (the scale snaps to 1-2-5 steps). Rejected values are reported from the scope's error queue. |
set_timebase |
🎛 control | Set the main timebase scale and/or offset. Returns the applied values. |
set_trigger |
🎛 control | Configure an edge trigger (source, level, slope) and the sweep mode. Sets the trigger type to EDGE. The level must lie within the source channel's screen range. |
single |
🎛 control | Arm a single acquisition (SINGLE key): the scope triggers once, then stops. With wait_s > 0 it waits until the acquisition is complete (status STOP). If the trigger condition is never met the status stays WAIT - check the trigger level/source or use force_trigger. |
stop |
🎛 control | Stop acquisition (STOP) and freeze the current waveforms, e.g. before reading deep memory. |
get_connection_info, get_command_log and reconnect are built into every LabMCP server. The command log shows every SCPI command sent.
Safety limits
| Limit | Default | Meaning |
|---|---|---|
max_memory_points |
1 200 000 points | Most points read from acquisition memory in one capture (bounds transfer time) |
Override at launch, e.g. --limit max_memory_points=24000000. An oscilloscope does not source energy, so there are no hazard tools; this server does not control the built-in waveform generator some models have.
Example prompts
- "What is on the scope right now? Give me the channel settings, timebase and trigger."
- "Measure the frequency, Vpp and rise time on CH1."
- "Set CH2 to 200 mV/div, AC coupling, trigger on CH2 falling edge at 0 V, then take a single shot and save the full memory record to ~/data/burst.csv."
- "Capture the CH1 waveform from the screen and tell me the duty cycle and whether the trace is clipped."
- "Take a screenshot of the scope so I can put it in my lab notebook."
Notes
- One driver, three command families. Commands common to all guides are used as written (long forms, e.g.
:TRIGger:EDGE:LEVel,:MEASure:ITEM? VPP,CHANnel1,:WAVeform:PREamble?). The differences are taken from each guide: autoscale is:AUToscale(DS1000Z, MSO5000) or:AUToset(DHO); screenshots are:DISPlay:DATA? ON,OFF,PNG(DS1000Z),:DISPlay:DATA? PNG(DHO) or:DISPlay:DATA?→ BMP (MSO5000); screen captures return 1200 points (DS1000Z, 12 divisions) or 1000 (MSO5000/DHO, 10 divisions). The family is detected from*IDN?; unknown Rigol models are refused unless you force--option profile=DS1000Z|MSO5000|DHO. - Waveform scaling always uses
:WAVeform:PREamble?and BYTE format: volts = (byte − YORigin − YREFerence) × YINCrement. WORD format is not used because the DHO guides do not document its byte order. For the 12-bit DHO scopes this means 8-bit screen/memory data. - Deep memory (
mode="memory") follows the documented RAW procedure: stop the scope, then read in batches of 250 000 points with:WAVeform:STARt/STOP(the DS1000Z maximum for BYTE). The MSO5000 and DHO guides document batched reads but not a maximum; the server checks every batch length and fails clearly if a batch comes back short. Not yet verified on MSO5000/DHO hardware. - Captures hold the connection from source selection to the last byte, then re-read the preamble: if V/div or the timebase changed during the read, the capture is refused rather than mis-scaled.
clipped_fractioncounts points at the ADC limits (codes 0/255) in both modes. The returned trace keeps each block's minimum and maximum sample, so narrow glitches survive downsampling.save_path(CSV) and the screenshotsave_path(.png,.bmpon the MSO5000) must be new files: they are checked before anything is read, and existing files are never overwritten. - Measurements return
nullwhen the scope answers 9.9E37 (cannot measure). Duty cycle and overshoot are reported exactly as the scope returns them (the DS1000Z guide defines duty as a ratio). - Setting commands are checked by reading
:SYSTem:ERRor?afterwards, so an out-of-range scale, offset or trigger level is reported instead of silently ignored. The scale snaps to 1-2-5 steps unless fine adjustment (VERNier) is on. - LAN: the programming guides describe LAN access through VISA (VXI-11/LXI,
TCPIP0::<ip>::INSTR). Rigol scopes also accept raw SCPI on TCP port 5555 (tcp://<ip>:5555); that port is widely used but not stated in these guides. - USB additionally needs
libusbforpyvisa-py;pyusbis installed with this package. - Screenshots are returned as MCP image content only for PNG; large BMP screenshots (MSO5000) are saved to disk and the path is returned.
Hardware verification
| Model | Firmware | Interface | Verified by | Date |
|---|---|---|---|---|
| none yet: be the first |
Release files for labmcp-rigol-scope 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| labmcp_rigol_scope-0.1.2.tar.gz | 28.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| labmcp_rigol_scope-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 54.6 kB
Release files / labmcp_rigol_scope-0.1.2.tar.gz
| Download URL | labmcp_rigol_scope-0.1.2.tar.gz |
|---|---|
| Size | 28.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fbcb5dc38796d290d59ceaeb51d5dbfe9dba5053ca4fb46e2809c0421c731a3f
|
|
BLAKE2b-256 checksum How to use checksums |
45883c6611adc50ef736fc9847ee9ea6a327976f639d39f3324f986de464514c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency logRelease files / labmcp_rigol_scope-0.1.2-py3-none-any.whl
| Download URL | labmcp_rigol_scope-0.1.2-py3-none-any.whl |
|---|---|
| Size | 26.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
90541c2b68a945698b93d244672b23d2a7de8c2d89967a95e1f8247c130534f3
|
|
BLAKE2b-256 checksum How to use checksums |
78fdaac3b01ccd400f15707cfb3b9eb1fbbe6c56b385e1785bbd2cdbeba4aa9a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency log