# xDarkzx/Reaper-MCP [Health: Active]

**Category:** 🎥 Multimedia Process  
**Repository:** https://github.com/xDarkzx/Reaper-MCP  
**GitHub Stars:** 113  
**Views:** 16  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/xdarkzx-reaper-mcp

## Description
AI-powered music production in REAPER through natural conversation - 173 tools across 26 modules covering transport, tracks, MIDI (insert/edit/quantize/humanize), mixing and FX (EQ, compression, sidechain, sends), tempo maps, markers/regions, ReaScript discovery/execution, and more.

## Tools
Capabilities this server exposes over MCP:

- **item_split_at_transients** — Slice an audio item at every detected transient.

        Uses REAPER's native transient detection (action 40310). Sensitivity
        is controlled by REAPER's project settings (Options → Preferences →
        Project → Item handling → Transient detection sensitivity), not
        by this tool — set it once in REAPER and it applies to every call.

        Args:
            item_index: Global 0-based item index of the source audio item.

        Returns:
            chops_created: number of new items created from the split.
            chops_total: total items now spanning the original time range.
            chops: list of `{item_index, position, length, offset_in_original_sec}`
                   in playback order. The AI uses these indices to repitch /
                   duplicate / reverse individual chops.
        
- **item_split_at_positions** — Slice an item at a list of absolute project-time positions.

        For grid-based or hand-picked chopping where transient detection
        isn't right. Pass positions in seconds; positions outside the
        item's range are silently ignored.

        Args:
            item_index: Global 0-based item index.
            positions: JSON array of absolute project-time positions
                       (seconds). Example: `"[1.5, 2.0, 2.5, 3.0]"`.

        Returns the resulting chops in playback order with their indices,
        positions, lengths, and offset relative to the original item start.
        
- **take_set_pitch** — Pitch-shift a take by N semitones (no time change).

        The core of "tune a chop to a chord tone". Pass positive values to
        shift up, negative for down. Range is roughly -60 to +60 (REAPER
        clamps extremes). Half-semitone values work (`0.5`, `-1.25`, etc.)
        for fine-tuning.

        Pitch quality depends on REAPER's per-take pitch shift mode setting
        (Item Properties → Take pitch shift mode). For vocals, "Élastique
        Pro Soloist" preserves formants well. Set it once in REAPER's
        project defaults and it applies to every chop.

        Args:
            item_index: Global 0-based item index.
            semitones: Pitch shift in semitones. Float. Default 0 (no change).
            take_index: 0-based take index, or -1 for the active take.
        
- **take_set_playrate** — Time-stretch a take by changing its playrate.

        With `preserve_pitch=True` (default), audio plays slower/faster
        without changing pitch — useful for fitting a chop to a beat grid.
        With `preserve_pitch=False`, this becomes a vinyl-style speed change
        (faster = higher pitch).

        After changing the rate, the item's visible length in the timeline
        does NOT auto-resize. Call `item_set_length` afterwards to match.

        Args:
            item_index: Global 0-based item index.
            rate: Playback rate. 1.0 = normal, 0.5 = half speed, 2.0 = double.
                  Range 0.05 to 16.0.
            preserve_pitch: Time-stretch without pitch change. Default True.
        
- **take_set_reversed** — Reverse an item's audio.

        Uses REAPER's "Item: Reverse items as new take" action — the new
        reversed take becomes the active one, the original take is kept
        (item now has 2 takes). To revert, switch back to the original
        take via `item_take_set_active`.

        Useful for: reverse-cymbal-into-downbeat fills, breath-in vocal FX,
        glitchy chop reversals (Skrillex / Flume style).

        Args:
            item_index: Global 0-based item index.
        
- **item_duplicate** — Copy an item N times at fixed spacing.

        Each copy is a full clone — same source, same take properties
        (pitch, playrate, FX, fades). Used for the Porter Robinson
        1/16 stutter, EDM build-up risers, percussive vocal repeats.

        With default spacing (0), copies are placed back-to-back at the
        original item's length. For tight stutters, pass a small spacing
        like `60/bpm/4` for 1/16-note repeats at the project's tempo.

        Args:
            item_index: Global 0-based source item index.
            count: Number of copies to make. Range 1-100.
            spacing_sec: Time between copy starts in seconds. 0 = use
                         item length (back-to-back). Default 0.

        Returns the new clones with their `item_index` so the AI can
        manipulate them (pitch, fade, etc.) directly.
        
- **analyze_chop_set** — Inspect a list of chops and classify each by duration.

        Pure-Python helper that calls `item_get_info` for each index and
        returns enriched per-chop info plus summary stats. Helps the AI
        pick which chops to use for which musical role without doing
        audio content analysis.

        Duration classes:
            hit       — < 150 ms (drum hit, consonant burst)
            staccato  — 150-400 ms (short syllable, good for stutters)
            syllable  — 400 ms-1 s (full syllable / word)
            sustain   — > 1 s (phrase / vowel held)

        Args:
            item_indices: JSON array of item indices. Up to 200 chops.
        
- **arrange_chops_to_chord_tones** — [NICHE] Retune each chop to a chord tone.

        ⚠️  WARNING: This is NOT how professional vocal chops are made.
        Per-slice chord-tone retuning smears vocal formants and sounds
        unnatural. `chop_pipeline` uses uniform transpose instead, which
        is the correct default for EDM vocal chops.

        This tool is kept for stylized experimental use (e.g., glitch /
        granular / heavily-processed chop effects where the pitched-up-
        demon-voice sound is the desired character). DO NOT use it as
        your default chop workflow.

        Walks the chops in playback order. For each chop, determines
        which chord it falls within (based on its time position) and
        picks a chord tone according to `layout`. Applies the pitch
        shift via `take_set_pitch`.

        Args:
            item_indices: JSON array of chop item indices (in playback order).
                          Typically the `chops` list returned by
                          `item_split_at_transients`.
            chord_progression: Chord names separated by commas, pipes,
                               or dashes-with-spaces. E.g.,
                               `"Cm7, Fm7, Bb7, Eb"` or `"Am - F - C - G"`.
            beats_per_chord: How many beats each chord lasts. Default 4
                             (one bar per chord at 4/4).
            bpm: Project tempo. Pass 0 to fetch from REAPER (extra round-trip).
            layout: How to pick a chord tone for each chop:
                - `follow`     — cycle root → 3rd → 5th → root → 3rd → 5th
                - `ascending`  — climb chord tones across each chord
                - `porter`     — root → 5th → octave → 5th → root (Porter Robinson)
                - `root`       — every chop = root of current chord
            source_root: Note name the source vocal is "in". Pitch shifts
                         are computed relative to this. Default `"C"`.
                         If you don't know, leave as `C`; the result will
                         still be musical relative to the chord changes,
                         just not in absolute key.
        
- **chop_pipeline** — End-to-end vocal-chop arrangement (redesigned for pro workflow).

        Produces a contiguous, key-matched, professionally-structured chop
        arrangement on a new track. Follows researched EDM vocal-chop rules:

          • CHOPS ARE CONTIGUOUS — every slot filled wall-to-wall, no
            silence gaps. Slot length is determined by the grid.
          • PITCH IS UNIFORM — the whole chop set is transposed ONCE to
            match the track key (source_key → target_key). No per-slice
            chord-tone retuning; that destroys formants and sounds bad.
          • LAYER STACKS ARE SEPARATE TRACKS — octave/fifth layers are
            duplicate child tracks at reduced volume (not overlays that
            smear the lead).
          • BAR-LEVEL VARIATION — each bar pulls a different permutation
            of source slices, so the arrangement doesn't just loop.

        REQUIREMENT for clean pitch: set REAPER's per-item pitch-shift mode
        to "élastique Pro Soloist Monophonic" (Project Settings → Media
        Item Defaults, or per-take in Item Properties). Raw pitch shift
        without formant preservation destroys vocal character. The tool
        does not auto-set this (requires REAPER user config).

        Modes:
          • `slice-preserve` (default): slice source at a 1/16 (or 1/8 /
            1/32 per style) grid, reorder slices across bars for variation,
            apply ONE uniform pitch shift to all chops.
          • `melody`: pick a central vowel offset in the source, write a
            chord-tone melody across bars (root/3rd/5th of each chord),
            capped at ±5 semitones from source. Requires
            `chord_progression`. Approximates the "one vowel + sampler +
            MIDI melody" workflow (Flume / Simpler / Quick Sampler).

        Args:
            vocal_item_index: Global 0-based index of the vocal item.
            chord_progression: Chord list (required for `melody` mode,
                               optional for `slice-preserve`). E.g.
                               `"Fm, Ab, Bb, Fm"`.
            bpm: Project tempo. 0 = fetch from REAPER.
            bars: Length in bars. Default 4, max 32.
            style: One of `future_bass`, `chillstep`, `porter`, `trap`,
                   `mura_masa`, `odesza`. Drives grid / stutter / layers.
            target_track_name: Name for the new track.
            mute_original: Mute the source vocal's track. Default True.
            source_key: Vocal's original musical key. Accepts `Am`,
                        `A minor`, `A`, `Ebm`, etc. Default `C`.
            target_key: Track key to transpose TO. Empty = use root of
                        first chord in `chord_progression`, or source_key
                        if no progression given.
            mode: `slice-preserve` (default) or `melody`. See above.
            seed: Random seed for bar permutations. 0 = nondeterministic.
        
- **stack_chop_layers** — For each chop, create overlay copies at parallel pitch intervals.

        The classic "future bass / Porter Robinson" harmonized chop stack:
        each chop becomes a 3-voice cluster — original (root) + 5th
        (`+7`) + octave (`+12`).

        Each layer is a clone placed at the EXACT same position as the
        source on the SAME track, with `take_set_pitch` applied. This
        means layers play simultaneously with the original.

        Args:
            item_indices: JSON array of chop indices to stack. Up to 50.
            intervals_semitones: JSON array of pitch shifts to add. Default
                                 `"[7, 12]"` = perfect 5th + octave (the
                                 classic stack). For unison-only set to
                                 `"[]"` (no extra layers).

        Note: stacking creates many overlapping items. Best applied to a
        SUBSET of chops (e.g., the standout chops in your sequence) rather
        than every chop, or REAPER's mixer will get crowded.
        
- **wipe_all_midi** — Wipe all MIDI items and reset composition state. Tracks can compose again after.

        Only deletes items whose active take is MIDI — audio items on the same
        tracks are left untouched. On a full wipe (tracks omitted), also clears
        all markers and regions.

        Args:
            tracks: JSON array of track indices e.g. "[0,1,2]", or empty for all.
        
- **reset_composition** — Unlock compose_arrangement without deleting MIDI. Use wipe_all_midi to also delete.
- **configure_tracks** — Batch set volume_db, pan, color, mute, solo, name on multiple tracks.

        Args:
            tracks: JSON array. Each: {"track_index":0, "volume_db":-3.0, "pan":-0.5,
                    "color":[200,90,60], ...}. Only track_index required.
                    color is [r, g, b], each 0-255 (not a {"r":..} object, not a hex string).
        
- **setup_routing** — Batch create sends with optional volume/pan.

        Args:
            sends: JSON array. Each: {"source_track":0, "dest_track":10, "volume_db":-6.0, "pan":0.0}.
        
- **add_markers_batch** — Batch add markers/regions.

        Each result entry includes both `index` and `number` - use `index`
        for any follow-up marker_delete/marker_edit/markers_apply call.
        `number` is REAPER's own display id, which it reuses once freed by
        a delete; it does not track the marker's position in the project
        and will not match `index` in a project that's had markers deleted.

        Args:
            markers: JSON array. Markers: {"position":0,"name":"Intro"}. Regions:
                     {"start":0,"end":8,"name":"V1","is_region":true}.
                     color optional: [r, g, b], each 0-255.
        
- **rewrite_cc** — Replace CC automation in a time range, leaving notes untouched.

        Write curves as explicit CC points — there are no curve templates.
        For a crescendo, insert a series of points with rising cc_value;
        the resolution is up to you (e.g. one point every half bar).

        Args:
            tracks: JSON array. Each: {"track_index":0, "ccs":[{"cc_number":1,
                    "cc_value":64, "position":0.0, "channel":0}]}. Or "all".
                    position is in seconds. Entries missing any of cc_number/
                    cc_value/position are skipped silently, so a malformed
                    array still reports success with ccs_inserted: 0 — check
                    that count rather than the success flag.
            start_time: Range start in seconds.
            end_time: Range end in seconds.
        
- **edit_section** — Clear+replace notes/CCs in a time range. For CC-only fixes, use rewrite_cc instead.

        Args:
            tracks: JSON array. Each: {"track_index":0, "notes":[...], "ccs":[...]}. Omit notes/ccs to just clear. Or "all".
            start_time: Range start in seconds.
            end_time: Range end in seconds.
            mode: "all" (default), "ccs_only", or "notes_only".
            trim_item: Shorten item to start_time (for cutting endings).
        
- **setup_fx_chain** — Add and/or configure FX — the only tool for this, for one plugin
        on one track just as much as many plugins across many tracks. There
        is no separate single-shot "add one FX" or "set one param" tool:
        a single `fx_chain` entry with one track is exactly as simple as
        those would be, and this is the only path that writes params in the
        order some plugins require — e.g. FabFilter Pro-Q 3/Pro-C 2 need a
        band's Used/Enabled flag written before its other params (Frequency,
        Gain, Shape, ...), or the write has no audible effect and silently
        looks like it didn't work.

        Params are applied in a fixed, deterministic order (not raw dict
        iteration order) specifically because some plugins (confirmed:
        FabFilter Pro-Q 3/Pro-C 2) crash if a band's Used/Enabled flag isn't
        written before its other params — this already handles that, no
        need to sequence params yourself.

        Example — add the same plugin (e.g. an EQ) to 5 different tracks in
        one call:
        ```json
        [
            {"track_index": 0, "fx_chain": [{"name": "FabFilter Pro-Q 3"}]},
            {"track_index": 1, "fx_chain": [{"name": "FabFilter Pro-Q 3"}]},
            {"track_index": 2, "fx_chain": [{"name": "FabFilter Pro-Q 3"}]},
            {"track_index": 3, "fx_chain": [{"name": "FabFilter Pro-Q 3"}]},
            {"track_index": 4, "fx_chain": [{"name": "FabFilter Pro-Q 3"}]}
        ]
        ```
        `fx_chain` can hold more than one plugin per track too — a whole
        Saturn + Pro-Q 3 + Pro-C 2 chain, with params, added to several
        tracks, is still one call.

        Example — set up Band 1 (Low Cut) and Band 2 (High Shelf, +2dB) on
        an EQ that's already track 0's fx_index 1, by index (skips the
        fuzzy name-matching lookup, marginally faster for a big batch):
        ```json
        [{"track_index": 0, "fx_chain": [
            {"fx_index": 1, "params_by_index": {
                "0": 1.0, "1": 1.0, "2": 0.218, "8": 0.25
            }},
            {"fx_index": 1, "params_by_index": {
                "13": 1.0, "14": 1.0, "15": 0.845, "16": 0.533, "21": 0.375
            }}
        ]}]
        ```
        (Same `fx_index` reused across entries is fine — each entry is an
        independent set of param writes, not a new FX add.)

        Args:
            tracks: JSON array, one object per track:
                `{"track_index": int, "fx_chain": [...]}`, where
                `track_index` is 0-based, or **-1 for the master track** —
                so a full mastering chain (EQ -> compressor -> saturation ->
                limiter, with params) can be built in one call, without
                engine_master wiping the existing chain. Each `fx_chain`
                entry is one FX to add-and/or-configure:
                - `"name": str` — add new via fuzzy match (default mode).
                  `"add_mode": "find_or_add"` reuses an existing instance of
                  that plugin instead of adding a duplicate;
                  `"add_mode": "find_only"` fails if it's not already there.
                - `"fx_index": int` — target an FX that's already in the
                  chain (from an earlier setup_fx_chain call, or already
                  present in the project) instead of adding one.
                - `"params": {name: value}` — set params by fuzzy name
                  match (e.g. `"Band 1 Frequency"`). Values 0.0-1.0
                  normalized.
                - `"params_by_index": {"<index>": value}` — same, by exact
                  0-based param index (keys are strings — JSON object keys
                  always are). Skips the per-param name lookup.
                - `"preset": str` — load a preset by name after params are
                  applied.

        Every failure is reported per-item in the response's `summary`
        (`{"error": "..."}` on that track or that one param), never a hard
        abort of the whole batch — a bad track_index, an FX name that
        doesn't resolve, or a param name/index that doesn't match anything
        each fail on their own without losing whatever else in the same
        call succeeded. Check `summary` rather than assuming a
        non-throwing call means every item landed.
        
- **setup_effect_bus** — Create effect return bus (track + FX + sends) in one call.

        Args:
            bus_name: Bus track name.
            fx_chain: JSON array of FX (same format as setup_fx_chain).
            sends_from: JSON array: [{"source_track":0, "volume_db":-6.0}].
            bus_position: Track insert position (-1 = end).
            bus_volume_db: Bus volume in dB.
            bus_color: Optional JSON color "[r,g,b]".
        
- **get_track_instruments** — List all tracks with detected VSTi names and item counts.

        Call this before any composition work so you know which track holds
        which instrument. Each track's `name` and VSTi are returned so you can
        pick the right track index for MIDI insertion.
        
- **analyze_score** — Dump current MIDI data: per-track notes, CCs, timing stats.

        Use this to inspect what's in the project before editing, or to verify
        what you just inserted.

        Args:
            tracks: JSON array of track indices e.g. [0,1,2], or "all".
            start_time: Start in seconds (default 0).
            end_time: End in seconds (-1 = project end).
        
- **compose_arrangement** — Batch-insert MIDI from shorthand or JSON — guarded for small edits only.

        Accepts either shorthand notation (e.g. `3|D3:2.5:65 E3:3.0:70|cc1:0-8:35-55`)
        or a JSON tracks array: `[{"track_index": N, "notes": [...], "ccs": [...]}]`.

        All note/CC times (shorthand durations, JSON start/end) are RAW SECONDS,
        not beats or quarter-notes, and are independent of the project's tempo —
        there is no bpm parameter to set. If you need notes aligned to bars, get
        the real tempo from `project_get_info` first and convert yourself
        (seconds = beats * 60 / bpm). This differs from `create_drum_pattern` /
        `create_chord_progression`, which take quarter-notes and convert using
        the actual project BPM automatically — don't mix up the two conventions.

        Blocked at >2 tracks or >30 total notes to prevent accidental mass inserts —
        for larger writes, call this repeatedly in smaller chunks, or use the
        batch MIDI tools directly.

        Args:
            tracks: JSON tracks array or shorthand string.
            clear_existing: True to wipe target tracks before inserting.
        
- **demo_edm_project** — Scaffold a minimal EDM test project with 7 tracks, ReaSynth on each, and 8 bars of MIDI.

        Use this to verify `engine_mix` / `engine_master` / `setup_sidechain` actually
        work end-to-end: after this call, you should immediately be able to run:

            engine_mix("melodic_dubstep")    # applies EQ/comp/reverb/sidechain
            engine_master("melodic_dubstep") # master chain on master bus
            transport_play()                  # hear it

        Tracks created: Kick Drum, Snare, Closed Hat, Sub Bass, Pad, Lead Synth, Vocal Chop.
        Each gets REAPER's stock ReaSynth (always available) + a simple 8-bar pattern.

        Args:
            clean_first: Wipe existing MIDI + markers first (default True).
            bpm: Project BPM (default 140 — matches melodic_dubstep's half-time feel).
        
- **envelope_get_points** — Read automation points from a track or FX parameter envelope.

        Track envelopes: "Volume", "Pan", "Mute", "Width", "Volume (Pre-FX)",
        "Pan (Pre-FX)", "Width (Pre-FX)".

        For FX param envelopes: pass `fx_index` and `param_index` (get these
        from `fx_get_params`). `envelope_name` is ignored when FX param is set.

        Args:
            track_index: Track.
            envelope_name: Named track envelope (ignored if fx_index+param_index set).
            fx_index: FX slot index, -1 to target track envelope instead.
            param_index: FX parameter index, -1 to target track envelope instead.
            max_results: Cap on returned points (default 2000, prevents context blow-up).
        
- **envelope_add_points** — Batch-insert automation points into an envelope.

        Point shape codes: 0=linear, 1=square, 2=slow start/end, 3=fast start,
        4=fast end, 5=bezier. Tension -1 to +1 for curve skew.

        Value units:
          - Volume track env: linear gain where 1.0 = unity (0 dB), 2.0 ≈ +6 dB,
                              0.5 ≈ -6 dB. Use 10**(db/20) to convert from dB.
          - Pan: -1.0 (hard left) to +1.0 (hard right).
          - Mute: 0.0 or 1.0.
          - FX params: 0.0-1.0 normalized (same as setup_fx_chain's params/params_by_index).

        Args:
            track_index: Track.
            points: JSON array of `{"time":s, "value":v, "shape":0, "tension":0}`.
            envelope_name: Track envelope name (ignored with FX param).
            fx_index / param_index: Target FX param envelope (-1 for track env).
            create: If the envelope doesn't exist yet, create it (default True).
        
- **envelope_clear_range** — Delete all envelope points in a time range.

        Args:
            track_index: Track.
            start_time: Range start in seconds (>= 0).
            end_time: Range end in seconds (> start_time).
            envelope_name: Track envelope name.
            fx_index / param_index: Target FX param envelope (-1 for track env).
        
- **fx_remove_batch** — Remove one FX, several, or a whole chain, across one or many tracks, in one call.

        Args:
            entries: JSON array. Each entry has track_index (-1 = master) plus
                     exactly one of:
                       {"track_index":-1, "all":true}            clear the whole chain
                       {"track_index":2, "fx_index":3}           remove one FX
                       {"track_index":2, "fx_indices":[0,3,4]}   remove several
                     Indices refer to each chain as it is when the call starts;
                     deletion order is handled for you, so entries can overlap
                     or come in any order. A bad track or index is recorded in
                     the response's errors array and does not abort the rest.
        
- **fx_get_chain** — Get FX chain for a track (names, enabled, presets, param counts).

        Args:
            track_index: 0-based track index, or -1 for the master track.
        
- **fx_get_params** — Get all parameters of an FX plugin (names, values, formatted display).

        Automatically filters out junk params (Internal, MIDI CC, unused FabFilter bands)
        to keep context size small.

        Args:
            track_index: 0-based track index, or -1 for the master track.
            fx_index: 0-based FX chain index.
            max_results: Cap on returned (post-filter) params (default 300,
                hard ceiling 2000) — junk filtering handles the common case
                (e.g. FabFilter Pro-Q's ~500 params), this is the backstop
                for any plugin with a genuinely large number of real params.
                `truncated` in the response says whether more exist.
        
- **fx_scan_params** — On-demand fallback: sweep a plugin's parameters to learn their real
        range/units/curve shape, when you're genuinely unsure and about to
        guess. NOT a general "understand this plugin" tool, and not something
        to call routinely or proactively on every plugin you touch.

        Scope: this answers "what range/units does this specific parameter
        actually use" (calibration) — nothing more. It does not explain what
        a parameter *means* or what a plugin is *for*; reasoning about a
        plugin's purpose and a vaguely-named control ("Character," "Drive")
        is something you're already equipped to do from general knowledge,
        the same way you already handle FabFilter without this tool. Reach
        for fx_scan_params only when that reasoning genuinely isn't enough —
        e.g. an obscure/freeware plugin with no clear labeling, or a
        specific parameter whose behavior turned out to be surprising (see
        docs/superpowers/specs/2026-08-06-vst-param-autoscan-design.md for
        the reasoning behind this scope).

        Caches the result by plugin name, so repeat scans of the same plugin
        (even in a different project) return instantly from the on-disk
        cache instead of touching REAPER again. Briefly writes and restores
        every parameter's value during the sweep, and can take longer on
        plugins with many parameters — another reason this is deliberately
        separate from fx_get_params rather than folded into routine reads.

        Args:
            track_index: 0-based track index, or -1 for the master track.
            fx_index: 0-based FX chain index.
        
- **fx_enable** — Enable an FX plugin.

        Args:
            track_index: 0-based track index, or -1 for the master track.
            fx_index: 0-based FX chain index.
        
- **fx_disable** — Bypass an FX plugin.

        Args:
            track_index: 0-based track index, or -1 for the master track.
            fx_index: 0-based FX chain index.
        
- **fx_show_ui** — Open FX plugin UI window.

        Args:
            track_index: 0-based track index, or -1 for the master track.
            fx_index: 0-based FX chain index.
        
- **fx_get_preset** — Get current preset name and count.

        Args:
            track_index: 0-based track index, or -1 for the master track.
            fx_index: 0-based FX chain index.
        
- **fx_set_preset** — Load preset by name. Returns the resulting preset, not the parameters.

        The name must match the plugin's own spelling. Some plugins report
        names padded to a fixed width (e.g. KORG TRINITY pads to 16 chars),
        and the padded form is what has to be passed. Take the name from
        fx_get_preset or fx_navigate_preset rather than from REAPER's
        presets/*.ini, which stores it trimmed.

        If the preset does not actually change, this raises instead of
        reporting success.

        Args:
            track_index: 0-based track index, or -1 for the master track.
            fx_index: 0-based FX chain index.
            preset_name: Preset name, exactly as the plugin reports it.
            include_params: Also return the plugin's full parameter list.
                Off by default — on large instruments that runs to tens of
                thousands of characters. Use fx_get_params when the
                parameters are what you actually want.
        
- **fx_navigate_preset** — Step to next/previous preset.

        Args:
            track_index: 0-based track index, or -1 for the master track.
            fx_index: 0-based FX chain index.
            direction: 1=next, -1=previous.
        
- **fx_get_instrument** — Find VSTi instrument on track. Returns index + params, or -1 if none.

        Args:
            track_index: 0-based track index, or -1 for the master track.
        
- **fx_move** — Move FX to different position in chain.

        Args:
            track_index: 0-based track index, or -1 for the master track.
            fx_index: Current FX index.
            new_index: Target position.
        
- **fx_rename** — Rename an FX instance's display label (cosmetic — plugin unchanged).

        Use to tag FX you've added yourself so later cleanup can find them
        without affecting other FX on the track. The mix engine uses this
        internally to prefix all its additions with "[MIX] ".

        Args:
            track_index: 0-based track index, or -1 for the master track.
            fx_index: FX slot within the chain.
            new_name: New display name. Max 1000 characters.

        Requires REAPER 6.37+ (for TrackFX_SetNamedConfigParm with
        "renamed_name"). Older REAPER versions will error out cleanly.
        
- **fx_get_pin_mappings** — Read-only inspection of an FX plugin's audio input and output pin mappings.

        Returns the track channels wired to each FX pin via TrackFX_GetPinMappings.
        Use this to verify existing sidechain routing without modifying anything —
        e.g. confirm that Pro-Q 4's sidechain input pins are receiving channels 3/4
        before running automated parameter optimisation.

        Each pin entry includes:
          - ``pin`` — 0-based pin index
          - ``track_channels`` — list of 1-based track channel numbers connected
          - ``low32_raw`` / ``high32_raw`` — raw bitmasks for full lossless round-trips

        Requires REAPER >= 6.0 (TrackFX_GetPinMappings).

        Args:
            track_index: 0-based track index, or -1 for the master track.
            fx_index: 0-based FX chain index.
        
- **fx_list_installed** — List every FX plugin installed in REAPER, grouped by category.

        Uses REAPER's EnumInstalledFX API. Returns:
          - `all_installed`: raw list of plugin names — capped at 150 unless
            `full_list=True` (power users can have 500+ installed; the
            `best_*` picks below already cover the decision the AI actually
            needs to make, so the raw list is capped by default to avoid
            dumping the whole catalog into context on every call)
          - `best_eq` / `best_compressor` / `best_reverb` / `best_limiter` /
            `best_deesser` / `best_gate` / `best_saturator` / `best_multiband` /
            `best_stereo`: highest-ranked plugin per category that the user has
          - `racks_detected`: any rack-style plugins (StudioRack, PatchWork, Lion,
            Multipass, Snap Heap) — the MCP does NOT configure modules inside
            racks, so avoid routing auto-mixing through them
          - `user_overrides`: current contents of the preferences file

        Use this BEFORE running mix pipelines to know what the user has.
        If the user has premium plugins (FabFilter, Waves, iZotope, Valhalla,
        Softube, etc.), prefer those over REAPER stock.

        Args:
            category: Optional — filter output to a single category:
                      "eq", "compressor", "limiter", "reverb", "deesser",
                      "gate", "saturator", "multiband", "stereo".
                      Empty returns full inventory.
            full_list: Return the complete `all_installed` list uncapped.
                       Only needed if you're hunting for something the
                       built-in category rankings don't cover.
        
- **set_fx_preferences** — Save user FX preferences — per-category plugin overrides.

        Stored at `%APPDATA%/reaper_mcp/fx_prefs.json` (Windows) or
        `~/.reaper_mcp/fx_prefs.json` (macOS/Linux). The mix and master
        pipelines apply a preference for eq, compressor, reverb and limiter
        when the engine has a parameter map for that plugin (REAPER's own
        ReaEQ / ReaComp / ReaVerbate / ReaLimit, and FabFilter Pro-Q 3 /
        Pro-C 2 / Pro-R / Pro-L 2). The response's `engine_support` says which
        of your preferences the engine can use; for any it can't, the engine
        uses the best supported plugin for that category and reports it in
        the mix result's `preferences_ignored`.

        Args:
            preferences: JSON object mapping categories to plugin names, e.g.
                `{"eq": "FabFilter Pro-Q 3", "compressor": "Waves SSL G-Master Buss Compressor",
                  "reverb": "Valhalla VintageVerb", "limiter": "FabFilter Pro-L 2"}`

        Categories: eq, compressor, limiter, reverb, deesser, gate, saturator,
                    multiband, stereo. Plugin names must match what REAPER
                    reports (check `fx_list_installed` first).
        
- **item_get_all** — Get all media items. Filter by track or -1 for all.

        Each item includes `source_file` (full path) and `source_filename`
        (basename only) for audio items — the actual dragged-in media file
        name, e.g. from a Splice/sample-pack import. Sample vendors commonly
        embed BPM/key in the filename itself ("Karra_Vocal_Loop_120bpm_Cmin.wav"),
        which the take's editable `name` field does not reliably preserve.
        Both are "" for MIDI items or items with no source.

        Args:
            track_index: Track filter (-1=all).
            max_results: Max items to return (default 200, hard ceiling 2000).
                         A chop-heavy project can have hundreds of items —
                         narrow with track_index if the result is truncated.
        
- **item_get_info** — Get detailed info for one item.

        Includes `source_file`/`source_filename` for audio items — see
        `item_get_all` for what these are and why they matter.

        Args:
            item_index: Item index.
        
- **item_select** — Select/deselect an item.

        Args:
            item_index: Item index.
            selected: True=select.
            exclusive: Deselect others first.
        
- **item_split** — Split item at position.

        Args:
            item_index: Item index.
            position: Split point in seconds.
        
- **item_delete** — Delete an item. Prefer items_apply for multiple.

        Args:
            item_index: Item index.
        
- **item_move** — Move item to new position. Prefer items_apply for multiple.

        Args:
            item_index: Item index.
            new_position: New start in seconds.
        
- **item_set_length** — Set item length. Prefer items_apply for multiple.

        Args:
            item_index: Item index.
            length: Length in seconds.
        
- **item_set_volume** — Set item volume in dB. Prefer items_apply for multiple.

        Args:
            item_index: Item index.
            volume_db: dB (0=unity).
        
- **item_set_mute** — Mute/unmute item. Prefer items_apply for multiple.

        Args:
            item_index: Item index.
            mute: True=mute.
        
- **item_set_fade** — Set fade in/out. -1=unchanged. Prefer items_apply for multiple.

        Args:
            item_index: Item index.
            fade_in: Seconds, 0 or more (-1=keep, max 60s).
            fade_out: Seconds, 0 or more (-1=keep, max 60s).
        
- **item_insert_media** — Insert audio/MIDI file into track.

        Args:
            track_index: Target track index.
            path: Absolute file path.
            position: Insert position in seconds.
        
- **item_create_midi** — Create an empty MIDI item on a track. Returns the item's global index.

        Args:
            track_index: 0-based track index.
            position: Item start time in seconds (>= 0).
            length: Item length in seconds (> 0).
        
- **item_move_to_track** — Move item to different track.

        Args:
            item_index: Item index.
            dest_track_index: Destination track index.
        
- **items_apply** — Batch set position/length/volume_db/mute/fade_in/fade_out, or delete, across many items in one call.

        Args:
            entries: JSON array. Each: {"item_index":0, "volume_db":-3.0, "fade_in":0.05}.
                     Or {"item_index":5, "delete":true}. Only item_index required.
                     Non-delete changes apply first (in the order given); deletes apply
                     last, in descending item_index order, regardless of input order —
                     this avoids a delete shifting the indices of items processed later
                     in the same batch. A bad item_index is recorded in the response's
                     errors array and does not abort the rest of the batch.
        
- **scan_audio_folder** — Walk a folder for audio loops and parse metadata from filenames.

        Returns every audio file (.wav, .mp3, .flac, .aif, .aiff, .ogg, .m4a)
        with BPM, musical key, and role (kick / bass / pad / lead / fx / …)
        extracted from the filename. Plus a `summary` of distributions so
        the AI can quickly decide on a target BPM / key before picking loops.

        For a large library (thousands of files), don't scan the whole
        thing blindly: call `list_audio_subfolders` first to see its shape,
        point `path` at the specific subfolder that matters, and use the
        filters below to narrow further.

        Args:
            path: Absolute folder path (e.g., `D:/Music Production/Chillstep Express`).
            recursive: Walk subfolders too. Default True.
            max_files: With no filters, stop after this many files visited.
                With any filter below active, stop after this many MATCHES
                instead (the walk looks past non-matching files to find
                them, up to an internal safety ceiling). Default 500; max 5000.
            query: Space-separated terms, ALL must match (AND) against the
                full path, case-insensitive — covers genre/style words
                ("techno") and specific naming ("laugh") since these
                usually appear literally in folder or file names.
            min_bpm: Only files whose parsed BPM is >= this. 0 = no filter.
            max_bpm: Only files whose parsed BPM is <= this. 0 = no filter.
            role: Exact match (case-insensitive) against the parsed `role`
                (kick/snare/hat/ride/perc/bass/pad/lead/vocal/fx/drums).
                Empty = no filter.
            key: Root note + optional accidental, e.g. "D", "F#", "Bb".
                Matches any quality (major/minor) unless you specify one
                ("Dm"). Empty = no filter.

        Returns a structure with:
          - folder: resolved absolute path
          - total_files: how many matched
          - truncated: True if max_files hit before end
          - summary: BPM / key / role distributions + parse-failure counts
          - loops: list of { path, filename, duration_sec, size_mb,
                             parsed: { bpm, key, role } }
          - hint: a one-line suggestion for the AI's next call

        Files without parseable metadata are still listed when no filter
        needs that field; a min_bpm/max_bpm/role/key filter excludes files
        where that specific field didn't parse. Pair with
        `transport_set_bpm` + `load_loops` to turn the selection into a
        working REAPER session.
        
- **list_audio_subfolders** — List the subfolder structure under a folder — fast, no per-file
        metadata work (no duration/BPM/key parsing, no size stat), just
        directory names and a cheap audio-file count per folder. Use this
        BEFORE scan_audio_folder on a large library (thousands of files):
        see the shape of it first (genre/category folders are almost
        always real subfolders — "Footsteps/", "Ambience/Wind/",
        "Weapons/Gunshots/"), then point scan_audio_folder's `path` at the
        specific subfolder that matters instead of scanning everything.

        Args:
            path: Absolute folder path to list under.
            max_depth: How many levels deep to descend. 1 = immediate
                children of `path` only. Default 2.
        
- **detect_common_bpm** — Given a JSON array of file paths, return the most common BPM
        parsed from their filenames.

        Useful after `scan_audio_folder` when the AI has narrowed down a set
        of candidate loops and wants to confirm they agree on tempo before
        setting the project BPM.

        Args:
            file_paths: JSON array of absolute paths.

        Returns:
            detected_bpm: the most common value (int), or None if none found.
            bpm_votes: full distribution of BPMs across the set.
            confidence: fraction of files that agreed with the winner.
            hint: a one-line next-step suggestion.
        
- **load_loops** — Batch-load audio loops into REAPER, auto-creating tracks by name.

        For each entry in the JSON array, find the named track (or create
        it if missing) and insert the audio file at the given position.
        Optionally sets the project BPM first so the loops align with the
        grid.

        Args:
            loops: JSON array of entries. Each entry is an object with:
                - track_name (required): name of the destination track.
                  Case-sensitive match against existing tracks; if no match,
                  a new track is created with this name.
                - file_path (required): absolute path to the audio file.
                - position_sec (optional, default 0.0): start position in
                  seconds within the project.
            project_bpm: If > 0, call transport_set_bpm before inserting
                media so the project's tempo grid is correct.

        Returns a summary with tracks created vs reused, loops loaded, and
        per-entry errors (missing files, invalid entries) so the AI can
        recover without blowing up the whole batch.
        
- **marker_get_all** — Get all markers and regions. For just the region list plus a cheap
        project overview, prefer project_get_overview() instead.

        Args:
            max_results: Cap on returned markers/regions (default 500, hard
                ceiling 5000, prevents context blow-up on a marker-heavy
                project). `truncated` in the response says whether more exist.
        
- **marker_add** — Add marker. Prefer add_markers_batch for multiple.

        Returns both `marker_index` and `marker_number` - use `marker_index`
        for any follow-up marker_delete/marker_edit/markers_apply call.
        `marker_number` is REAPER's own display id, which it reuses once
        freed by a delete, so it will not match `marker_index` in a project
        that's had markers deleted.

        Args:
            position: Seconds.
            name: Label.
            color_r: Red 0-255.
            color_g: Green 0-255.
            color_b: Blue 0-255.
        
- **marker_add_region** — Add region. Prefer add_markers_batch for multiple.

        Returns both `marker_index` and `region_number` - use `marker_index`
        for any follow-up marker_delete/marker_edit/markers_apply call.
        `region_number` is REAPER's own display id, which it reuses once
        freed by a delete, so it will not match `marker_index` in a project
        that's had regions deleted.

        Args:
            start: Start seconds.
            end: End seconds.
            name: Label.
            color_r: Red 0-255.
            color_g: Green 0-255.
            color_b: Blue 0-255.
        
- **marker_delete** — Delete marker/region. Prefer markers_apply for multiple.

        Args:
            marker_index: Index from marker_get_all.
        
- **marker_edit** — Edit marker/region. Prefer markers_apply for multiple, or to also edit a region's end/color.

        Args:
            marker_index: Marker index.
            position: New seconds, must be >= 0, or -1 to keep current position.
            name: New name (None=keep).
        
- **marker_go_to** — Move cursor to marker.

        Args:
            marker_number: 1-based marker number.
        
- **markers_apply** — Batch edit (name/position/start/end/color) or delete markers and regions in one call.

        Args:
            entries: JSON array. Each: {"marker_index":0, "name":"Line 12"}.
                     Region bounds: {"marker_index":5, "start":10.0, "end":14.5}.
                     Point marker position: {"marker_index":2, "position":8.0}.
                     Color: {"marker_index":7, "color":[200,90,60]} (0-255 each).
                     Delete: {"marker_index":9, "delete":true}. Only marker_index required.
                     start/end only apply to regions, position only to point markers — a
                     mismatched field (e.g. start/end on a point marker) is silently
                     ignored, not an error. Non-delete changes apply first (in the order
                     given); deletes apply last, in descending marker_index order,
                     regardless of input order — this avoids a delete shifting the
                     indices of markers processed later in the same batch. A bad
                     marker_index is recorded in the response's errors array and does
                     not abort the rest of the batch.
        
- **midi_insert_note** — Insert single note into existing item. For composing use compose_arrangement.

        Args:
            item_index: MIDI item index.
            channel: 0-15.
            pitch: 0-127 (60=C4).
            velocity: 1-127.
            start_position: Start seconds.
            end_position: End seconds.
        
- **midi_insert_notes_batch** — Batch-insert notes into a MIDI item. Primary tool for writing MIDI.

        Args:
            track_index: 0-based track index.
            item_index: MIDI item index on that track.
            notes: JSON array of `{"pitch":60, "velocity":100, "start":0.0, "end":0.5, "channel":0}`.
        
- **midi_get_notes** — Get notes in a MIDI item (capped at max_results to limit context size).

        Args:
            item_index: MIDI item index.
            max_results: Max notes to return (default 500, hard ceiling 10000).
                         Use midi_count_events for total count first if unsure.
        
- **midi_set_note** — Edit existing note. -1 keeps current value.

        Args:
            item_index: MIDI item index.
            note_index: Note index in item.
            pitch: 0-127 (-1=keep).
            velocity: 1-127 (-1=keep).
            start_position: Seconds (-1=keep).
            end_position: Seconds (-1=keep).
            channel: 0-15 (-1=keep).
        
- **midi_delete_note** — Delete a note.

        Args:
            item_index: MIDI item index.
            note_index: Note index.
        
- **midi_select_notes** — Select/deselect all notes in item.

        Args:
            item_index: MIDI item index.
            select_all: True=select, False=deselect.
        
- **midi_delete_all_notes** — Clear all notes from a MIDI item.

        Args:
            item_index: MIDI item index.
        
- **midi_insert_program_change** — Insert a MIDI Program Change, optionally preceded by Bank Select.

        This is how emulations of classic hardware switch sounds. Their
        program list is often inert through the FX preset selector while
        Program Change works — check with midi_list_programs which programs
        a track offers.

        Bank Select (CC 0 / CC 32) is written before the Program Change when
        given, one tick apart so the order cannot be reordered away. Omit
        both for instruments with a single bank.

        Args:
            track_index: 0-based track index.
            item_index: MIDI item index.
            channel: 0-15, 0-based (REAPER's UI shows 1-16).
            program: 0-127, 0-based (the UI usually shows 1-128).
            position: Seconds. Place it before the notes it should affect.
            bank_msb: Optional Bank Select MSB (CC 0), 0-127.
            bank_lsb: Optional Bank Select LSB (CC 32), 0-127.
        
- **midi_list_programs** — List the MIDI program names a track's instrument exposes.

        Returns `supplier` (the plugin providing the list, null if none) and
        `programs` as {program, name} pairs, program numbers 0-based.

        Names come back exactly as the plugin reports them, including any
        fixed-width padding, so they can be passed straight to fx_set_preset
        — unlike the names in REAPER's presets/*.ini, which are stored
        trimmed and will be rejected.

        Whether a plugin honors a Program Change is a separate question from
        whether it publishes names here; some publish a full list and still
        only respond to the preset selector, or the other way round.

        Args:
            track_index: 0-based track index.
        
- **midi_insert_cc** — Insert single CC event. For whole curves use the `ccs` array of rewrite_cc or compose_arrangement.

        Args:
            track_index: 0-based track index.
            item_index: MIDI item index.
            channel: 0-15.
            cc_number: 0-127.
            cc_value: 0-127.
            position: Seconds.
        
- **midi_delete_cc** — Delete a CC event.

        Args:
            item_index: MIDI item index.
            cc_index: CC event index.
        
- **midi_get_note_names** — Get MIDI note number to name mapping (C4=60, etc.).
- **midi_count_events** — Count notes, CCs, and sysex events in item.

        Args:
            item_index: MIDI item index.
        
- **midi_sort** — Sort MIDI events by time.

        Args:
            item_index: MIDI item index.
        
- **midi_set_item_extents** — Set MIDI item boundaries in quarter notes.

        Args:
            item_index: MIDI item index.
            start_qn: Start in quarter notes.
            end_qn: End in quarter notes.
        
- **engine_fix_mix** — Rescue a muddy / harsh / unbalanced mix with a single call.

        Applies corrective EQ per track based on what the track sounds like
        (bass / vocal / lead / drum / other), classified by name (and by role
        if `style` is a known v2 catalog style).

        Each track gets:
          - HP filter at 80-120 Hz (except bass-family, which keeps low end)
          - Narrow cut at 250 Hz (tames low-mid mud)
          - Narrow cut at 3 kHz for vocals/leads (tames harshness)
          - High-shelf air boost at 12 kHz for vocals/leads/perc

        Plus optional master chain: bus glue comp + brick-wall limiter at -1 dB.

        Call this when a mix sounds "stuffy" or "harsh" and you want a quick
        corrective pass before fine-tuning. Doesn't replace `engine_mix` — use
        this to fix something already mixed (poorly) by someone else.

        Args:
            style: Optional v2 catalog style (e.g. melodic_dubstep) for better
                   track classification via aliases. Empty = keyword fallback.
            include_master: Also apply the emergency master chain (default True).
        
- **engine_master** — Apply a professional mastering chain to the master bus for the given style.

        Chain applied (in order): HP 25Hz → bus glue comp → tonal shelf EQ →
        stereo width → brick-wall limiter. Targets per-style LUFS and true-peak ceiling.

        Auto-detects FabFilter Pro-L 2 / Pro-C 2 / Pro-Q 3; falls back to REAPER stock
        (ReaLimit / ReaComp / ReaEQ).

        Supported styles: melodic_dubstep, big_room, future_bass, future_house, deep_house,
        tech_house, progressive_house, dubstep, trap, drum_and_bass, trance, modern_pop,
        dance_pop, indie_pop, rnb_pop, alt_rock, classic_rock, pop_rock, hard_rock, punk,
        post_rock, synthwave, lofi, ambient, hiphop, swing_jazz, jazz_fusion, latin_jazz,
        classical_chamber, cinematic_trailer, ambient_orchestral, classic_funk, motown_soul,
        neo_soul, disco_funk (35 total). Any other name falls through to the legacy
        per-instrument orchestral master chain.

        Args:
            style: Style name from the catalog (required).
            clean: Remove previously added master FX before applying (default True).
        
- **engine_mix** — One-click professional mix pipeline. Applies volume staging, pan, EQ, compression, reverb buses, and sidechain.

        Two paths depending on the style:
          - **v2 catalog** (EDM / Rock / Pop / Electronic / Jazz / Orchestral /
            Funk-Soul — 35 styles): resolves live track NAMES to instrument
            roles via alias matching (e.g. a track named "Kick" → role "kick").
            Applies per-role EQ/comp/sends and profile-defined sidechain
            relationships. Rename your tracks to include role keywords like
            "kick", "snare", "sub", "pad", "lead", "vocal" — or for the
            orchestral v2 styles, consolidated section names like "strings",
            "brass", "woodwinds", "choir".
          - **Legacy orchestral**: for a full per-instrument multi-mic template
            (separate Violin 1 / Violin 2 / Viola / Cello / individual winds &
            brass tracks) — matches track VSTi names against the BBC/Spitfire
            instrument map with per-instrument-tuned EQ/comp, not just one
            curve per section. Finer-grained than the v2 orchestral styles.
            Triggered by any style name NOT in the v2 catalog (empty string
            works).

        Auto-detects FabFilter (Pro-Q 3, Pro-C 2, Pro-R) or falls back to REAPER
        stock (ReaEQ, ReaComp, ReaVerbate).

        Args:
            style: Style name. For v2: melodic_dubstep, big_room, future_bass, modern_pop,
                   alt_rock, swing_jazz, classical_chamber, classic_funk, etc. (35 total).
                   Empty, or any name not in the v2 catalog, → legacy per-instrument path.
            clean: Remove existing mix FX before applying (default True).
        
- **create_drum_pattern** — Create a drum pattern from step-sequencer shorthand.

        Each line of `pattern` is one drum lane. Characters map to GM drums:
        `k`=kick, `s`=snare, `h`=hi-hat closed, `o`=hi-hat open, `c`=crash,
        `r`=ride, `t`=mid tom, `l`=low tom, `i`=high tom, `p`=clap, `b`=rimshot.
        `.` or space = rest. Any unknown character is ignored.

        Example — standard 16-step rock beat:

            k...k...k...k...
            ....s.......s...
            h.h.h.h.h.h.h.h.

        Defaults: 16 steps per bar = 1/16-note grid. `channel=9` = GM drum channel.
        Pass `item_index=-1` to auto-create a new MIDI item positioned in the
        project at `start_qn` (e.g. bar 5 in 4/4 = start_qn=16.0). Building a
        multi-section arrangement on one track? Call this once per section
        with `item_index=-1` each time and increasing `start_qn` — each call
        creates its own non-overlapping item at the right place, it does NOT
        append to a previous item.

        Returns the item index and number of notes inserted.
        
- **create_chord_progression** — Insert a chord progression as voiced MIDI notes.

        Accepts chords separated by commas, pipes, dashes, or newlines:
        `"Cm7, Fm7, Bb7, Eb"` or `"Am - F - C - G"` or `"Dm | G | Em | A"`.

        Supported qualities: maj, m/min, dim, dim7, aug, sus2, sus4,
        6, m6, 7, m7, maj7, add9, 9, m9, maj9, 11, m11, 13, m13.

        `base_octave=4` places C as MIDI 60 (middle C). Each chord occupies
        `chord_duration_qn` quarter notes (default 4 QN = one bar at 4/4).

        Pass `item_index=-1` to auto-create a MIDI item positioned in the
        project at `start_qn` (e.g. bar 5 in 4/4 = start_qn=16.0). Building a
        multi-section arrangement on one track? Call this once per section
        with `item_index=-1` each time and increasing `start_qn` — each call
        creates its own non-overlapping item at the right place, it does NOT
        append to a previous item.

        Returns the item index, number of chords placed, and any that failed to parse.
        
- **setup_parallel_compression** — Create a NY-style parallel compression bus and route source tracks to it.

        Classic use: crush drums or vocals with a heavy compressor on a parallel
        bus, then blend the crushed signal back in subtly with the dry. Adds
        perceived density and power without destroying transients.

        Args:
            source_tracks: JSON array of track indices to send to the parallel bus, e.g. "[0,1,2]".
            bus_name: Display name for the created bus track.
            send_db: Send level from each source to the bus (0 = unity).
            return_db: Bus output (return) level. -8 is a typical subtle blend.
            threshold_db: Compressor threshold. -24 to -30 for heavy crush.
            ratio: Compression ratio. 10:1 or higher for that "smashed" sound.
            attack_ms: Fast (1-3 ms) for tight pumping.
            release_ms: Medium (80-120 ms) typical.
        
- **setup_drum_bus** — Group drum tracks into a dedicated bus with a glue compressor.

        This is a lighter-touch version of parallel compression — the bus is a
        full sub-mix of the drums, with gentle comp gluing them together rather
        than crushing them. Use this to control the drum group's overall level
        and apply shared processing (additional EQ, saturation) above it.

        Args:
            source_tracks: JSON array of drum track indices, e.g. "[0,1,2,3]".
            bus_name: Display name for the drum bus.
            return_db: Bus output level (0 = unity, no change vs pre-bus).
            glue_threshold_db: Glue comp threshold (-10 to -14 typical).
            glue_ratio: Glue comp ratio (1.5-2.5 for gentle glue).
        
- **setup_vocal_chain** — One-call vocal-chain setup: HP + compressor + tonal EQ + plate reverb send.

        Creates (or reuses) a dedicated plate reverb bus and sends the vocal
        track to it. Designed to give a polished broadcast-ready vocal with one call.

        Chain applied in order on the vocal track:
          1. HP filter at `hp_freq`
          2. Mud cut: -2dB at 250Hz, Q=1.8
          3. Presence boost: +1.5dB at 4kHz, Q=1.2
          4. Harsh cut: -1.5dB at 3kHz, Q=3 (de-harsh)
          5. Air lift: +1.5dB high-shelf at 12kHz
          6. Compressor (3:1, -16dB threshold, vocal character)
        Plus a send to a plate reverb bus at `plate_send_db`.

        Args:
            track_index: Track to process.
            hp_freq: High-pass cutoff in Hz.
            comp_threshold_db / comp_ratio: Compression settings.
            air_boost_db / mud_cut_db / harsh_cut_db: EQ amounts in dB.
            plate_bus_name: Bus track name (reused if exists).
            plate_send_db: Send level from vocal to the plate bus.
        
- **bounce_stems** — Render the given tracks as individual audio stems (in-project).

        Selects the tracks, then invokes REAPER's "Render tracks to stereo stem
        tracks" action (command ID 40892). Resulting stems are added to the
        project as new tracks.

        **IMPORTANT SIDE EFFECTS (undocumented by REAPER, confirmed by behavior):**
          - The source tracks are AUTOMATICALLY MUTED after rendering so you
            can hear the stems instead of double-playback. If you need to
            compare or continue editing the originals, call `track_set_mute(False)`
            on them after this finishes.
          - New stem tracks are inserted below the source tracks.
          - Works with pre-fader + post-FX rendering (standard REAPER behavior).

        Requires REAPER 5.x or newer. For export to audio files on disk, use
        `project_export_audio` instead — this tool is for in-project stemming.

        Args:
            track_indices: JSON array of track indices to stem, e.g. "[0,1,2]".
            output_dir: Reserved (not currently used — in-project stemming).
            format: Reserved (not currently used).
        
- **project_get_info** — Get project info (name, BPM, time sig, tracks, length, markers, render settings).

        For post-production work, prefer project_get_overview() instead —
        one call for this plus the region list, change_count, and a
        selection summary, versus composing several calls yourself.

        `path` is the recording/media directory (exists even for a brand
        new, never-saved project). `file_path` is the actual .rpp project
        file's path — empty string if this project has never been saved.
        
- **project_get_change_count** — Cheap check for whether the project has changed since you last looked.

        Returns a monotonically increasing counter that bumps on any edit.
        Compare against a value you saved earlier to decide whether you need
        to re-fetch heavier data (track_get_all, item_get_all) instead of
        blindly re-querying every turn.
        
- **project_get_overview** — One cheap call for post-production awareness: counts, region list, change_count, and a selection summary.

        Bundles what project_get_info + marker_get_all + project_get_change_count +
        selection_get_time + selection_get_selected_tracks + selection_get_selected_items
        would otherwise take 6 separate calls to assemble. The selection summary
        returns indices and counts only (not full track/item detail) to stay
        genuinely lightweight — call selection_get_selected_tracks/items directly
        if you need full detail on what's currently selected.
        
- **project_new** — Create a new empty REAPER project. Returns the new project info.
- **project_open** — Open .rpp project file.

        Args:
            path: Absolute path to .rpp file.
        
- **project_save** — Save the current project. Returns project info confirming the save.
- **project_save_as** — Save project to new path. This project's active file becomes
        `path` going forward — subsequent project_save calls target it,
        not the original file. Use project_backup instead if you want a
        snapshot copy without switching your active file.

        Args:
            path: Absolute .rpp path.
        
- **project_backup** — Save a snapshot copy to `path` WITHOUT changing this project's
        active file — unlike project_save_as, your next project_save still
        targets the original file. Use this before a risky/destructive
        change (wiping MIDI, deleting tracks, clean=True mix passes) to
        leave a recoverable copy of what existed beforehand.

        Args:
            path: Absolute .rpp path for the backup copy.
        
- **project_export_audio** — Render project to audio file(s) on disk.

        Args:
            path: Output file path. For source="master" this is the exact
                output file. For source="stems" only its directory is used —
                each stem's filename comes from `pattern` instead, since
                stems produce multiple files, not one.
            format: wav, mp3, ogg, flac, or aiff.
            source: "master" (default) renders one mixed-down file.
                "stems" renders one file per track in `track_indices`.
            track_indices: JSON array of track indices to render as stems,
                e.g. "[0,1,2]". Required when source="stems", ignored otherwise.
            pattern: Filename pattern for stems, e.g. "$track" (the default
                when source="stems" and pattern isn't given). Ignored for
                source="master", which uses path's own filename instead.
        
- **project_undo** — Undo the last step, or the last several steps in one call.

        Every change made through these tools is its own named undo step
        ("MCP: <tool>"), and a batch call is one step. A tool that runs many
        commands, like engine_mix, leaves one step per command, labelled
        "MCP: <tool>#<run> > <command>". Undo just the last 2 or 3 of them
        with `steps`, or use project_undo_group to undo the whole run.

        Args:
            steps: How many steps to undo, 1-100 (default 1). Stops early if
                   the undo history runs out. The response lists every step
                   undone, most recent first.
        
- **project_undo_group** — Undo every step of the most recent multi-command tool run.

        Use it to take back everything a tool like engine_mix or chop_pipeline
        just did, without counting its steps. It only works when the last undo
        step came from such a run (its name looks like "MCP: engine_mix#7 >
        setup_fx_chain"); otherwise it changes nothing and says to use
        project_undo. Steps from earlier runs are left alone.
        
- **project_redo** — Redo the last undone step, or the last several in one call.

        Args:
            steps: How many steps to redo, 1-100 (default 1).
        
- **project_get_notes** — Get the project notes/description text.
- **project_set_notes** — Set the project notes/description text.

        Args:
            notes: The text to set as project notes (max 100 KB).
        
- **project_get_metadata** — Get project render metadata (title, author, album, comment, etc.).

        These are the metadata fields REAPER embeds into rendered files
        (ID3/BWF/Vorbis/APE tags) and substitutes into render-filename
        templates ($title, $artist, ...). Reachable in the GUI via the
        Render dialog's Metadata section.

        Note: REAPER's API exposes WHICH fields are set (the tag list), but
        not their VALUES — values are write-only via GetSetProjectInfo_String
        (verified on REAPER 7.78). The returned `fields_set` maps each known
        logical field to true when any of its tags is present; `tags_present`
        gives the raw tag list (e.g. ["ID3:TIT2", "VORBIS:TITLE"]).
        
- **project_set_metadata** — Set project render metadata fields. Non-empty fields are written.

        Each field is written to every tag format REAPER can embed on render
        (ID3 for mp3/wav-bwf, VORBIS for flac/ogg, APE for mpc/wv) so the
        value survives regardless of the render format chosen later. Empty
        strings are ignored and leave any existing value for that field
        untouched. Setting a field again overwrites its previous value.

        Args:
            title: Track / project title.
            author: Artist / author.
            album: Album name.
            comment: Free-form comment.
            genre: Genre.
            year: Year / date.
            track_number: Track number.
            composer: Composer.
            isrc: ISRC code.
            copyright: Copyright string.
        
- **project_get_notes_info** — Get the Title and Author fields of the Project Settings -> Notes tab.

        These are the two single-line fields at the top of REAPER's Project
        Settings -> Notes tab — distinct from both the notes free-text area
        (project_get_notes / GetSetProjectNotes) and the render metadata
        (project_get_metadata, which feeds file tags and filename templates).

        Unlike render metadata, both fields return their actual stored values
        (they are not write-only).
        
- **project_set_notes_info** — Set the Title and Author fields of the Project Settings -> Notes tab.

        Non-empty fields are written; empty strings are ignored and leave any
        existing value untouched. At least one field must be non-empty.

        Args:
            title: Project title (stored via the PROJECT_TITLE descriptor).
            author: Project author (stored via PROJECT_AUTHOR; this is the same
                value GetSetProjectAuthor() reads and writes).
        
- **project_set_grid** — Set grid division (1.0=quarter, 0.5=eighth, 0.25=sixteenth).

        Args:
            grid_division: Grid size in quarter notes.
        
- **midi_quantize** — Quantize notes in a MIDI item to a time grid.

        Grid math: for 120 BPM, a 16th note = 0.125s, 8th = 0.25s, quarter = 0.5s.
        At tempo T: grid_sec = (60 / T) * (4 / divisor). For 140 BPM 16ths:
        (60/140) * (4/16) = 0.107s.

        Args:
            item_index: Global item index of the MIDI item.
            grid_seconds: Grid spacing in seconds (see above).
            strength: 0.0 (no quantize) to 1.0 (hard snap). 0.5 = pull halfway.
        
- **midi_humanize** — Add random timing + velocity jitter to all notes in a MIDI item.

        Makes programmed MIDI feel less mechanical. Typical values:
          - Strings / pads: timing_ms=20, velocity=10 (subtle)
          - Drums: timing_ms=8, velocity=15 (tighter timing, wider dynamics)
          - Piano: timing_ms=12, velocity=12

        Args:
            item_index: Global item index of the MIDI item.
            timing_ms: Max random shift in milliseconds (each note ±timing_ms). 0 = off.
            velocity_amount: Max random velocity offset (each note ±amount). 0 = off.
        
- **project_set_ripple_mode** — Set REAPER's ripple edit mode.

        In ripple mode, editing one item shifts everything after it (per-track
        or across all tracks). Critical for dialogue/podcast editing.

        Args:
            mode: 0=off, 1=per-track ripple, 2=all-tracks ripple.
        
- **script_list** — List ReaScripts (.lua/.eel only) found in REAPER's own Scripts folder.

        Never scans anywhere else — this is hard-coded to REAPER's Scripts
        folder, not a caller-supplied path. Returns each script's path
        (relative to the Scripts folder — pass this straight to script_run)
        and description (parsed from an @description/@about header comment
        if present, empty string otherwise). Capped at 300 results; use
        filter (case-insensitive substring match against path only — not
        description) to narrow a larger install.

        filter matches path, not description: description requires opening
        and reading the file, which only happens for scripts that already
        matched by path. Installs with large community script packs (a few
        thousand .lua files) would otherwise take minutes to filter. If you
        can't find a script by keyword, its path may not contain that
        keyword even though its description does — try a shorter/different
        substring or browse by folder instead.

        Args:
            filter: Optional case-insensitive substring filter (matches path only).
        
- **script_run** — Run a ReaScript from REAPER's own Scripts folder and optionally read back a result.

        script_path must be one of the relative paths script_list returned.
        Registers the script as a REAPER action if it isn't already (safe to
        call every time), runs it, then waits up to wait_seconds for the
        script to report a result via
        reaper.SetExtState("reaper_mcp_script_result", "last_result", <string>, false).
        Scripts that don't opt into this convention — including anything
        that hasn't finished running (e.g. a background/defer-based script)
        by the time wait_seconds elapses — come back with result_found: false.

        This runs arbitrary local script code. Confirm with the user before
        calling this unless they've already given clear, specific
        instruction to run this exact script.

        Args:
            script_path: Relative path from script_list, e.g. "MyScripts/foo.lua".
            wait_seconds: How long to wait for a result, 0-30. Default 5.0.
        
- **selection_set_time** — Set time selection.

        Args:
            start: Start seconds.
            end: End seconds.
        
- **selection_get_time** — Get current time selection. If you also need track/item counts or
        the region list, project_get_overview() gets all of it in one call.
- **selection_set_loop** — Set loop points.

        Args:
            start: Start seconds.
            end: End seconds.
        
- **selection_select_all_items** — Select all items.
- **selection_deselect_all_items** — Deselect all media items.
- **selection_select_all_tracks** — Select all tracks.
- **selection_deselect_all_tracks** — Deselect all tracks.
- **selection_get_selected_tracks** — Get a list of all currently selected tracks with their full info.

        Just need to know which indices are selected, alongside other project
        state? project_get_overview() returns indices only (cheaper) as part
        of a combined read — use this tool instead when you need full detail
        (name, color, volume, etc.) on each selected track.
        
- **selection_get_selected_items** — Get a list of all currently selected media items with their full info.

        Just need to know which indices are selected, alongside other project
        state? project_get_overview() returns indices only (cheaper) as part
        of a combined read — use this tool instead when you need full detail
        (position, volume, fades, etc.) on each selected item.
        
- **send_create** — Create send between tracks. Prefer setup_routing for batch.

        To create a MIDI-only send to a multi-timbral VSTi track, pass
        midi_source_channel and midi_dest_channel (0=all, 1-16). The send
        will carry MIDI on the specified channels. Audio on the send can
        be muted separately via send_set_mute.

        Args:
            source_track: Source track index.
            dest_track: Destination track index.
            midi_source_channel: If set, enables MIDI routing. 0=all channels, 1-16=specific. Must be paired with midi_dest_channel.
            midi_dest_channel: Target channel on the destination track. 0=all, 1-16. Must be paired with midi_source_channel.
        
- **send_remove** — Remove a send.

        Args:
            track_index: Source track index.
            send_index: Send index.
        
- **send_get_all** — Get all sends/receives on a track.

        Args:
            track_index: Track index.
        
- **send_set_volume** — Set send volume.

        Args:
            track_index: Source track.
            send_index: Send index.
            volume_db: dB (0=unity).
        
- **send_set_pan** — Set send pan.

        Args:
            track_index: Source track.
            send_index: Send index.
            pan: -1.0 to 1.0.
        
- **send_set_mute** — Mute/unmute send.

        Args:
            track_index: Source track.
            send_index: Send index.
            mute: True=mute.
        
- **send_set_midi_channel** — Set MIDI source/destination channel on an existing send.

        Enables or changes MIDI routing on a track send. Use this to route
        MIDI from a source track to a specific channel on a multi-timbral
        VSTi (e.g. ARIA Player, Kontakt multi) on the destination track.

        To disable MIDI on the send, set both channels to 31.

        Args:
            track_index: Source track index.
            send_index: Send index to modify.
            midi_source_channel: Source MIDI channel. 0=all channels, 1-16=specific, 31=disabled.
            midi_dest_channel: Destination MIDI channel. 0=all, 1-16=specific, 31=disabled.
        
- **send_get_routing_diagram** — Get full project routing diagram (sends, receives, outputs).
- **setup_sidechain** — Set up sidechain compression: source pumps/ducks the target.

        Creates an aux send from source→target on channels 3/4, sets the target
        track to 4 channels, finds-or-adds a compressor on the target, pin-maps
        channels 3/4 to the compressor's sidechain input pins, and configures
        the compressor for pumping character.

        Use cases:
          - EDM kick → bass (classic pumping bassline)
          - EDM kick → pads (breathing pad)
          - Vocal lead → synth pad (pad ducks under vocals)
          - Snare → reverb tail (dynamic reverb)

        Args:
            source_track: Track whose signal drives the ducking (e.g. kick).
            target_track: Track that gets ducked (e.g. pad, bass).
            amount: 0.0 (subtle) to 1.0 (heavy pumping). Defaults to 0.7.
                    Translates to threshold/ratio if those are not explicit.
            attack_ms: Comp attack. Fast (2-10ms) = tight pump. Default 5.
            release_ms: Comp release. 80-250ms for classic EDM pump. Default 180.
            ratio: Compression ratio. 0 = derive from amount. Typical 4:1 to 10:1.
            threshold_db: Threshold in dB. 0 = derive from amount. Typical -15 to -30.
            compressor_name: "ReaComp" (default), "FabFilter Pro-C 2", or any loaded compressor.
            fx_index: Use an existing FX by index on the target. -1 = find-or-add by name.
            send_db: Aux send level in dB (drives how hard the compressor reacts). 0 = unity.

        Returns: {success, send_index, fx_index, compressor_name, threshold_db, ratio, ...}
        
- **item_take_list** — List all takes on an item with their active status.

        Useful for vocal comping (multiple vocal passes as separate takes on
        one item) or alternate MIDI performances.

        Args:
            item_index: Global item index.
        
- **item_take_set_active** — Switch which take plays back on an item.

        Args:
            item_index: Global item index.
            take_index: 0-based take index (from `item_take_list`).
        
- **item_take_add** — Add a new empty take to an item. Returns the new take's index.

        Args:
            item_index: Global item index.
        
- **item_take_delete_active** — Delete the currently active take from an item.

        To delete a non-active take, first call `item_take_set_active` on it.

        Args:
            item_index: Global item index.
        
- **track_template_save** — Save a track's full state (FX chain, sends, volume/pan/color, automation)
        as a named template on disk.

        Templates are stored at `%APPDATA%/reaper_mcp/track_templates/<name>.template`.

        Args:
            track_index: 0-based track index to save.
            name: Template name (letters/digits/space/`-_()` only).
        
- **track_template_apply** — Apply a saved template to a track — replaces its FX chain + settings.

        Args:
            name: Template name (from `track_template_list`).
            track_index: 0-based target track index.
        
- **track_template_list** — List all saved track templates.
- **track_template_delete** — Delete a saved track template.

        Args:
            name: Template name to delete.
        
- **tempo_list_markers** — List all tempo + time-signature markers in the project.

        Returns each marker's time, measure, beat, BPM, and time signature.
        Use this to see the project's tempo map before editing.
        
- **tempo_add_marker** — Add a tempo and/or time-signature marker at a given time.

        Args:
            position: Time in seconds (>= 0).
            bpm: New BPM at this marker. 0 = inherit from previous.
            time_sig_num: Numerator (e.g. 3 for 3/4). 0 = inherit.
            time_sig_denom: Denominator (e.g. 4 for 3/4). 0 = inherit.
            linear: True = smooth BPM ramp to the NEXT marker; False = instant change.
        
- **tempo_delete_marker** — Delete a tempo/time-sig marker by its index (from `tempo_list_markers`).

        Args:
            index: 0-based marker index.
        
- **tempo_clear_all** — Delete every tempo/time-sig marker — resets the project to its base tempo.

        Does NOT change the base BPM itself; use `transport_set_bpm` for that.
        
- **track_get_all** — Get all tracks with properties (name, volume, pan, mute, solo, FX, items, routing).

        Each track includes `sample_filenames` — the distinct audio file
        names actually dragged/imported onto that track (Splice, sample
        packs, etc.), not the track's own display name. Vendors commonly
        embed BPM/key in the filename itself, e.g. "Karra_Vocal_Loop_120bpm_Cmin.wav".
        Capped at 20 distinct names per track; empty if the track has no
        audio items.
        
- **track_get_info** — Get detailed info for one track.

        Includes `sample_filenames` — see `track_get_all` for what this is.

        Args:
            track_index: 0-based track index.
        
- **track_create** — Create new track.

        Args:
            index: Insert position (-1 = end).
            name: Optional track name.
        
- **track_delete_batch** — Delete one track, several, or all of them, in one call.

        Destructive: on a project that already has content, confirm with the
        user before calling. A project backup is taken first.

        Args:
            entries: JSON array. Each entry has exactly one of:
                       {"all": true}                          delete every track
                       {"track_index": 3}                     delete one track
                       {"track_indices": [0, 2, 5]}           delete several
                     Indices are 0-based and refer to the track numbering as it
                     is when the call starts; deletion order is handled for you,
                     so entries can overlap or come in any order. The master
                     track can't be deleted. A bad index is recorded in the
                     response's errors array and does not abort the rest. The
                     response lists what was deleted (original index and name).
        
- **track_rename** — Rename a track.

        Args:
            track_index: 0-based track index.
            name: New name.
        
- **track_set_volume** — Set track volume. Prefer configure_tracks for batch.

        Args:
            track_index: 0-based track index.
            volume_db: Volume in dB (0=unity, -6=half).
        
- **track_set_pan** — Set track pan. Prefer configure_tracks for batch.

        Args:
            track_index: 0-based track index.
            pan: -1.0 (left) to 1.0 (right).
        
- **track_set_mute** — Mute/unmute a track.

        Args:
            track_index: 0-based track index.
            mute: True=mute, False=unmute.
        
- **track_set_solo** — Solo/unsolo a track.

        Args:
            track_index: 0-based track index.
            solo: True=solo, False=unsolo.
        
- **track_set_record_arm** — Arm/disarm track for recording.

        Args:
            track_index: 0-based track index.
            arm: True=arm, False=disarm.
        
- **track_set_color** — Set track color (RGB 0-255). Prefer configure_tracks for batch.

        Args:
            track_index: 0-based track index.
            r: Red 0-255.
            g: Green 0-255.
            b: Blue 0-255.
        
- **track_select** — Select/deselect a track.

        Args:
            track_index: 0-based track index.
            selected: True=select.
            exclusive: Deselect others first.
        
- **track_set_input** — Set recording input. 0=none, 1-1024=mono, 1024+=stereo, 4096+=MIDI, -1=MIDI all.

        Args:
            track_index: 0-based track index.
            input_index: Input channel index. Valid values: -1 (MIDI all inputs), or >= 0.
        
- **track_get_mixer_state** — Get mixer state for all tracks (volumes, pans, mutes, solos, sends).
- **track_get_peak** — Read instantaneous peak level from a track's meter (L + R channels).

        Returns peak in both linear gain (0-1+) and dB (-144 to +12 or so).
        **Only meaningful DURING playback** — REAPER's meters only report
        when audio is passing through. Start playback, wait a moment, then read.

        Useful for: checking if a track is clipping, comparing levels between
        tracks, verifying a mix isn't too hot.

        Returns:
            `{peak_l_db, peak_r_db, peak_max_db, peak_l_linear, peak_r_linear}`

        Args:
            track_index: 0-based track index.
        
- **track_freeze** — Freeze a track to stereo audio (pre-fader render of FX + VSTi).

        Uses REAPER action 41223. Freezing bypasses the live FX chain and
        plays back rendered audio — use when a track's VSTi or FX chain
        is CPU-heavy and you're done editing that part.

        Call `track_unfreeze` to restore the original FX + MIDI state.

        Args:
            track_index: 0-based track index.
        
- **track_unfreeze** — Unfreeze a track, restoring its original FX chain and MIDI state.

        Uses REAPER action 41644.

        Args:
            track_index: 0-based track index.
        
- **track_set_folder** — Set folder state. 0=normal, 1=folder parent, -1=last in folder.

        Args:
            track_index: 0-based track index.
            folder_depth: Folder depth value.
        
- **transport_play** — Start playback.
- **transport_stop** — Stop playback/recording.
- **transport_pause** — Toggle pause.
- **transport_record** — Start recording.
- **transport_get_state** — Get transport state (play/record status, position, BPM, time sig, repeat).
- **transport_set_position** — Set cursor position.

        Args:
            seconds: Position in seconds.
        
- **transport_set_bpm** — Set project tempo.

        Args:
            bpm: BPM (20-999).
        
- **transport_set_time_signature** — Set time signature.

        Args:
            numerator: Beats per measure (1-32).
            denominator: Beat value (1,2,4,8,16,32).
        
- **transport_toggle_repeat** — Toggle repeat/loop mode.
- **transport_toggle_metronome** — Toggle metronome.
- **transport_set_playrate** — Set playback speed.

        Args:
            rate: 0.25-4.0 (1.0=normal).
        

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `uvx` (confidence: high):

```json
"mcpServers": {
  "reaper-mcp": {
    "command": "uvx",
    "args": ["xdarkzx-reaper-mcp"]
  }
}
```

## Documentation

## What xDarkzx/Reaper-MCP does

xDarkzx/Reaper-MCP lets an MCP-compatible AI assistant operate REAPER as a music-production workspace. Its tool set covers transport, project and track management, MIDI composition and inspection, audio-item editing, takes, markers and regions, automation, FX chains, routing, mixing, mastering, and ReaScript-related workflows.

The server is particularly suited to agent-directed production tasks. It can split audio at detected transients or chosen timeline positions, duplicate and rearrange chops, change pitch or playrate, reverse takes, and build structured vocal-chop arrangements. MIDI tools insert and inspect notes and CC data, while composition helpers support patterns, chord progressions, and arrangement edits.

## How it works

Communication stays on the local computer. The MCP process exchanges commands with a Lua script running inside REAPER through file-based IPC, so the AI client does not need a remote service or API key. REAPER remains the application that owns the project, audio, MIDI, plug-ins, and playback state.

The client sends tool calls such as `setup_fx_chain`, `compose_arrangement`, or `item_split_at_transients`; the server performs the corresponding REAPER action and returns structured results. Many operations use zero-based track, item, take, marker, and FX indices. Results often include those indices so later calls can target the created or modified objects.

## Setup and configuration

REAPER 7 or newer and Python 3.10 or newer are supported by the project. Install from the repository, use the published Python package with `pip`, or install from a downloaded archive. The installer can configure Claude Desktop; other clients can use a standard MCP server entry with `reaper-mcp` as the command.

The Lua bridge must also be loaded in REAPER. The installer can configure it to load automatically. With a manual setup, load `reaper_scripts/reaper_mcp_server.lua` through REAPER's Actions menu and run it. REAPER must remain open and the script must be active while the MCP client sends requests.

## Tools and capabilities

Notable capabilities include:

- Control playback, recording, tempo, time signature, looping, and other transport state.
- Create and configure tracks, including volume, pan, mute, solo, color, routing, and sends.
- Insert, edit, inspect, and clear MIDI notes and CC automation.
- Split, duplicate, move, resize, reverse, repitch, and time-stretch audio items and takes.
- Build vocal-chop arrangements with grid slicing, key transposition, style modes, and optional melody generation.
- Add, inspect, configure, scan, and remove plug-ins, including batch FX chains and master-track processing.
- Read, create, edit, and clear track or FX automation envelopes.
- Add markers and regions, inspect project data, and create a demo EDM project for testing production workflows.

`setup_fx_chain` accepts plug-in names or existing FX indices and can write parameters by fuzzy name or exact index. It reports failures per item in a batch response. FX parameter scanning temporarily sweeps values and caches discovered ranges, so it is better reserved for plug-ins whose parameter behavior is unclear.

## Limitations and notes

Audio transient sensitivity comes from REAPER's project settings rather than the split tool. Changing a take's playrate does not automatically resize the item's visible timeline length. Pitch quality depends on REAPER's configured take pitch-shift mode; the vocal-chop pipeline recommends an Élastique Pro Soloist setting for formant preservation but does not set it automatically.

Composition calls use different timing conventions. `compose_arrangement` expects raw seconds, while some pattern and chord tools convert quarter-note timing using the project BPM. Large MIDI writes may need to be divided into smaller calls, and layered chop workflows can create many overlapping items. Check returned counts and per-item summaries instead of treating a non-throwing batch call as proof that every operation succeeded.

_Full upstream README: https://allmcps.com/mcp/xdarkzx-reaper-mcp/readme_

