keyscope-mcp
AI-native oscilloscope control. A pure-Python MCP service that lets LLM agents control Keysight EDUX1052G oscilloscopes via a compact DSL.
README
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 tools - TCL-like DSL — Positional args, implicit units (
1V,10ms,1kHz) - Fail-fast by default — Stops on first error;
continue_on_errorfor batch jobs - Side-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
Release Status
- Current stable baseline:
v0.1.0 - Stability 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+
- Keep MCP tool name and input contract stable:
- 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-commands
Quick 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-devices
3. 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 add
When prompted:
- Name:
keyscope - Type:
local - Command:
/root/keyscope-mcp/.venv/bin/python -m keyscope_mcp
Check registration:
opencode mcp list
Smoke 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:
help
Expected: command reference text is returned.
C. Hardware connectivity check (optional)
Use MCP tool `keyscope.scope_exec` with script:
idn
Expected: 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 off
Timebase
time 1ms # 1ms/div
time 10us -5ms main # 10us/div, -5ms offset, main mode
Trigger
trig edge chan1 1.65 pos # Edge trigger on CH1, 1.65V, positive slope
trig auto # Auto trigger mode
trig holdoff 100ns # 100ns holdoff
Acquisition
run # Continuous
sing # Single shot
stop # Stop
acq norm # Normal mode
acq aver 16 # Average 16 samples
Measurements
meas 1 freq vpp # Frequency and Vpp on CH1
meas 1 all # All measurements
meas clear # Clear all
Waveform 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 off
Math / 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 math
Cursors
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 # Disable
Snapshot
save baseline # Save current setup
load baseline # Restore setup
list # List saved snapshots
Screenshot
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 command
Measurement 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 ground
Supported 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 generation
Testing
# 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 50mV
Safety 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 usbtmc
Firmware 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_mcp - Local 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_mcp - No-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_execwithhelp - Optional hardware check:
call
keyscope.scope_execwithidn - Known 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.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。