mcbladerf
MCP server for Nuand BladeRF SDR control via SoapySDR.
Overview
mcbladerf provides Model Context Protocol (MCP) tools for controlling Nuand BladeRF software-defined radios. It enables AI assistants to configure RF parameters, capture IQ samples, and perform spectrum analysis through a simple tool-based interface.
Features
- Device Management: Enumerate, connect, and disconnect BladeRF devices
- RF Configuration: Set/get frequency, sample rate, bandwidth, and gain stages
- IQ Capture: Stream samples to files in CF32 or CS16 format
- Spectrum Analysis: Capture and compute FFT power spectra
- Advanced Operations: FPGA loading, firmware flashing, DC calibration
Requirements
- Python 3.14+ (must match system SoapySDR installation)
- SoapySDR with BladeRF support (system packages)
- bladeRF CLI tools (for advanced operations)
- BladeRF 1.0 or 2.0 hardware
System Dependencies (Arch Linux)
sudo pacman -S soapysdr soapysdr-bladerf bladerf
Installation
SoapySDR is a C++ library with Python bindings that must be installed system-wide. The venv needs access to system site-packages:
cd mcbladerf
# One-time setup (creates venv with system site-packages access)
make setup
# Or manually:
uv venv --system-site-packages --python 3.14
uv pip install -e .
Running
# After setup, run with:
uv run mcbladerf
# Or via make:
make run
Usage with Claude Code
# Add to Claude Code (with full path)
claude mcp add mcbladerf -- uv run --directory /path/to/mcbladerf mcbladerf
The .mcp.json is already configured for local use:
claude -p "show bladerf status" --mcp-config .mcp.json --allowedTools "mcp__mcbladerf__*"
Available Tools
Device Management
bladerf_status- Get comprehensive device statusbladerf_list_devices- Enumerate connected devicesbladerf_connect- Connect to a device by serial or indexbladerf_disconnect- Close the current connection
RF Configuration
bladerf_get_frequency/bladerf_set_frequency- Center frequencybladerf_get_sample_rate/bladerf_set_sample_rate- Sample ratebladerf_get_bandwidth/bladerf_set_bandwidth- Analog bandwidthbladerf_get_gain/bladerf_set_gain- Individual gain stagesbladerf_list_gains- Enumerate available gain stagesbladerf_get_frequency_range/bladerf_get_sample_rate_range- Hardware limits
IQ Capture
bladerf_capture_iq- Stream samples to file (CF32/CS16)bladerf_capture_spectrum- Capture and compute FFT, power in dBFSbladerf_stream_status- Capture progress, or the result of a background capturebladerf_stop_capture- Abort running capture
Advanced
bladerf_load_fpga- Load FPGA bitstream (RAM)bladerf_flash_firmware- Flash FX3 firmwarebladerf_calibrate_dc- Run DC offset calibrationbladerf_get_hardware_info- Detailed hardware infobladerf_set_loopback- Configure loopback modebladerf_probe- Device diagnostics
Example Session
> Connect to my BladeRF and show status
[Uses bladerf_connect and bladerf_status]
> Set frequency to 915 MHz with 2 MHz sample rate
[Uses bladerf_set_frequency and bladerf_set_sample_rate]
> Capture 1 second of IQ data to ~/claude/sdr/mcbladerf/captures/capture.cf32
[Uses bladerf_capture_iq]
> Show me the spectrum around 100 MHz
[Uses bladerf_capture_spectrum]
Capture Files and Readings
CF32 files are complex64 scaled to ±1.0. CS16 files are the device's raw SC16 Q11 integers, interleaved I/Q with full scale 2048, the same layout bladeRF-cli writes with format=bin. Spectrum power is dBFS, so a full-scale tone reads 0. Every capture reports clipped_samples and peak_dbfs, so check those before trusting the data.
When the host falls behind, the driver drops samples, and not always with an overflow error (a libusb transfer timeout drops data silently). Each read carries a hardware timestamp, so every gap is measured exactly and, by default, zero-filled: sample N of the file is always N / rate seconds after the first. Such captures finish as completed_with_gaps with a warning, samples_dropped and a gap list in output-sample offsets. Pass fill_gaps=false to get a shorter file with the same report instead.
Long captures should use background=true: the call returns at once, bladerf_stream_status shows progress and then the result, and bladerf_stop_capture ends it early. Spectra come back as a 256-point max-hold summary with noise_floor_db; peak and floor are computed at full resolution. Out-of-range gain, frequency, rate or bandwidth values are rejected, not clamped. The sample rate resets to 4 MHz each time the device is opened, so always set it.
Write captures to disk, not /tmp. On many Linux systems /tmp is a tmpfs held in RAM, and 30 Msps CS16 is 120 MB per second, so a few long captures can push the machine into swap and the OOM killer. Once the host stalls, the stream drops samples. The project's captures/ directory is gitignored for this.
Gains you leave out keep whatever the device last had, including values set by another process. The radio is often shared, so set all three RX gains explicitly.
While connected, the server holds the USB device and nothing else can open it. The bladeRF-cli backed tools (probe, calibrate, FPGA load, firmware flash, hardware info) release it for the duration and restore the RX/TX settings afterwards. Call bladerf_disconnect when you are done so other tools can use the radio.
Troubleshooting
setupStream() -3 / bladerf_sync_config() returned -3. The SoapySDR plugin was built against an older libbladeRF. libbladeRF 2025.10 inserted BLADERF_FORMAT_SC16_Q11_PACKED into the bladerf_format enum without changing the soname, so a stale soapybladerf requests the wrong format and bladeRF1 rejects it. Rebuild soapybladerf against the installed headers (the Arch PKGBUILD needs -DCMAKE_POLICY_VERSION_MINIMUM=3.5 under CMake 4).
No device(s) available (-7) with the board plugged in. Another process has the device open. Check pgrep -xa bladeRF-cli; pgrep -f can match your own shell.
No module named 'SoapySDR'. The venv must see system site-packages. Recreate it with make setup.
BladeRF 1.0 x40 Specifications
- Frequency: 237.5 MHz - 3.8 GHz
- Sample rates: 80 Ksps - 40 Msps
- RX Gain stages: lna (0/3/6 dB), rxvga1 (5-30 dB), rxvga2 (0-30 dB)
- TX Gain stages: txvga1 (-35 to -4 dB), txvga2 (0-25 dB)
License
MIT. See LICENSE.