keyscope-mcp

keyscope-mcp

AI-native oscilloscope control. A pure-Python MCP service that lets LLM agents control Keysight EDUX1052G oscilloscopes via a compact DSL.

Category
Visit Server

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_error for 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+
  • Known limitation (LLM-facing): query syntax is not fully uniform across all commands yet (for example, trig? works while chan 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 measurement
  • digital_si.dsl — Clock signal integrity analysis
  • fft_spectrum.dsl — Harmonic content analysis
  • frequency_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 (chan settings 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 -q python -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_exec with help
  • Optional hardware check: call keyscope.scope_exec with idn
  • Known limits: stdio MCP only, Keysight-focused, no protocol-level CI, .raw is CLI-only expert path.
  • If revisited later: prefer documentation and smoke-test maintenance, not architectural expansion.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured