MCP Server
The Birdcage MCP server (mcbirdcage) exposes the full dish control surface as MCP tools, resources, and prompts. Any MCP-compatible client — Claude Code, Claude Desktop, a custom agent, anything that speaks the protocol — can connect a serial port, slew motors, measure signal strength, query orbital catalogs, and read firmware registers. Demo mode works without hardware.
The same device layer that backs the TUI backs the MCP server. The difference is who’s driving: a person pressing arrow keys, or a language model calling move_to(azimuth=180, elevation=45).
Quick Start
Section titled “Quick Start”# Demo mode — no hardware, no serial portBIRDCAGE_DEMO=true uvx mcbirdcage
# Add to Claude Codeclaude mcp add mcbirdcage -- uvx mcbirdcage
# With hardwareBIRDCAGE_PORT=/dev/ttyUSB0 uvx mcbirdcagecd mcp/uv sync
# Demo modeBIRDCAGE_DEMO=true uv run mcbirdcage
# Add to Claude Code (local dev)claude mcp add mcbirdcage -- uv run --directory /path/to/mcp mcbirdcageEnvironment Variables
Section titled “Environment Variables”| Variable | Default | Description |
|---|---|---|
BIRDCAGE_DEMO | false | Set true to use DemoDevice (simulated motors, RSSI, full NVS table) |
BIRDCAGE_PORT | /dev/ttyUSB0 | Serial port path for hardware mode |
BIRDCAGE_FIRMWARE | g2 | Firmware variant: g2, hal205, or hal000 |
BIRDCAGE_CRAFT_URL | https://space.warehack.ing | Orbital catalog API endpoint |
36 tools across 6 modules. Every tool that touches hardware requires a prior connect call (or demo mode).
Connection
Section titled “Connection”| Tool | Parameters | Description |
|---|---|---|
connect | port? firmware? skip_init? | Open serial port, optionally run firmware init sequence |
disconnect | — | Close serial port, return to root menu |
status | — | Connection state, port, firmware variant, current menu |
Movement
Section titled “Movement”| Tool | Parameters | Description |
|---|---|---|
get_position | — | Current AZ/EL in degrees |
move_to | azimuth elevation | Move both axes to absolute position (AZ 0-455, EL 18-65 for G2) |
move_motor | motor_id degrees | Move single axis (0=AZ, 1=EL) to absolute angle |
home_motor | motor_id | Stall-detect homing to reference position (audible grinding) |
engage_motors | — | Energize steppers (apply holding torque) |
release_motors | — | De-energize steppers (dish free under wind/gravity) |
stow | — | Move to AZ=0, EL=65 (transport-safe with stock feed) |
get_step_positions | — | Raw step counts (G2: 40000 steps/rev AZ, 24960 EL) |
get_el_limits | — | Firmware EL limits: min, max, home (degrees) |
Signal
Section titled “Signal”| Tool | Parameters | Description |
|---|---|---|
get_rssi | iterations? (default 10) | Averaged DVB tuner RSSI (noise floor ~500) |
get_adc_rssi | — | Single-shot raw ADC count (bypasses DVB averaging) |
get_lock_status | — | DVB signal lock, RSSI, glitch count |
enable_lna | — | Set LNB to 13V / V-pol (powers LNA for radio work) |
set_lnb_voltage | mode (odu or stb) | 13V/V-pol (odu) or 18V/H-pol (stb). Boot default is 18V. |
get_dvb_config | — | BCM4515 chip ID, revision, firmware version |
get_channel_params | — | Frequency, symbol rate, modulation, LNB polarity |
az_sweep | start_az span step_cdeg? num_xponders? timeout? | Firmware-accelerated AZ sweep (azscanwxp). Returns per-point RSSI/lock/SNR. Requires homed motors. |
System
Section titled “System”| Tool | Parameters | Description |
|---|---|---|
get_firmware_id | — | Full MCU/firmware ID: version, silicon, board, clock, flash layout |
get_motor_dynamics | — | Max velocity and acceleration for both axes |
set_max_velocity | motor_id deg_per_sec | Set motor max velocity (G2 defaults: AZ=65, EL=45 deg/s) |
set_max_acceleration | motor_id accel | Set motor max acceleration (G2 default: 400 deg/s^2) |
get_motor_life | — | Lifetime stats: total moves, degrees traveled, uptime hours |
get_pid_gains | — | PID gains for both axes (Kp, Kv, Ki) |
set_pid_gains | motor_id kp kv ki | Set PID gains. Bad values cause oscillation or motor damage. |
get_a3981_diag | — | Allegro A3981 stepper driver fault status (OK or FAULT per axis) |
get_a3981_modes | — | Step mode (AUTO/fixed), current mode, step size per driver |
nvs_dump | — | Full 134-entry NVS table: name, current, saved, default |
nvs_read | index | Single NVS value (key indices: 20=tracker, 80=AZ vel, 101=min EL) |
Satellite
Section titled “Satellite”Satellite tools use the Craft API for orbital predictions. The API computes AZ/EL server-side from TLE data — no local propagation needed.
| Tool | Parameters | Description |
|---|---|---|
search_satellites | query limit? | Search catalog by name (“ISS”, “NOAA”, “Moon”). Returns NORAD ID, type, group. |
get_passes | norad_id hours? | Upcoming passes: AOS/TCA/LOS times, max EL, duration |
get_next_pass | norad_id | Single soonest pass prediction |
get_visible_targets | min_alt? | All objects currently above horizon with live AZ/EL |
Console
Section titled “Console”| Tool | Parameters | Description |
|---|---|---|
send_raw_command | command | Send arbitrary firmware command, return raw response. The q command is blocked (kills UART shell, requires power cycle). |
Resources
Section titled “Resources”Five read-only MCP resources provide live dish state without tool calls. Clients can subscribe to these for continuous monitoring.
| URI | Description |
|---|---|
birdcage://config | Connection config: demo mode, port, firmware variant, connected flag |
birdcage://position | Live AZ/EL (queries hardware on each read) |
birdcage://firmware | Firmware identification string |
birdcage://motor-dynamics | Max velocity and acceleration for both axes |
birdcage://el-limits | Elevation min/max/home in degrees |
Prompts
Section titled “Prompts”Three guided workflows that walk an LLM through multi-step operations. Call them to get a structured plan with tool names and sequencing.
| Prompt | Workflow |
|---|---|
setup_wizard | Connect to serial port, verify firmware, home both motors, confirm position and EL limits |
satellite_tracking_guide | Search catalog, get passes, wait for AOS, poll get_visible_targets + move_to at 1 Hz |
rf_sweep_guide | Enable LNA, baseline RSSI, configure and run az_sweep, analyze peaks above noise floor |
Example Workflows
Section titled “Example Workflows”These are things an LLM can do with the tool set — not scripts to execute, but descriptions of how the tools compose.
Track the ISS
Section titled “Track the ISS”Search the catalog for “ISS”. The result includes NORAD ID 25544. Call get_passes with that ID to see upcoming passes — pick one with max elevation above 30 degrees for a decent arc. When the AOS time arrives, start a loop: call get_visible_targets to get the ISS’s current AZ/EL (computed server-side from fresh TLEs), then move_to with those coordinates. Repeat every second. The firmware queues motor commands, so the dish follows the arc smoothly even if individual moves are still in progress. Stop when the satellite drops below the dish’s minimum elevation (18 degrees on the G2). The whole sequence is about 5-10 minutes for a typical LEO pass.
RF Sky Survey
Section titled “RF Sky Survey”Enable the LNA with enable_lna (switches LNB from 18V boot default to 13V / V-pol). Call get_rssi to read the noise floor — should be around 500 ADC counts with no signal present. Move the dish to a starting position with move_to(0, 30). Run az_sweep with a wide span (e.g., 180 degrees, 100 centidegree steps, 1 transponder). The firmware handles the sweep natively — no serial round-trips per point. Repeat at EL 35, 40, 45, … up to 60, using move_motor(1, el) between sweeps. The result is a 2D grid of RSSI values mapped to AZ/EL coordinates. Peaks above the noise floor correspond to geostationary satellites, terrestrial interference, or (at Ku-band) the Sun.
Firmware Inspection
Section titled “Firmware Inspection”Call get_firmware_id for the full MCU identification — version 02.02.48, NXP K60 at 96 MHz, 512 KB flash. Call get_a3981_diag to confirm both stepper drivers report OK. Call nvs_dump for the complete 134-entry non-volatile storage table, then spot-check critical values: NVS 20 (tracker disabled), NVS 80-81 (motor velocity/acceleration), NVS 101-102 (elevation limits). For anything not covered by a dedicated tool, drop to send_raw_command — enter a submenu with mot or dvb, issue the command, and read the raw response.