keyscope-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@keyscope-mcpmeasure the frequency of the signal on channel 1"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
keyscope-mcp
AI-native oscilloscope control. A pure-Python MCP service that lets LLM agents control Keysight EDUX1052G oscilloscopes via a compact DSL.
┌─────────────┐ DSL/SCPI ┌──────────────┐ USBTMC ┌──────────┐
│ AI Agent │ ─────────────────→│ keyscope │ ─────────────→│ EDUX1052G│
│ (Claude/…) │←─ waveform/data───│ -mcp │←─ screenshot──│ │
└─────────────┘ └──────────────┘ └──────────┘Features
Single MCP tool (
scope_exec) — ~800 tokens context vs 3000+ for 12 separate toolsTCL-like DSL — Positional args, implicit units (
1V,10ms,1kHz)Fail-fast by default — Stops on first error;
continue_on_errorfor batch jobsSide-effect tagging — ○ none / ◒ soft / ● hard for AI visibility
Three-tier capability — VERIFIED / EXPERIMENTAL / EXCLUDED (security)
Snapshot persistence — Save/load instrument state via LMDB
Thread-safe — Per-device lock for concurrent AI sessions
Related MCP server: SkippyMCP
Release Status
Current stable baseline:
v0.1.0Stability promise for
v0.1.x:Keep MCP tool name and input contract stable:
scope_exec(script, continue_on_error?)Prefer additive output changes (new fields) over breaking field renames/removals
Reserve potentially breaking interface changes for
v0.2.0+
Known limitation (LLM-facing): query syntax is not fully uniform across all commands yet (for example,
trig?works whilechan 1?may be inconsistent depending on parser path)
Installation
pip install keyscope-mcp
# or from source
pip install -e .Local source install (recommended for development)
cd /root/keyscope-mcp
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -U pip
python -m pip install -e .Verify install:
keyscope --list-commands
python -m keyscope_mcp --list-commandsQuick Start
1. MCP Server (default mode)
# Auto-detects USB device
python -m keyscope_mcp
# Or specify address explicitly
python -m keyscope_mcp --address "USB0::10893::923::CN60121247::0::INSTR"2. CLI Modes
# Interactive REPL
keyscope -i
# One-shot command
keyscope -c "idn"
# Execute script file
keyscope examples/power_ripple.dsl
# Dry-run (parse only, no hardware)
keyscope -n -c "rst; chan 1 on 1V dc; time 1ms"
# List all commands
keyscope --list-commands
# List VISA devices
keyscope --list-devices3. Python API
from keyscope_mcp.executor import execute
result = execute("""
chan 1 on 1V dc
time 1ms
trig edge chan1 0.5 pos
wgen on sin 10k 3.3 0
run
meas 1 freq vpp
""")
print(result["results"][-1]["value"])
# → {"freq": 10000.0, "vpp": 3.28}4. OpenCode MCP setup (local)
Use OpenCode interactive MCP setup:
opencode mcp addWhen prompted:
Name:
keyscopeType:
localCommand:
/root/keyscope-mcp/.venv/bin/python -m keyscope_mcp
Check registration:
opencode mcp listSmoke Test (minimal)
A. No-hardware smoke test
PYTHONPATH=. .venv/bin/pytest tests/test_dsl.py -q
python -m keyscope_mcp -n -c "idn; chan 1 on 1V dc; time 1ms; meas 1 vpp"B. MCP tool smoke test in OpenCode
In an OpenCode chat, run:
Use MCP tool `keyscope.scope_exec` with script:
helpExpected: command reference text is returned.
C. Hardware connectivity check (optional)
Use MCP tool `keyscope.scope_exec` with script:
idnExpected: Keysight ID string (for example, Keysight Technologies,EDUX1052G,...).
DSL Quick Reference
Channel
chan 1 on 1V dc # CH1 on, 1V/div, DC coupling
chan 2 on 500mV ac bw20 # CH2 on, 500mV/div, AC, 20MHz BW limit
chan 1 off # Turn offTimebase
time 1ms # 1ms/div
time 10us -5ms main # 10us/div, -5ms offset, main modeTrigger
trig edge chan1 1.65 pos # Edge trigger on CH1, 1.65V, positive slope
trig auto # Auto trigger mode
trig holdoff 100ns # 100ns holdoffAcquisition
run # Continuous
sing # Single shot
stop # Stop
acq norm # Normal mode
acq aver 16 # Average 16 samplesMeasurements
meas 1 freq vpp # Frequency and Vpp on CH1
meas 1 all # All measurements
meas clear # Clear allWaveform Capture
wave 1 10k word # 10k points, WORD format
wave 1 max asc # Max points, ASCII (slow but human-readable)Waveform Generator
wgen on sin 10k 3.3 0 # Sine 10kHz 3.3Vpp 0V offset
wgen on dc 1.65 # DC offset only (no frequency)
wgen on squ 1M 5 0 # Square 1MHz 5Vpp
wgen off # Turn offMath / FFT
math fft 1 10kHz 100kHz hann # FFT of CH1, center 10kHz, span 100kHz
math sub 1 2 # CH1 - CH2 waveform subtraction
math off # Turn off mathCursors
curs on chan1 # Enable cursors on CH1
curs x 0us 50us # Set X cursors
curs y -1V 1V # Set Y cursors
curs? # Read cursor values
curs off # DisableSnapshot
save baseline # Save current setup
load baseline # Restore setup
list # List saved snapshotsScreenshot
shot png # Capture PNG screenshot
shot bmp # Capture BMP (larger, faster)Utility
idn # Query identity
rst # Reset to factory defaults
opc # Wait for operation complete
err # Check error queue
auto # Autoscale
help # Show help
help chan # Help for specific commandMeasurement Sentinel
When a measurement cannot be made (e.g., frequency with no signal), Keysight returns ~9.9e37. keyscope-mcp normalizes this to:
{"freq": null, "freq_invalid": true}This lets AI agents distinguish "no measurement" from "zero" or invalid data.
Examples
See examples/ directory:
power_ripple.dsl— Switching regulator ripple measurementdigital_si.dsl— Clock signal integrity analysisfft_spectrum.dsl— Harmonic content analysisfrequency_sweep.py— Frequency response (Bode plot approximation)
Hardware Setup
Minimal Setup
PC USB ───→ EDUX1052G (USBTMC)
WaveGen OUT ──balun──→ CH1 (10x probe)Dual-Channel Setup (stereo audio)
PC Audio tip (left) ──→ CH1 (1x probe, 100-200mV/div)
PC Audio ring (right) ──→ CH2 (1x probe, 100-200mV/div)
PC Audio sleeve (gnd) ──→ scope groundSupported Hardware
Model | Bandwidth | WaveGen | Verified |
EDUX1052G | 50–200MHz | 100Hz–12MHz | ✓ |
DSOX1102G | 70–100MHz | 100Hz–12MHz | ✓* |
Other InfiniiVision | — | — | Likely* |
*Compatible SCPI command set; may need capability flags for advanced features.
Architecture
keyscope_mcp/
├── __main__.py # CLI entry point (MCP/REPL/script)
├── server.py # MCP server (stdio/sse)
├── dsl.py # DSL lexer, parser, 24-command registry
├── executor.py # Fail-fast script engine
├── scope.py # VISA connection, binary I/O, SCPI errors
├── units.py # Human-readable unit parsing (1V → 1.0, 1ms → 0.001)
├── persist.py # LMDB snapshot save/load with IDN validation
├── repl.py # Interactive REPL
└── help.py # Help text generationTesting
# Unit tests (no hardware required)
PYTHONPATH=. .venv/bin/pytest tests/test_dsl.py -v
# Device integration tests (requires EDUX1052G)
PYTHONPATH=. .venv/bin/pytest tests/test_device.py -v
# Stereo dual-channel device test only
PYTHONPATH=. .venv/bin/pytest tests/test_device.py -k stereo_audio_inputs -vv
# Manual stereo validation script
PYTHONPATH=. .venv/bin/python test_dual_channel.py
# XY oscilloscope music demo
PYTHONPATH=. .venv/bin/python examples/oscilloscope_music_demo.py --scale 50mVSafety note:
Audio output level is manual by design (scripts do not modify system volume).
Start with low OS volume and increase gradually.
Use scope vertical scale (
chansettings or demo--scale) to improve visibility.Avoid headphones/speakers during high-level tuning.
Troubleshooting
No VISA devices found
# Check USB connection
lsusb | grep Keysight
# → Bus 001 Device 002: ID 2a8d:039b Keysight Technologies, Inc.
# Check kernel module
lsmod | grep usbtmc
# If present, may conflict with pyvisa-py backend:
sudo rmmod usbtmcFirmware hang after bad binary data
Physical replug required. Scope may need to re-initialize USBTMC state.
Large waveform truncated
Fixed by read_bytes() with precise length parsing instead of read_raw(). See scope.py:89-120.
License
MIT
Contributing
Bug reports and PRs welcome. See PLAN.md for detailed architecture and SCPI taxonomy.
Archive Handoff
keyscope-mcp is a small support asset for SiliconAIO / Noema / Autopilot, not a platform branch.
Purpose: expose a Keysight oscilloscope as a single local MCP tool,
scope_exec, backed by a compact DSL.Main entry:
python -m keyscope_mcpLocal install:
python3 -m venv .venv && . .venv/bin/activate && python -m pip install -e .OpenCode local MCP command:
/root/keyscope-mcp/.venv/bin/python -m keyscope_mcpNo-hardware smoke test:
PYTHONPATH=. .venv/bin/pytest tests/test_dsl.py -qpython -m keyscope_mcp -n -c "idn; chan 1 on 1V dc; time 1ms; meas 1 vpp"Minimal MCP smoke test in OpenCode: call
keyscope.scope_execwithhelpOptional hardware check: call
keyscope.scope_execwithidnKnown limits: stdio MCP only, Keysight-focused, no protocol-level CI,
.rawis CLI-only expert path.If revisited later: prefer documentation and smoke-test maintenance, not architectural expansion.
This server cannot be deployed
Maintenance
Related MCP Connectors
LLM Orchestration Agent (Mcp)
LLM Orchestration MCP Agent
MCP-Native LLM Orchestration Agent
- UnifAPIOAuthcom.unifapi
Hosted MCP server for live public-data APIs and Skills for AI agents.
Related MCP Servers
- AlicenseAqualityDmaintenanceAn MCP server that enables AI assistants to control Siglent SDS oscilloscopes over a local network using SCPI commands. It allows users to measure signals, configure channel and acquisition settings, and capture waveforms or screenshots through natural language.135 npm7MIT
- AlicenseNot gradedqualityBmaintenanceAn MCP server for controlling Rigol oscilloscopes from an AI assistant. It translates MCP tool calls into SCPI commands over PyVISA.1MIT
- AlicenseAqualityAmaintenanceAn MCP server that enables AI agents to operate an oscilloscope through high-level tools like signal capture and measurement, abstracting vendor-specific SCPI commands.183MIT
- AlicenseNot gradedqualityDmaintenanceAn MCP server that connects AI assistants to the Owon DS1102 oscilloscope, enabling waveform capture, measurement retrieval, and remote control of scope settings.GPL 3.0