name: avr:calibrate version: 2.0.0 description: | Run a calibration recipe end-to-end. Lists available recipes, recommends one based on hardware, then reads and executes each step via MCP tools. Saves equipment state and run history for later review. allowed-tools: - Bash - Read - Write - Edit - Glob - Grep - Agent - AskUserQuestion
/avr:calibrate
Run a calibration recipe by reading it and executing each step via MCP tools.
Arguments
$ARGUMENTS— recipe name (default: none — show picker). Looks inrecipes/core/{name}.md.
Workflow
Step 1 — Discover hardware and show recipes
1. Call `get_config` to read the user's hardware setup.
2. Show a three-layer system diagram. Build ENTIRELY from config — do not
hardcode ANY labels, counts, models, or connection types.
**Data sources:**
- `config.connections` → physical wiring between devices (from/to/via arrays)
- `config.minidsp.output_slots` → per-output label and type (sub/shaker/unused)
- `config.eq_capabilities` → PEQ slot counts, FIR tap counts per output
- `config.speakers` → speaker groups with positions, models, and specs
- `config.mic.name` → mic model
- `config.denon` → AVR info
**Three layers, same heading style for each:**
Each layer uses a `═══ LABEL ═══` banner, with boxes below it connected by
labeled lines. Lines between layers show connection type from `config.connections`.
**a. INTELLIGENCE layer** (top)
Banner: `═══ INTELLIGENCE ═══`
One box: "avr-calibration (Pi 5) / Claude orchestrator"
Three lines descend from this box to the hardware layer, labeled with their
connection types from `config.connections` (e.g. "HDMI, Network", "USB", "USB").
**b. HARDWARE layer** (middle)
Banner: `═══ HARDWARE ═══`
One separate box per MCP-controlled device — read from `config.connections`:
- **AVR box** — model from `config.denon`. Connections to other hardware devices
(e.g. Analog arrow to miniDSP) shown as horizontal labeled arrows between boxes.
- **DSP box** — "miniDSP 2x4 HD". Inside, list each output from
`config.minidsp.output_slots` with label, type, and EQ capability from
`config.eq_capabilities`. Mark shaker outputs "MUTED during cal", unused "(unused)".
- **Mic box** — model from `config.mic.name`, plus cal file info.
Lines descend from AVR and DSP boxes into the physical layer, labeled with
connection types (speaker_wire, analog).
**c. PHYSICAL layer** (bottom)
Banner: `═══ PHYSICAL ═══`
One room box containing ALL sound-producing transducers (these are NOT
separate device boxes — they live in the room):
- Speakers from `config.speakers` with positions, models, and specs (sensitivity, impedance)
- Subs from `config.minidsp.output_slots` where type=sub, with labels
- Shakers from `config.minidsp.output_slots` where type=shaker, with labels
Group by which hardware device drives them and label the connection type.
3. List all .md files in recipes/core/ using Glob.
4. Read the first few lines of each recipe to get the Goal section.
5. Display them as a numbered list with a brief description.
6. Recommend based on the user's hardware:
- bass-calibration is the primary recipe for sub/bass work
- Note whether the recipe uses FIR or PEQ only (from the Filter Strategy section),
so the user knows what hardware will be touched
7. If $ARGUMENTS names a valid recipe, pre-select it but still confirm.
8. Let the user pick or accept the recommendation.
9. After recipe selection, read the recipe's "## Measurement Signal Path" section
and build the ACTIVE SIGNAL PATH diagram from config. The recipe tells you which
config fields determine the path (e.g. `playback_route`, `speakers`, `output_slots`).
Show only the boxes and connections that are active for this recipe's measurement mode.
Step 2 — Pre-flight check
- Call the
check_systemMCP tool (avr-calibration server) - If any component is unreachable, report the issue and STOP
- Confirm with the user: "System is ready. Starting calibration with recipe: {name}. Proceed?"
Step 3 — Record equipment state
Capture the starting equipment state for the run record:
- Call
get_device_stateto capture AVR volume, input, DSP preset, routing state - Call
save_calibration_runwith the recipe name, target curve, and device_state - Save the returned
run_id— use it to record iterations and final results
Step 4 — Choose execution mode
Ask the user:
Run in safe mode (confirm each DSP write) or autonomous mode (proceed without confirmation, SafetyValidator still enforces limits)?
- Safe mode: Before each signal-path write (
set_polarity,set_delay,set_output_gain,apply_eq,apply_input_eq), describe the intended change and wait for the user to explicitly confirm before calling the tool. - Autonomous mode: Call tools without asking for confirmation. SafetyValidator in code still enforces all safety limits.
Step 5 — Execute the recipe
Read the recipe step by step and execute each instruction by calling the appropriate MCP tools.
You are the orchestrator. The recipe tells you WHAT to do. You decide HOW by calling MCP tools.
Key MCP tools available on the avr-calibration server:
measure— take a sweep measurement, returns frequency response dataapply_eq— write PEQ filters to miniDSP (SafetyValidator enforced). Passsimulation_verified=truewhen the filter set was just confirmed bysimulate_eq(relaxes per-iteration limit to +6 dB).apply_input_eq— write shared input PEQ filters (samesimulation_verifiedsupport)mute_output/unmute_output— mute/unmute specific DSP outputsset_delay/set_polarity/set_output_gain— per-output DSP settingsset_volume/set_master_gain— AVR/DSP volumecheck_system— verify all hardwareget_config/set_config— system configurationget_output_state— per-output gain_db, delay_ms, polarity_invertedget_measurement_history— raw FR data. ALWAYS passmin_hz=20, max_hz=120, format="compact"for sub bass work.get_fr_summary— 1/3-octave summary (coarse convergence checks only)compute_deviation— RMS deviation from target. Supports configurableresolution("sixth_octave" default) andconvergence_threshold(recipe-defined).analyze_ir/analyze_phase/analyze_decay/compare_sub_phase— analyticssimulate_eq/optimize_q/design_fir— simulation and optimizationcalibrate_level/configure_matrix/end_sweep_session— setup toolssave_calibration_run/update_calibration_run/save_calibration_iteration— run tracking
FR data resolution rule: Always use get_measurement_history(min_hz=20, max_hz=120, format="compact") when designing or verifying sub bass filters. The compact format gives 0.18Hz resolution, ~550 points per session, ~8KB. get_fr_summary returns only 11 bands — too coarse to resolve narrow peaks.
Step 6 — Record each iteration (NON-NEGOTIABLE)
After EVERY iteration of EVERY phase (Phase 0 baseline, Phase 1 alignment, Phase 2 PEQ, Phase 3 FIR, Phase 4 target shaping — every one):
- MUST call
save_calibration_iteration(run_id, iteration=N, rms_before=..., rms_after=..., filters_proposed=..., filters_applied=..., safety_ok=...). This is not optional, even if the iteration was a no-op or "just a measurement." - Report what you measured, what you changed, and convergence status
If you skip this call, the run record will look empty in get_calibration_runs even though
you did the work — exactly the bug seen with runs 17/18 on 2026-04-24, where 57 measurement
sessions completed but iterations_run stayed at 0.
Step 7 — Convergence or max iterations (NON-NEGOTIABLE)
On EVERY exit path — convergence, max iterations, halt, error, user abort — BEFORE any cleanup or before reporting "done":
- MUST call
update_calibration_run(run_id, converged=bool, final_rms=..., iterations_run=N, error=...). Always. Even on abort. Even on error. - Report final state: RMS deviation, filters applied, alignment corrections, remaining deviations
- If not converged, suggest next steps (room treatment, sub repositioning, different recipe)
Same class of bug as feedback_calibration_cleanup.md (master gain not restored): closing-state
instrumentation gets silently skipped at the end of long sessions. Don't let it.
Step 8 — Cleanup
- Call
end_sweep_sessionto restore miniDSP source - Call
set_master_gain(0)and verify —end_sweep_sessiondoes NOT do this - Unmute everything — even if calibration failed
Important rules
- SafetyValidator is in the code. You do not need to enforce safety limits yourself —
apply_eqwill reject unsafe filters. If rejected, reduce the offending values and retry. - Prefer cuts over boosts. Cuts are always safe. Boosts are limited (frequency-dependent: +6 dB below 30 Hz, +8 dB above 30 Hz).
- Always include the 18Hz HPF in every
apply_eqcall. - Mute bass shakers before starting calibration if the config has shaker outputs.
- Unmute everything when done, even if calibration fails.
- Do not hardcode frequencies or gains. Read them from the measurement data and recipe.
- Pass simulation_verified=true to
apply_eq/apply_input_eqwhen you have just confirmed the predicted response viasimulate_eq. This allows +6 dB per iteration instead of +3 dB. - Record every run — NON-NEGOTIABLE.
save_calibration_runat start,save_calibration_iterationafter EVERY iteration of EVERY phase (no exceptions, including no-op phases),update_calibration_runon EVERY exit path BEFORE cleanup (converged, halted, errored, aborted — always). Passdevice_statefromget_device_stateto capture equipment state. Skipping any of these silently turns a successful calibration into an empty run row — the exact bug seen with runs 17/18 on 2026-04-24. - Describe every hardware action explicitly. Before each DSP write, say in plain language what you are doing and why — which inputs/outputs are involved, what values are being set, and what problem it solves. Examples:
configure_matrix: "Routing input 1 (the Denon HDMI analog input) to outputs 1, 2, and 3. This ensures all subs receive signal — the 2x4 HD default matrix can split inputs across outputs unexpectedly."set_delay: "Sub 2 (Nearfield, output 2) arrives 16.8ms earlier than Sub 1 at the mic. Adding 16.8ms delay to output 2 so both subs arrive simultaneously."set_polarity: "Sub 2 IR peak sign is +1 while Sub 1 is -1. Inverting polarity on output 2 to match Sub 1's phase."set_output_gain: "Sub 1 measured 3.4 dB quieter than Sub 2. Applying +3.4 dB gain to output 1 to level-match."