Skip to main content
Glama
PXE200 SA MCP
by apiwat59

pxe200-sa-mcp

MCP server for the Harogic PXE200 spectrum analyzer. It drives SAStudio4 on the PXE through an SCPI bridge plugin (libScpiBridge.so, TCP 5025) that runs inside SAStudio4.

Default PXE IP: 10.0.0.223 (then 10.0.0.211 as a fallback), port 5025. If your PXE has a different address, set PXE_HOST, e.g. PXE_HOST=192.168.1.50 (several addresses can be given, comma separated).

Requirements

  • Python 3.10+ and uv (for uvx) on the computer running the AI harness

  • The PXE200 on the same LAN, with the bridge plugin installed in SAStudio4 (see Bridge plugin)

Related MCP server: Instrument MCP Server

Install

Claude Code:

claude mcp add --scope user pxe200-sa -e PXE_HOST=10.0.0.223 -- uvx --from git+https://github.com/apiwat59/pxe200-sa-mcp pxe200-sa-mcp

Any other MCP harness (Claude Desktop, Cursor, Windsurf, VS Code, Cline, ...) — add to its MCP config JSON:

{
  "mcpServers": {
    "pxe200-sa": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/apiwat59/pxe200-sa-mcp", "pxe200-sa-mcp"],
      "env": { "PXE_HOST": "10.0.0.223" }
    }
  }
}

Codex CLI (~/.codex/config.toml):

[mcp_servers.pxe200-sa]
command = "uvx"
args = ["--from", "git+https://github.com/apiwat59/pxe200-sa-mcp", "pxe200-sa-mcp"]
env = { PXE_HOST = "10.0.0.223" }

Without uv: pip install git+https://github.com/apiwat59/pxe200-sa-mcp, then use pxe200-sa-mcp as the command.

Environment

Variable

Default

Meaning

PXE_HOST

10.0.0.223,10.0.0.211

Bridge address(es), comma separated, tried in order

PXE_REPORT_DIR

~/pxe200-sa-reports

Where reports, screenshots and CSV go

ภาษาไทย (สั้น ๆ)

  • IP เริ่มต้นของ PXE คือ 10.0.0.223 (ถ้าไม่ได้จะลอง 10.0.0.211) พอร์ต 5025 — ถ้าเครื่องของคุณใช้ IP อื่น ให้ตั้ง PXE_HOST

  • ต้องติดตั้ง uv ก่อน แล้วใช้คำสั่ง claude mcp add ... ด้านบน หรือคัดลอก JSON ไปใส่ในไฟล์ตั้งค่า MCP ของโปรแกรมที่ใช้

  • ต้องติดตั้ง bridge plugin ใน SAStudio4 บนเครื่อง PXE ก่อน (หัวข้อ Bridge plugin)

Bridge plugin

bridge/src/bridge_plugin.cpp is a SAStudio4 plugin that opens an SCPI server on TCP 5025 and drives the running GUI. Build it on the PXE (Qt 5 dev headers, pkg-config, g++):

sh bridge/build.sh          # -> bridge/libScpiBridge.so ; set SA4=<SAStudio4 folder> if not the default

Copy libScpiBridge.so into <SAStudio4>/plugin/ and restart SAStudio4. bridge/deploy.sh does the rebuild + restart on our unit (it expects the sources in ~/sa4dev/scpi; adjust paths for yours). Tested with SAStudio4 4.3.55.35. iplugin.h / propertysystem.h are minimal declarations reconstructed from SAStudio4's exported libraries; no vendor code is included.

Quick check from a PC: python scpi_client.py 10.0.0.223 "*IDN?"

Tools

  • Generic, any control: sa_controls (catalog search), sa_menu (open a menu, live values), sa_get, sa_set, sa_press, sa_dialog, sa_app (File/Mode/System menus), sa_file (file dialogs), sa_read (labels, tables, plots, windows, device, widget tree), sa_gui / sa_scpi (raw).

  • Measurement: sa_configure (mode-aware), sa_trace, sa_sweep, sa_trace_data (any graph; DET in seconds), sa_peak, sa_channel_power, sa_channel_table, sa_measure (CP/OBW/ACPR), sa_results (all results of the current mode), sa_marker, sa_peak_table, sa_view (dock views).

  • Files and state: sa_files (list/download exports, images, states), sa_state (save/list/recall), sa_info (versions, temperature, USB, battery, menu states), sa_screenshot.

  • Digital Demod (needs a working lib/libDigitalSigDemod.so in SAStudio4, not part of this repo): dd_configure opens the mode and sets modulation, symbol rate, center, filter alpha, averaging and reference level, then waits until two reads agree. dd_results gives the results as numbers (EVM / phase / magnitude / frequency error / SNR for PSK and QAM, FSK error / deviation / offset for FSK2, FSK4 and GMSK); repeat=n adds mean, median, min and max. dd_constellation saves the constellation, the ideal points and the eye as CSV plus a PNG. All three can add items to the report.

  • Report: report_start, report_add_note, report_edit_item, report_set_info, report_status, report_list, report_open, report_export.

Safety

  • DANGER (refused unless confirm=true after the user agreed): signal-generator RF output and sweep (AUXS page, hidden on this unit), RxPort, reference clock source, calibration / antenna-factor table Apply/Clear/Delete/Import, Screen Lock, Date/Time, Power On State, Setting Mode, Fan/Display/Device.

  • Blocked: File/Exit (would kill SAStudio4 and the bridge).

  • Mode-switch prompts ("Switch to phase noise mode?") are answered Cancel automatically and reported.

  • MMEM:* reads only below /media/rpdzkj/userdata.

  • The bridge port has no authentication: keep the PXE on a trusted LAN.

Values

Numbers are entered on SAStudio4's own keypad with the unit given ('30kHz', '250ms', '-3dB', '2GB'); Hz and seconds are scaled to whatever unit buttons the keypad has. Choices take the option text (sa_controls lists them), toggles take On/Off.

Absolute power: the rule (learned the hard way)

A jammer test once reported 184 W for a 25 W module: PosPeak + Max Hold read noise-like signals (jammers, OFDM, multi-tone) ~10 dB too high. Now enforced by the tools:

  • sa_total_power measures RMS + Average, cross-checks against the peak detector (>3 dB apart = noise-like) and restores the detector/trace. Use it for every watt / dBm-total figure.

  • sa_channel_power, sa_channel_table, sa_trace_data attach power_basis (detector, trace type) and a warnings list (peak detector / Max Hold, ADC overflow, mismatch with rating). A report item with warnings is marked FAIL. Max Hold is for edges, flatness and hopping occupancy.

  • report_start(rated_power_w=...): every power result is compared with the DUT rating (+-3 dB).

  • sa_configure(ref_level_dbm=...) is the level AT THE INPUT; the tool adds the offset for the screen value. (Before, ref 20 with offset 40 put the input range at -20 dBm: silent ADC overflow, power read 40 dB low.)

Digital Demod notes

  • The symbol rate must match the signal. A wrong rate or modulation still gives steady numbers (FSK4 read 7.5 % at 60 ksym/s instead of 55.56, QPSK on a QAM16 signal read 45 % EVM). dd_results warns above 15 %.

  • Right after a change the screen can show results from the old setting for a second or two. dd_configure waits for that; take figures with dd_results(repeat=10) and quote the median.

  • FilterType is greyed out in SAStudio4 4.3.55.35 and stays RRC; FilterAlpha can be set.

  • SAStudio4 lists QAM32 as "QAM34"; the tools accept QAM32. PSK8 also accepts 8PSK.

  • The bridge reports this mode as INST? UNKNOWN; the tools detect it by the constellation graph.

  • Reading eye traces costs about 0.1 s each. dd_constellation reads 100 by default, the screen holds up to about 400.

Available Tools

42 tools
dd_configureDd ConfigureB

Open SAStudio4's Digital Demod mode (if not open) and set modulation, symbol rate, center, filter, averaging and reference level, with read-back. Blank arguments are left unchanged.

Inputs: center, mod_type, avg_number, filter_type, +4 more. Returns: object

dd_constellationDd ConstellationB

Read the measured constellation points (plus the ideal reference points and the eye traces) from the Digital Demod screen, save them as CSV and a PNG plot, and return the plot.

Inputs: eye, note, label, return_image, +2 more.

dd_resultsDd ResultsB

Digital Demod results as numbers. PSK/QAM: EVM rms/peak %, phase error deg, magnitude error %, frequency error Hz, SNR(MER) dB. FSK/GMSK: FSK error %, magnitude error %, carrier offset Hz, deviation Hz. ASK: ASK error %, carrier offset Hz. Use repeat > 1 for a stable figure.

Inputs: note, label, repeat, interval_s, +2 more. Returns: object

report_add_noteReport Add NoteC

Add a free-text section (setup, observation, conclusion) to the report.

Inputs: text (required), result, heading. Returns: result

report_edit_itemReport Edit ItemC

Edit label/note/result/limit of a report item, or delete it.

Inputs: index (required), note, label, limit, +2 more. Returns: object

report_exportReport ExportB

Generate the report document(s) from the active session.

Inputs: fmt. Returns: object

report_listReport ListB

List report folders.

Inputs: no named parameters. Returns: result

report_openReport OpenB

Resume an existing report session.

Inputs: folder (required). Returns: object

report_set_infoReport Set InfoC

Update report header fields / overall conclusion.

Inputs: dut, setup, title, project, +3 more. Returns: object

report_startReport StartB

Start a new report session. Later screenshots / traces / measurements are saved into its folder. Pass rated_power_w whenever the DUT's power rating is known.

Inputs: title (required), dut, setup, project, +3 more. Returns: object

report_statusReport StatusB

Show the active report and its items.

Inputs: no named parameters. Returns: object

sa_appSa AppB

Application menu (File / Mode / System). File/Exit is blocked; Power On State, Setting Mode, Fan/Display/Device need confirm=true.

Inputs: path, confirm. Returns: object

sa_channel_powerSa Channel PowerB

Channel power integrated over [center +- bandwidth/2] from the displayed trace (same method as SAStudio4's Channel Power, works on Max Hold and any span).

Inputs: center (required), bandwidth (required), note, label, +2 more. Returns: object

sa_channel_tableSa Channel TableB

Channel power and peak for a regular channel plan, from one trace read (use a Max Hold trace for hopping transmitters). Returns per-channel dBm/W plus min/max/spread.

Inputs: count (required), spacing (required), bandwidth (required), first_center (required), +4 more. Returns: object

sa_configureSa ConfigureA

Set up the analyzer (any mode). Only the given parameters change; returns the resulting settings. In HARM / PN / IQS / DET the mode's own controls are used (HARM center/span/level, PN carrier, IQS/DET analysis bandwidth for span). ref_level_dbm is the level at the input: with an external attenuator of 40 dB and a +40 dB offset, ref_level_dbm=20 shows 60 dBm on screen. The offset is applied first, then the reference level (so the input range never drops below the one you asked for).

Inputs: rbw, vbw, span, stop, +7 more. Returns: object

sa_connectSa ConnectB

Connect to the SCPI bridge inside SAStudio4 on the PXE200.

Inputs: host. Returns: object

sa_controlsSa ControlsA

Catalog of every control of a mode (from the survey; no instrument access): name, caption, kind (number/enum/toggle/action/file/...), options, where it lives and its risk class. Use the names with sa_get / sa_set / sa_press. Dialog controls are listed under 'dialogs'.

Inputs: menu, mode, search, include_generator. Returns: object

sa_dialogSa DialogA

See open dialogs / message boxes (text and buttons) and answer or close them.

Inputs: action, button, window. Returns: object

sa_fileSa FileA

Drive an open SAStudio4 file dialog (after sa_app 'File/Save State' / 'File/Recall State', record path, replay file, export): change folder, type a name or pick a file, confirm. Returns the folder listing.

Inputs: name, select, confirm, directory. Returns: object

sa_filesSa FilesB

List or download files SAStudio4 wrote (DataExport CSV/profile, saved images, state .xml, records).

Inputs: path, fetch. Returns: object

sa_getSa GetB

Read any control: navigates to it and returns value, options, caption, kind.

Inputs: name (required), menu, mode, panel, +1 more. Returns: object

sa_guiSa GuiA

Generic access to any SAStudio4 control by name, for settings without a dedicated tool. Menu controls are only reachable while their right-hand menu page is shown: open it with action=menu.

Inputs: action (required), value, target. Returns: object

sa_infoSa InfoB

Instrument info: model, UID, firmware/FPGA/MCU/API/GUI versions, temperature, USB link and rate, battery, clock, current mode and the checked items of the title menus (theme, power-on state...).

Inputs: gnss. Returns: object

sa_markerSa MarkerC

Markers on the current graph (any mode): enable, place, peak search, next peaks, marker->center, noise marker, delta, peak tracking, signal track. Returns the marker table (all markers with x/y).

Inputs: trace, value, action, marker, +2 more. Returns: object

sa_measureSa MeasureA

Run one of SAStudio4's own measurements (Meas menu) and read its result. Note: entering Channel Power can change span/RBW to the measurement's own settings.

Inputs: kind (required), note, label, obw_xdb, +4 more. Returns: object

sa_menuSa MenuB

Open a right-hand menu and return its live controls (values, options, risk).

Inputs: menu (required), mode, page, screenshot.

sa_modeSa ModeC

Switch the SAStudio4 measurement mode.

Inputs: mode (required). Returns: object

sa_peakSa PeakC

Highest point of the trace (optionally within a range).

Inputs: stop, start, trace, show_marker. Returns: object

sa_peak_tableSa Peak TableB

Turn on SAStudio4's Peak Table (10 strongest peaks) and read it (n, frequency, level).

Inputs: show. Returns: object

sa_pressSa PressB

Press any SAStudio4 button (momentary actions, menu buttons). Reports dialogs it opened.

Inputs: name (required), menu, mode, panel, +4 more. Returns: object

sa_readSa ReadA

Raw readouts of what is on screen: labels (result values, annotations), tables (peak/harmonic/SEM/ mapping/pulse tables), plots (graphs + trace point counts), windows, device info, widget tree.

Inputs: what, scope. Returns: object

sa_resultsSa ResultsC

All results SAStudio4 currently shows, parsed: Channel Power / ACPR (all pairs) / OBW / IM3 / SEM table + verdict (SA), AM/FM demod metrics and pulse statistics (IQS/DET), phase-noise table (PN), harmonic table + THD (HARM), mapping results + table (MAP), graph annotations, markers, notices (e.g. 'Carrier Loss!'), overflow.

Inputs: mode, note, label, add_to_report. Returns: object

sa_scpiSa ScpiB

Send a raw command to the bridge (queries end with '?'). Supported: *IDN? *OPC? *WAI SYST:ERR? FREQ:CENT|STAR|STOP|SPAN, BAND[:VID], DISP:WIND:TRAC:Y:RLEV, INIT[:CONT], SWE:WAIT? n, SWE:COUN?, CALC:MARK:MAX, CALC:MARK[n]:X?|Y?, INST , TRAC:DATA? n, TRAC:DATA:X? n, TRAC:CAT?, CONF:CHP|OBW|ACP|OFF, FETC:CHP?|OBW?|ACP?, CHP:BWID|FREQ, OBW:XDB, CALC:CHP? c,bw[,n], GUI:LIST?|CLICK|SET|SELECT|OPTIONS?|VALUE?|TEXT, DEBUG:TAP , DEBUG:LAB?, DEBUG:BUTT?.

Inputs: command (required), timeout_s. Returns: object

sa_screenshotSa ScreenshotC

Capture the SAStudio4 screen as PNG (saved to the report folder, with the current settings).

Inputs: note, label, return_image.

sa_setSa SetA

Set any SAStudio4 value control (number / choice / on-off) the way a user would, with read-back. Confirmation pop-ups that would switch mode are cancelled and reported.

Inputs: name (required), value (required), menu, mode, +3 more. Returns: object

sa_stateSa StateC

Save / list / recall complete SAStudio4 setups (File > Save State / Recall State).

Inputs: name, action, confirm. Returns: object

sa_statusSa StatusC

Current mode, frequency range, RBW/VBW, reference level, offset and ADC overflow flag.

Inputs: no named parameters. Returns: object

sa_sweepSa SweepC

Run control. single = stop after one fresh sweep and return when it is complete.

Inputs: action, sweeps. Returns: object

sa_total_powerSa Total PowerA

Absolute power of a band, measured the right way: RMS detector + Average trace, cross-checked against the peak detector. If peak and RMS differ by more than 3 dB the signal is noise-like (jammer, OFDM, multi-tone): only the RMS figure is valid. Restores the detector and trace type. Checks the result against the DUT rating and returns warnings when it does not match. Set the span so the whole signal is inside it, and the offset (attenuator) before calling.

Inputs: center (required), bandwidth (required), note, label, +3 more. Returns: object

sa_traceSa TraceB

Set a trace's type (Max Hold for hopping/bursty signals) and optionally accumulate for hold_s seconds.

Inputs: trace, hold_s, restart, trace_type. Returns: object

sa_trace_dataSa Trace DataB

Read a displayed trace of any graph (values include amp offset). Spectrum graphs: peak, band power, occupied ranges. Time graphs (DET PvT, IQS IQ): X in seconds. Saves CSV + plot.

Inputs: note, plot, save, stop, +5 more. Returns: object

sa_viewSa ViewC

List / show / hide the dock views of the current mode.

Inputs: on, name. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.1/5.0

    Scored across 42 tools

    Disambiguation3/5

    Several generic-access tools overlap substantially: sa_scpi (raw SCPI), sa_gui (control by name), and the sa_get/sa_set/sa_press trio (generic control access) all offer ways to read/write arbitrary settings, and sa_configure overlaps with sa_set. The power-measurement cluster (sa_measure, sa_channel_power, sa_total_power, sa_channel_table) and the read cluster (sa_trace_data, sa_read, sa_results) also require careful reading to distinguish. The detailed descriptions do help, but the boundaries are genuinely blurry in places.

    Naming Consistency4/5

    Overwhelmingly consistent snake_case with clear namespaced prefixes (sa_, dd_, report_) that group tools logically by domain. Verbs are used predictably (sa_get/sa_set/sa_press, report_start/open/list). A few noun-style names (sa_status, sa_controls, sa_files, sa_info) are minor deviations but still readable.

    Tool Count3/5

    42 tools is heavy and lands in the '25+' range that the rubric flags as potentially too many. However the server covers a genuinely broad instrument surface (multiple SA modes, Digital Demod, GUI control, files, state, and a full reporting subsystem), so many tools earn their place. It is on the excessive side but not arbitrary padding.

    Completeness5/5

    The surface is remarkably complete for an instrument-control server: connect/status, configuration, measurement, results parsing, markers, traces, GUI control, dialogs, file/state management, Digital Demod, and a full report lifecycle (start/open/list/status/add/edit/export). Generic sa_get/sa_set/sa_press/sa_scpi cover the long tail, leaving no obvious dead ends beyond a trivial missing explicit disconnect.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables AI assistants to directly control NI oscilloscopes (e.g., PXIe-5160/5164/5110) through the Model Context Protocol, including waveform acquisition, measurement, and configuration.
      MIT
    • A
      license
      C
      quality
      B
      maintenance
      Enables natural language control of test instruments like spectrum analyzers and power supplies via SCPI commands, with auto-discovery and multi-instrument session support.
      100
      1
      MIT
    • A
      license
      C
      quality
      D
      maintenance
      Enables AI tools to remotely control a SIGLENT SDS800X HD series oscilloscope, configure acquisition, capture waveforms, and analyze signal quality safely through high-level SCPI tools.
      20
      4
      GPL 3.0
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables LLMs to control a PicoScope 5000A USB oscilloscope for signal generation, block capture, measurements, and frequency response sweeps.
      1
      MIT