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
- Bump version to 0.2.0
- Require Python 3.14+
- Update to FastMCP 3.0.0b1 with new Client API
- Update tests for FastMCP 3.0 (Pydantic models in .data)
- Pin modern dependency versions