Close
0%
0%

scpi-mcp — an AI agent that runs my oscilloscope

An MCP server that gives an AI agent real oscilloscope skills — it drove my Rigol through a live -436 V flyback capture, no raw SCPI.

Similar projects worth following
scpi-mcp is a Model Context Protocol server that lets an AI agent operate an oscilloscope rather than just push SCPI strings at one. It exposes goal-level tools (capture this signal, measure that, characterize a channel, arm a single-shot) behind a clean, instrument-agnostic interface, with no raw-SCPI escape hatch and a permission-tier system where the server refuses unsafe actions. To prove it works, I let the agent drive my Rigol DS1104Z through a live bare-coil flyback experiment: it characterized both channels (~24 V DC), chased down switch-mode-supply noise with an FFT, and stepped the vertical scale until it caught the full unclamped kick — a -436 V spike with a +240 V overshoot (~676 Vpp) off a 24 V rail, about 18x the supply, versus a tidy ~24.7 V with a freewheel diode installed. The agent did the tedious instrument-wrangling while I did the wiring and the thinking. Code, mock backend, and the full write-up: https://github.com/armchairdeity/mcp-server-scpi

The idea

Everybody's wiring LLMs to instruments by handing them a raw SCPI socket and hoping for the best — which is like giving someone the scope's keyboard shortcuts and calling them an EE. scpi-mcp takes the opposite approach: it gives an AI agent oscilloscope expertise through goal-level tools — "capture this signal," "measure that," "characterize this channel," "arm a single-shot" — backed by a clean, instrument-agnostic interface. All the vendor SCPI lives in the instrument library; the agent never touches a raw command string. There is deliberately no raw-SCPI escape hatch, plus a permission-tier system (read_only / read_config / full) where the server refuses unsafe actions — the model never decides.

First instrument on the bench: a Rigol DS1054Z (unlocked to the DS1104Z base — 4 channels, 100 MHz), reachable over LAN.

The demo: hunting a bare-coil flyback

The best way to show a tool works is to do real work with it, so I ran a live inductive-kickback experiment and let the agent drive the scope the whole way. The setup: a 24 V relay coil with the freewheel diode removed — a bare inductor. Two 10x probes straddling the disconnect, CH1 on the supply side, CH2 on the coil side. The plan: break the circuit and catch the unclamped kick on a single-shot.

What actually happened is the fun part, because it's exactly the fiddly loop a human hates:

  • Characterized both channels — confirmed ~24 V DC on each, then noticed CH1's trace had "vanished." It hadn't: both channels sat at the same voltage with identical settings, so the traces overlapped pixel-for-pixel. Nudged the display offsets apart and there they both were.
  • Chased down noise — ~6 Vpp of hash on the rail. An FFT showed the energy piling up at the Nyquist edge no matter how fast we sampled: the tell-tale of high-frequency switch-mode-supply ripple aliasing into a slow, fake wander. A 100 nF cap across the supply knocked it down ~5x.
  • Got the disconnect right — pulling from the minus side of the bulk caps took the capacitance out of the loop, so the collapsing field had nothing to absorb it and rang free.
  • Cleared the clipping — the first single-shots railed the window: the kick blew past ±130 V, then −304 V. Step the vertical coarser... 50 V/div, still clipping... 100 V/div — there it is.

The payoff: an unclamped −436 V spike with a +240 V positive overshoot — a ~676 Vpp ring off a 24 V rail, roughly 18× the supply, then an exponential bleed-back over ~1 ms. For contrast, with the freewheel diode installed, the same coil clamps to a tidy ~24.7 V. That's the whole point of the diode in one screen.

Throughout, the agent did the tedious instrument-wrangling — rescale, re-trigger, de-noise, re-arm, grab the screen, dump the waveforms to CSV — while I did the wiring and the thinking. That division of labor is the pitch.

Rigor: a full 4-channel validation

Beyond the flyback, the agent ran a blind 4-channel validation of the scope: characterize each channel to lay down an expected baseline, then take an independent measurement snapshot and grade measured against it — 17/17 pass across frequency, period, Vpp, Vrms and duty (with a widened tolerance on the mains channel, whose line frequency legitimately wanders). The methods/results log and the Expected-vs-Measured spreadsheet are attached under Files.

Skippi

The project has a mascot: Skippi, the artistic gremlin who lives in the scope and paints portraits of the magic pixies — the ones who carry every electron down the probe. When you see a clean trace, that's Skippi's latest masterpiece.

Links

Code, mock backend (zero-hardware), and the full write-up: github.com/armchairdeity/mcp-server-scpi

flyback-ch1-supply-side.csv

Flyback capture, supply-side probe (CH1) - 1200-pt waveform, 200 us/div; holds ~24 V through the break.

Comma-Separated Values - 35.75 kB - 07/02/2026 at 06:24

Download

flyback-ch2-coil-kick.csv

Flyback capture, coil-side probe (CH2) - the unclamped kick: -436 V peak, +240 V overshoot (~676 Vpp), 100 V/div.

Comma-Separated Values - 36.20 kB - 07/02/2026 at 06:24

Download

scpi-mcp-4ch-test-log.md

Running methods/results log from the blind 4-channel scope validation (each SCPI method called + the value returned).

markdown - 3.58 kB - 07/02/2026 at 06:24

Download

scpi-mcp-4ch-validation-results.xlsx

Expected vs Measured spreadsheet from the blind 4-channel validation - side-by-side columns with per-metric pass/fail (17/17).

sheet - 8.60 kB - 07/02/2026 at 06:24

Download

flyback-summary.json

Machine-readable summary of the -436 V flyback capture: instrument ID, trigger config, per-channel scale/offset, and measurements.

JavaScript Object Notation (JSON) - 746.00 bytes - 07/02/2026 at 06:24

Download

  • The -436 V payoff: an unclamped flyback, caught live

    jrypkahauera day ago 0 comments

    The main event: watching a bare relay coil's collapsing field with no freewheel diode to tame it.

    Setup — 24 V coil, diode removed, two 10x probes straddling the disconnect (CH1 supply side, CH2 coil side). The agent armed a single-shot on CH2's falling edge and waited. The first pulls just clipped: the kick blew past ±130 V, then −304 V, railing the capture window each time. Two things cracked it — breaking from the minus side of the bulk caps took the supply capacitance out of the loop so the coil rang free, and then stepping the vertical coarser and re-arming until the whole transient fit at 100 V/div.

    There it was: a −436 V spike with a +240 V overshoot — a ~676 Vpp ring off a 24 V rail, roughly 18× the supply — then an exponential bleed-back over ~1 ms. Drop a freewheel diode back in and the same coil clamps to a tidy ~24.7 V. That's the whole argument for the diode, in one screen.

    Waveform CSVs for both channels and a machine-readable capture summary are attached under Files

    .

  • Chasing a ghost on the 24 V rail

    jrypkahauera day ago 0 comments

    With a real experiment queued up, the 24 V bench supply threw a curveball: ~6 Vpp of hash riding on the DC, plus a slow, cyclical wander that looked like the supply was breathing.

    It wasn't. The agent ran an FFT at several timebases and the energy kept piling up right at the Nyquist edge no matter how fast we sampled — the classic signature of content faster than the sample rate folding back. The "cyclical fluctuation" was an aliasing / moiré artifact: fast switch-mode-supply ripple, undersampled, rendering as a fake slow beat.

    Fix: a single 100 nF cap across the rail dropped the hash about 5×. Two gotchas along the way, both caught by comparing what the instrument reported against what was on the screen — at one point CH1's trace "vanished" (both channels sat at the same voltage with identical settings, so the traces overlapped pixel-for-pixel), and a ground clip that had shaken loose was doubling the noise on CH2.

  • First light: an AI agent characterizes the scope, blind

    jrypkahauera day ago 0 comments

    Before any fancy captures, I wanted to know the agent could actually measure reliably — so I had scpi-mcp run a blind 4-channel validation of the Rigol DS1104Z.

    The method is deliberately self-checking: for each channel the agent runs characterize_signal first, and that reading becomes the Expected baseline; then it takes an independent measurement snapshot as Measured and grades one against the other. Four different signals under test: a mains-derived sine through a step-down transformer, the Rigol's own 1 kHz probe-comp square, a 20 kHz PWM with a ramping duty cycle, and a bare BNC as a noise-floor control.

    Result: 17/17 pass across frequency, period, Vpp, Vrms and duty. The only tolerance I loosened was on the mains channel, whose line frequency legitimately wanders a few tenths of a percent — grading that at ±0.5% was flagging the power grid, not the scope.

    The full methods/results log and the Expected-vs-Measured spreadsheet are attached under Files.

View all 3 project logs

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates