Skip to main content
Glama

sdrpp-mcp

An MCP server that drives a running SDR++ instance to find, identify, decode, record and log radio transmissions. A companion skill (skill/sdr-signal-id) packages the identification workflow and reference material: signal signatures, decoder notes, hardware quirks and US legal guardrails.

It works with any SDR++ source. It was developed and tested on an RTL-SDR (Nooelec SMArTee XTR, E4000 tuner, 2.4 MS/s).

Features

  • Live spectrum: snapshot the current passband with max-hold detection against an estimated noise floor, or step-sweep an arbitrary range (up to 200 retunes). Both return waterfall PNGs. Retuning lands exactly on the requested centre, sweeps report any range they failed to cover, and every tool that has to retune puts the Radio VFO back afterwards.

  • Signal analysis: extract a channel and measure:

    • SNR, 99% occupied bandwidth, duty cycle and bursts

    • envelope statistics, spectral skew and flatness

    • FM deviation and FSK levels, symbol rate (snapped to standard bauds), TDMA framing

    • CTCSS and audio tones, WFM stereo pilot and RDS

    A heuristic classifier then makes a modulation guess: NFM, WFM, AM, SSB, CW/OOK, 2/4-FSK, wideband digital or carrier.

  • Band plan lookup: US allocations plus named channels (NOAA WX1–7, FRS/GMRS, MURS, APRS, ACARS, AIS, ISM, etc.).

  • Demodulation and recording: FM, WFM (75 µs de-emphasis), AM, USB, LSB and CW to WAV, or raw IQ (cu8 / cf32) for offline tools.

  • Decoding:

    • multimon-ng: POCSAG/FLEX pagers, APRS/AFSK1200, EAS/SAME, DTMF, ZVEI, Morse, X10.

    • rtl_433: ISM-band sensors.

  • Channel watching and LoRa/MeshCore decoding: continuous capture of one channel, logging every burst; LoRa is recognised by dechirping, demodulated with gr-lora_sdr, and MeshCore packets are parsed (route, hop path, payload type, node adverts with name/type/position). Encrypted message bodies are not decrypted.

  • Logbook: SQLite log of identified signals, searchable, exportable as an SDR++ Frequency Manager bookmark list.

Related MCP server: GR-MCP

How it talks to SDR++

SDR++ module

Port

Used for

Rigctl Server

localhost:4532

tuning the Radio VFO, mode/bandwidth, start/stop, triggering the centre probe

IQ Exporter (Baseband, TCP server, Float32)

localhost:4533

live wideband IQ for spectrum, analysis and demodulation

Recorder "Center Probe" (baseband)

–

rigctl only reports the VFO, so a ~0.1 s baseband recording's filename reveals the true centre frequency; the file is then deleted

setup_sdrpp.py configures all three. Run it with SDR++ closed. It backs up each changed file next to the original as *.json.bak-<time>.

Keep the Recorder and Center Probe menus in SDR++'s sidebar collapsed (the setup script collapses them). Rigctl starts and stops the probe from a background thread, and if a recorder menu is being drawn at that moment SDR++ can abort with an ImGui PushItemFlag/PopItemFlag assertion.

Install

Requires Python ≥ 3.10 and SDR++ 1.2+ (nightly builds include the IQ Exporter).

git clone https://github.com/entitledosprey/sdrpp-mcp.git
cd sdrpp-mcp
python3 -m venv .venv && .venv/bin/pip install -e .
python3 setup_sdrpp.py            # with SDR++ closed
sudo apt install multimon-ng rtl-433   # optional decoders

Optional, for LoRa/MeshCore decoding in watch_channel (GNU Radio + gr-lora_sdr, used through the system python3):

sudo apt install gnuradio-dev cmake pybind11-dev libspdlog-dev libfmt-dev libgmp-dev
git clone https://github.com/tapparelj/gr-lora_sdr.git && cd gr-lora_sdr
mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make -j"$(nproc)"
sudo make install && sudo ldconfig

Register the server with your MCP client as a stdio server, for example:

{
  "mcpServers": {
    "sdrpp": { "command": "/path/to/sdrpp-mcp/.venv/bin/sdrpp-mcp" }
  }
}

Skill

skill/sdr-signal-id/ is an Agent Skill (SKILL.md plus references/). To install it, copy or symlink the directory into your client's skills folder:

ln -s "$PWD/skill/sdr-signal-id" ~/.config/<client>/skills/sdr-signal-id

Example requests

Once the server (and optionally the skill) is registered with an MCP client, plain requests map onto the tools:

Request

What runs

"What's on 2 m / 70 cm right now?"

band_sweep 144–148 / 420–450 MHz, then analyze_signal on anything that isn't a spur

"What is this signal at 154.6?"

identify_frequency + analyze_signal

"Decode pagers on 929–932"

band_sweep, then decode … pager on active channels

"Any weather sensors?"

decode 433.92M ism (or 915M)

"Watch the MeshCore channel while I send an advert"

watch_channel 910.525M

"Log that"

log_signal

Tools

Tool

Purpose

sdr_status

SDR++ running? rigctl/IQ reachable, centre, rate, gain, clipping, installed decoders

sdr_launch

start SDR++ (optionally with --autostart); its console log goes to sdrpp.log in the data folder

sdr_tune

set VFO frequency, mode, bandwidth

sdr_power

start/stop the source

spectrum_snapshot

detect signals in the current passband, returns a waterfall image

band_sweep

sweep start–stop, merge detections, restore the VFO

identify_frequency

band plan allocation and known channels near a frequency

analyze_signal

measure and classify one signal, with a zoomed waterfall

record_audio

demodulate to WAV

record_iq

save raw IQ (cu8/cf32)

decode

run multimon-ng or rtl_433 on a capture

watch_channel

listen to one channel for up to 15 min; detect, decode and parse LoRa/MeshCore packets

p25_status / p25_calls

live state and recent calls of the sdrpp-p25 trunking module, if it's running

log_signal / search_log / delete_log_entry

logbook

export_bookmarks

write the logbook to SDR++'s Frequency Manager (SDR++ must be closed)

Frequencies accept Hz or strings such as 146.52M, 433.92 MHz or 1090m. Bare numbers below 10000 are treated as MHz.

P25 trunking

sdrpp-p25 is a companion SDR++ module that trunks P25 Phase 1/2 systems and plays the voice in SDR++. When it is running it owns the SDR's tuning: p25_status and p25_calls read its decoder's API and event log, and every tool here that would retune SDR++ refuses until the module is stopped.

Data locations

Everything lives under ~/.local/share/sdrpp-mcp/, or under SDRPP_MCP_DATA if that is set in the server's environment (run setup_sdrpp.py with the same variable so SDR++'s probe recorder writes there too):

  • images/: PNG spectra and waterfalls (named <time>_<freq>_<kind>); override with SDRPP_MCP_IMAGES

  • captures/: WAV and IQ recordings

  • center-probe/: temporary probe files

  • logbook.db: the logbook

  • sdrpp.log: SDR++'s console output when started with sdr_launch

Other tools

  • tools/sdrtrunk_to_sdrpp.py PLAYLIST.xml OUT_DIR: turns an SDRTrunk playlist's site frequencies into SDR++ Frequency Manager import files. SDR++ can't follow P25 trunking or decode its voice; for that use SDRTrunk, OP25 or DSD-FME (which can retune SDR++ through its rigctl server).

Frequency calibration

RTL-SDR crystals are typically off by somewhere between 0 and 50 ppm, and TCXO models by about 1 ppm. A good local reference is the ATSC digital TV pilot: an unmodulated carrier at the channel's lower edge plus 309.441 kHz. Measure the apparent offset of a few strong pilots (narrow FFT peak within ±15 kHz of the expected frequency; ignore weak channels and stations that run offset pilots). Broadcast FM carriers are too noisy for this. The correction is ppm = (true − measured) / true × 10⁶. Enter it, rounded, under Source → PPM Correction in SDR++.

The software is for receiving only. In the US, 47 USC 605 and ECPA restrict divulging or using the contents of private communications. Decoding cellular or cordless-phone content and decrypting anything are both off limits. Know your local rules.

History

See CHANGELOG.md.

License

MIT. See LICENSE.

Available Tools

15 tools
analyze_signalA

Measure a signal's characteristics to help identify it: occupied bandwidth, carrier, envelope constancy, FM deviation, FSK levels, symbol rate, burst timing, TDMA periodicity, CTCSS / audio tones (for FM/AM), stereo pilot/RDS (WFM). Returns heuristic modulation guesses, band-plan context and a zoomed waterfall image. If bandwidth_hz is omitted it is estimated. If SDR++ has to retune to reach the signal, the original VFO is restored afterwards, unless tune_vfo=True, which leaves the Radio VFO on the signal so the user can listen.

Inputs: frequency (required), seconds, tune_vfo, bandwidth_hz.

band_sweepA

Sweep a frequency range wider than the SDR's bandwidth by retuning SDR++ in steps. Returns detected signals across the range plus a combined spectrum image. Note: this moves the user's SDR++ view; the original VFO frequency is restored afterwards.

Inputs: stop (required), start (required), top, dwell_s, +2 more.

decodeA

Capture and decode digital transmissions. decoder:

  • multimon-ng (FM-demodulated audio): pager (POCSAG+FLEX), pocsag, flex, aprs, eas/same, dtmf, zvei, morse

  • rtl_433: ism (433/315/345/915 MHz sensors, remotes, TPMS, meters) -- run on captured IQ Long captures block for seconds. Privacy: pager and similar traffic can contain personal information; summarise rather than repeat sensitive content.

Inputs: decoder (required), frequency (required), seconds, bandwidth_hz.

delete_log_entryC

Delete a logbook entry by id.

Inputs: entry_id (required). Returns: result

export_bookmarksA

Write the logbook into SDR++'s Frequency Manager as a bookmark list (shown on the waterfall). SDR++ must be closed; a backup of the old bookmarks file is kept.

Inputs: list_name. Returns: result

identify_frequencyA

Band-plan lookup (US/FCC, ITU Region 2): which allocation a frequency falls in and which well-known channels/services are nearby. No radio access needed.

Inputs: frequency (required), window_khz.

log_signalC

Save an identified (or mystery) signal to the logbook. confidence: confirmed/likely/guess.

Inputs: label (required), frequency (required), mode, files, +4 more.

record_audioB

Demodulate a frequency from the live IQ stream and save a WAV (independent of SDR++'s own audio, so the user can keep listening elsewhere). mode: FM/NFM, WFM, AM, USB, LSB, CW.

Inputs: frequency (required), mode, seconds, audio_rate, +1 more.

record_iqB

Save channelised IQ around a frequency. fmt_: 'cu8' (rtl_433 / most tools, 8-bit unsigned) or 'cf32' (float32 complex). The output sample rate is chosen as exactly bandwidth_hz (250 kS/s default suits rtl_433). The filename carries freq and rate.

Inputs: frequency (required), fmt_, seconds, bandwidth_hz.

sdr_launchA

Start SDR++ if it isn't running (autostart=True also starts the SDR source).

Inputs: autostart. Returns: result

sdr_powerA

Start (on=True) or stop the SDR source in SDR++ (the play/stop button).

Inputs: on (required). Returns: result

sdr_statusA

Report SDR++ state: running, centre/VFO frequency, mode, source, gain, sample rate, whether rigctl and the IQ Exporter are reachable, and which decoders are installed.

Inputs: no named parameters.

sdr_tuneB

Tune SDR++'s Radio VFO so the user hears the frequency. mode: FM (=NFM), WFM, AM, DSB, USB, LSB, CW, RAW. bandwidth_hz optional (e.g. 12500 NFM, 200000 WFM, 10000 AM, 2700 SSB).

Inputs: frequency (required), mode, bandwidth_hz.

search_logC

Search the logbook by text and/or frequency range.

Inputs: stop, text, limit, start. Returns: result

spectrum_snapshotC

Capture the full SDR++ passband, detect signals (max-hold, so short bursts count) and return a table plus a spectrum/waterfall image. Optionally retune to frequency first. duty = fraction of time the signal was present (1.0 = continuous).

Inputs: top, seconds, fft_size, frequency, +1 more.

Tool Schema Changelog

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

Loading tool schema changelog…

    TDQS

    A3.5/5.0

    Scored across 15 tools

    Disambiguation5/5

    Each tool targets a clearly distinct aspect of SDR++ control or signal workflow: status, launch, tune, power, snapshot, sweep, identify, analyze, record audio/IQ, decode, and logbook CRUD. The descriptions explicitly delineate boundaries (e.g., passband snapshot vs. wideband sweep vs. single-signal analysis), so an agent can reliably select the right tool.

    Naming Consistency4/5

    All names use snake_case, and the sdr_ prefix logically groups control tools. However, the verb/noun ordering is not uniform across the rest (e.g., identify_frequency, spectrum_snapshot, band_sweep, decode), which is a minor deviation from a predictable verb_noun pattern.

    Tool Count5/5

    15 tools sit at the top of the ideal range and each covers a distinct, necessary capability for SDR++ remote control, signal analysis, recording, and logging. The set feels comprehensive without redundancy.

    Completeness4/5

    Core workflows—control, capture, analyze, record, decode, log—are well covered. Minor gaps include setting gain, sample rate, or input source, and no explicit log-entry update or list operation, though those can be worked around via search or manual SDR++ UI use.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      An MCP server that enables natural language-driven creation and management of GNU Radio flowgraphs with validated block connections and parameter configurations. It provides tools for searching the GNU Radio block library, detecting SDR hardware, and generating functional signal processing code.
      8
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables natural language control of a live GNU Radio SDR flowgraph, allowing users to tune frequencies, adjust gain, capture IQ samples, analyze spectra, and detect signals through an MCP-compatible client.
      2
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables MCP clients to control a local TradingView Desktop instance, providing tools to read chart state, change symbols and timeframes, and fetch OHLCV data.
      92 npm
      134
      MIT