Blocks generated by gr-mcp now have a first-class path to GRC's
graphical editor. Previously, .block.yml was only produced as a
side-effect of full OOT export — now clients can generate YAML
standalone to preview, customize, or use without a full OOT module.
- New MCP tool: generate_grc_yaml (registered in block dev mode)
- New model: GrcYamlResult with yaml_content, notes, filename
- Enhanced YAML generation: flags, asserts from min/max bounds,
parameter descriptions, docstring extraction
- New prompt resource: prompts://block-generation/grc-yaml
- 10 new unit tests for GRC YAML generation (475 total, 7 skipped)
- pyproject.toml URLs now point to git.supported.systems/MCP/gr-mcp
- README clone URL updated to match
- Version bumped to 2026.02.20 (date-based scheme)
- Published to PyPI as gnuradio-mcp==2026.2.20
- Change test_block_in_docker to use gnuradio-runtime:latest instead of
non-existent gnuradio/gnuradio:latest
- Add auto_enable option to McpBlockDevProvider to register block dev
tools at startup (avoids MCP reconnect dance)
- Support GR_MCP_BLOCK_DEV=1 env var for auto-enabling
Restructure ClientCapabilities to explicitly surface all MCP 2025-11-25
client capabilities:
- roots: workspace directory exposure (listChanged notification support)
- sampling: server-initiated LLM requests (tools, context sub-caps)
- elicitation: server-prompted user input (form, url modes)
Each capability now has its own Pydantic model with clear documentation
about what it enables. Raw capabilities dict preserved for any unknown
or future capabilities.
Add get_client_capabilities and list_client_roots tools to inspect
MCP client information. These help debug MCP connections by exposing:
- Client name/version (e.g., "claude-code" v2.1.15)
- Protocol version and supported capabilities
- Root directories advertised by the client (typically CWD)
Both tools are always registered (part of mode control, not runtime).
Runtime tools now register on-demand rather than at startup:
- get_runtime_mode(): check mode status and available capabilities
- enable_runtime_mode(): dynamically register 36 runtime tools
- disable_runtime_mode(): remove runtime tools when not needed
At startup, only 33 design-time tools are registered. When runtime mode
is enabled, tool count increases to 69. This reduces context usage
significantly when only doing flowgraph design work.
Uses FastMCP's add_tool/remove_tool API for dynamic registration,
following MCP spec's notifications/tools/list_changed pattern.
build_module() now re-registers when a Docker image exists but the
registry entry is missing (e.g., after manual registry edits or
schema migrations). This fixes auto-build in combo workflows where
a module's image exists but isn't tracked.
Also fixes gr-rds catalog branch (main → maint-3.10) and adds
tests for per-entry registry validation and orphan re-registration.
Combine multiple OOT modules into a single Docker image using
multi-stage COPY from existing single-OOT images. No recompilation
needed — fast and deterministic.
New MCP tools: build_multi_oot_image, list_combo_images, remove_combo_image
Also hardens _load_registry() to validate per-entry instead of
all-or-nothing, preventing one corrupt entry from discarding the
entire registry.
Add two new OOT modules to the catalog:
- gr-leo: LEO satellite channel simulator from LibreSpace Foundation
- gr-dl5eu: DVB-T OFDM synchronization and TPS decoder
Update fix_binding_hashes.py to search python/**/bindings/ recursively,
supporting both GR 3.9 layout (python/bindings/) and GR 3.10 layout
(python/<module>/bindings/).
New installable modules:
- foo: Wireshark PCAP connector, burst tagger (bastibl, maint-3.10)
- owc: Optical Wireless Communication simulation (UCaNLab, main)
- dab: DAB/DAB+ digital audio broadcast receiver (hboeglen, maint-3.10)
gr-dab requires fdk-aac-dab (bundled subdir) + libfaad-dev at build time.
gr-foo is a dependency of the already-cataloged gr-ieee802-11.
Catalog now has 22 modules (12 preinstalled, 10 installable).
All 3 new modules built and verified in Docker.
- Fix branch names for 5 modules discovered during install attempts:
ieee802_11, ieee802_15_4, adsb (maint-3.10), iridium, nrsc5 (master)
- Remove packet_radio (repo gone/private)
- Add build_deps for modules that need them (castxml, autotools, Qt5)
- Add fix_binding_hashes.py helper script to Dockerfile builds to
prevent castxml regen failures from stale pybind11 header hashes
- Use tar build context in Docker builds to support COPY instruction
- Note inspector as incompatible with GR 3.10 (API changes)
Successfully built: ieee802_11, ieee802_15_4, adsb, iridium, nrsc5
Curated catalog of 20 GNU Radio OOT modules served via two MCP
resources (oot://directory, oot://directory/{name}). Each entry
includes git URL, branch, build deps, and a ready-to-use
install_oot_module() example.
Modules are tagged preinstalled when they ship with the
gnuradio-runtime base Docker image (12 of 20), so agents can
distinguish what's already available from what needs building.
Single MCP tool call builds any GNU Radio OOT module from a git repo
into a reusable Docker image. Generates a Dockerfile from template,
builds via Docker SDK, and tracks results in a persistent JSON registry.
New tools: install_oot_module, list_oot_images, remove_oot_image
Also changes launch_flowgraph default xmlrpc_port from 8080 to 0 (auto)
LoRa receiver flowgraph built entirely through gr-mcp MCP tools.
Successfully decodes live packets from Heltec V3 beacon at 915MHz,
SF7, BW125k via RTL-SDR.
Signal chain: osmosdr(1MSPS) → LPF(200kHz, dec×2) → lora_rx(500kSPS)
→ message_debug
Key settings for RTL-SDR live reception:
- DC offset auto-correction (mode=2) to remove DC spike at center freq
- IQ balance auto-correction (mode=2)
- signal.pause() fallback for Docker detached mode
- XML-RPC on 0.0.0.0 for Docker host accessibility
Flowgraphs generated by GRC on the host often fail in Docker containers
due to three issues:
1. message_debug constructor signature changed between GR 3.10.5 and
3.10.12 — strip the second arg for older runtimes
2. XML-RPC binds to localhost, unreachable from Docker host — rewrite
to 0.0.0.0
3. input('Press Enter to quit:') gets immediate EOF in detached
containers, killing the flowgraph instantly — inject signal.pause()
fallback after EOFError
All patches are applied in a single pass via patch_flowgraph() before
the container launches. The original file is never modified.
Add add_block_path() and get_block_paths() MCP tools for incremental
OOT module loading with BlockPathsModel responses. On startup, auto-scan
/usr/local/share and ~/.local/share for OOT blocks so modules like
gr-lora_sdr are available without manual configuration.
The container's GNU Radio build doesn't support the log_levels
parameter added in newer GRC versions. Use single-arg form:
blocks.message_debug(True) instead of blocks.message_debug(True, gr.log_levels.info)
Add comprehensive integration tests that don't require Docker:
- test_xmlrpc_subprocess.py: Tests XmlRpcMiddleware against a real
subprocess-based XML-RPC server. Covers connection, variable
discovery, get/set operations, and flowgraph control (18 tests).
- test_mcp_runtime.py: Tests MCP runtime tools via FastMCP Client.
Verifies connect, disconnect, list_variables, get_variable,
set_variable, and flowgraph control work end-to-end (15 tests).
- test_fm_scanner.py: Tests signal probe functionality and FM scanner
parsing. Includes unit tests for helper functions and integration
tests for flowgraph construction with signal probe blocks (18 tests).
All 59 integration tests pass. These tests provide faster feedback
than Docker-based tests while still exercising real XML-RPC
communication and flowgraph construction.
New files for running the FM receiver in Docker with audio output:
- Dockerfile.gnuradio-audio: GNU Radio image with ALSA→PulseAudio bridge
- libasound2-plugins for ALSA PulseAudio plugin
- /etc/asound.conf configures ALSA to route to PulseAudio
- docker-compose.fm-receiver.yml: Full FM receiver setup
- PulseAudio socket mount for audio
- USB passthrough for RTL-SDR (requires privileged mode)
- XML-RPC port 8090 exposed for tuning control
- Environment vars: FREQ_MHZ, GAIN
- entrypoint-fm.sh: Builds and runs flowgraph at specified frequency
- run-fm-receiver.sh: Helper script with usage instructions
Usage:
HOST_UID=$(id -u) FREQ_MHZ=107.2 docker compose -f docker/docker-compose.fm-receiver.yml up
Add probe_avg_mag_sqrd_c block that taps the LPF output to measure
filtered RF power. A variable_function_probe polls it and exposes
signal_level via XML-RPC.
Display features:
- Signal strength in dB shown on tune and on 's' command
- Color-coded bar: green (strong), yellow (medium), red (weak)
- Updates after each frequency change
Signal chain now includes measurement tap:
source → LPF → demod → audio
↓
probe → var_function_probe ("signal_level")
Replace template-based prepare_flowgraph() with build_fm_receiver() that
constructs the entire flowgraph in Python code:
- Initialize Platform (same as gr-mcp main.py)
- Create variables: samp_rate, freq
- Create blocks: osmosdr_source, low_pass_filter, analog_wfm_rcv, audio_sink
- Add xmlrpc_server for runtime frequency control
- Connect signal chain: source → LPF → demod → audio
- Save and compile with grcc
This proves gr-mcp's middleware approach works end-to-end: parameters
set via set_params() serialize correctly after the lru_cache fix.
No longer needs examples/fm_receiver.grc template file.
The lru_cache on get_block() caused three cache coherence bugs:
1. Removed blocks remained accessible — set_block_params would
modify a detached block object that no longer exists in the
flowgraph, so save_flowgraph would serialize stale state.
2. Re-created blocks with the same auto-generated name would
return the cached (removed) block instead of the new one.
3. Renamed blocks (via set_block_params id=...) left phantom
cache entries under the old name.
Fix: always look up blocks from the live flowgraph. Block lookup
is a linear scan of typically <20 blocks — no cache needed.
Raises KeyError instead of StopIteration for missing blocks.
The --tune flag now builds an FM receiver from a GRC template,
compiles it with grcc, launches it as a subprocess, and tunes
via XML-RPC — the same mechanism gr-mcp uses for runtime
parameter changes. Includes an interactive freq> prompt for
live re-tuning without restarting the flowgraph.
GRC's SimpleXMLRPCServer uses register_instance() which doesn't
expose system.listMethods. Wrap the connectivity check in a
try/except so a Fault is treated as "connected" while
ConnectionRefusedError still propagates.
--tune flag scans the band then tunes to a station using rtl_fm
piped to aplay. Supports interactive station picker (--tune) or
direct frequency (--tune 102.0).
LLM-driven SDR session recorded 2026-01-28T15:05:21-07:00.
Built flowgraph via MCP tools, launched in Docker with RTL-SDR
USB passthrough, captured 128s of WBFM audio to WAV.
Song identified via songrec/Shazam: "Damn I Love Miami"
by Pitbull & Lil Jon.
Signal chain: RTL2838 → osmocom source (2.4 MS/s)
→ LPF (100 kHz, ÷5) → WBFM demod (÷10) → 48 kHz WAV
Includes GRC flowgraph, WAV recording, and helper scripts.
Prevent silent Docker bind failures by checking port availability
before container creation. Supports auto-allocation (port=0) and
patches compiled flowgraphs when the embedded XML-RPC port differs
from the requested port.
Two async agent-to-agent design threads:
xmlrpc-runtime-integration/ (10 messages)
- Architecture handoff from gnuradio-codebase-agent
- Phase 1 Docker + XML-RPC design decisions
- epy_block vs build() vs .block.yml discussion
- Implementation review and fixes
coverage-integration/ (7 messages)
- Cross-process coverage design for containerized flowgraphs
- SIGTERM vs SIGKILL for coverage.py atexit handlers
- Smoke test results and bug fixes
These threads document the collaborative design process between
gr-mcp-agent and gnuradio-codebase-agent using the immutable
flat-file agent thread protocol.
Agent-to-agent discussion between gr-mcp and gnuradio-codebase agents:
- 001: gnuradio-agent proposes RuntimeProvider for XML-RPC control
- 002: gr-mcp-agent initial response with architecture ideas
- 003: gnuradio-agent provides technical details (lock/unlock, types, ControlPort)
- 004: gr-mcp-agent responds with Docker-based architecture proposal
Key design decisions:
- Docker container for GNU Radio runtime (librespace/gnuradio)
- Xvfb for headless QT GUI rendering
- Support both XML-RPC and ControlPort/Thrift transports
- OOT block generation via epy_block and full modules