create_chord_progression
Create a chord progression on a REAPER track as a single MIDI item. Accepts comma-separated chord names (e.g., C,G,Am,F) with support for major, minor, seventh, and suspended chords, voiced around middle C.
Instructions
Create a chord progression on a track as a single MIDI item. chords: comma-separated chord names, e.g. "C,G,Am,F" or "Cm7,Fm7,Bb7,Ebmaj7". Supports: maj, min/m, dim, aug, maj7, min7/m7, dom7/7, dim7, hdim7, sus2, sus4. All chords are voiced around middle C (MIDI 60).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| track_index | Yes | ||
| chords | Yes | ||
| start_position | Yes | ||
| beats_per_chord | No |
Implementation Reference
- src/reaper_mcp/midi_tools.py:127-182 (handler)The main handler function for the create_chord_progression tool. It creates a single MIDI item on a track containing all chords, parses each chord string, and adds MIDI notes voiced around middle C (MIDI 60). Returns success/failure with chord metadata.
@mcp.tool() def create_chord_progression( track_index: int, chords: str, start_position: float, beats_per_chord: int = 4, ) -> dict: """ Create a chord progression on a track as a single MIDI item. chords: comma-separated chord names, e.g. "C,G,Am,F" or "Cm7,Fm7,Bb7,Ebmaj7". Supports: maj, min/m, dim, aug, maj7, min7/m7, dom7/7, dim7, hdim7, sus2, sus4. All chords are voiced around middle C (MIDI 60). """ try: project = get_project() track = project.tracks[track_index] chord_list = [c.strip() for c in chords.split(",")] seconds_per_beat = 60.0 / project.bpm chord_length = seconds_per_beat * beats_per_chord total_length = chord_length * len(chord_list) item = track.add_midi_item(start_position, start_position + total_length) take = item.active_take added_chords = [] for i, chord_str in enumerate(chord_list): try: intervals, root_num = _parse_chord(chord_str) chord_start = i * chord_length for interval in intervals: note_num = 60 + root_num + interval take.add_note( start=chord_start, end=chord_start + chord_length * 0.95, pitch=note_num, velocity=80, channel=0, ) added_chords.append({ "chord": chord_str, "position": chord_start, "length": chord_length, }) except Exception as e: logger.warning(f"Skipping chord '{chord_str}': {e}") return { "success": True, "item_id": item.id, "chords": added_chords, "start_position": start_position, "total_length": total_length, } except Exception as e: logger.error(f"create_chord_progression failed: {e}") return {"success": False, "error": str(e)} - src/reaper_mcp/midi_tools.py:23-38 (schema)CHORD_TYPES dict defining interval patterns for all supported chord types (maj, min, dim, aug, maj7, min7, 7/dom7, dim7, hdim7, sus2, sus4).
CHORD_TYPES = { "maj": [0, 4, 7], "min": [0, 3, 7], "m": [0, 3, 7], "dim": [0, 3, 6], "aug": [0, 4, 8], "maj7": [0, 4, 7, 11], "min7": [0, 3, 7, 10], "m7": [0, 3, 7, 10], "7": [0, 4, 7, 10], "dom7": [0, 4, 7, 10], "dim7": [0, 3, 6, 9], "hdim7": [0, 3, 6, 10], "sus2": [0, 2, 7], "sus4": [0, 5, 7], } - src/reaper_mcp/midi_tools.py:40-44 (schema)NOTE_TO_NUMBER mapping from note names (e.g., C, C#, Db) to semitone numbers 0-11, used to parse chord roots.
NOTE_TO_NUMBER = { "C": 0, "C#": 1, "Db": 1, "D": 2, "D#": 3, "Eb": 3, "E": 4, "F": 5, "F#": 6, "Gb": 6, "G": 7, "G#": 8, "Ab": 8, "A": 9, "A#": 10, "Bb": 10, "B": 11, } - src/reaper_mcp/midi_tools.py:47-58 (helper)Helper function _parse_chord that parses a chord string like 'Cm7', 'F#maj7', or 'G' into its interval list and root semitone number.
def _parse_chord(chord_str: str): """Return (intervals_list, root_semitone) for a chord string like 'Cm7', 'G', 'F#maj7'.""" chord_str = chord_str.strip() if len(chord_str) >= 2 and chord_str[1] in ("#", "b"): root = chord_str[:2] chord_type = chord_str[2:] or "maj" else: root = chord_str[:1] chord_type = chord_str[1:] or "maj" intervals = CHORD_TYPES.get(chord_type, CHORD_TYPES["maj"]) root_num = NOTE_TO_NUMBER.get(root, 0) return intervals, root_num - src/reaper_mcp/server.py:6-28 (registration)Registration of all tool modules including midi_tools. The mcp instance is created on line 6, midi_tools.register_tools is imported on line 12 and called on line 22, which registers create_chord_progression via the @mcp.tool() decorator.
mcp = FastMCP("reaper-mcp") # Import each tool module's register_tools function and call it with the mcp instance. # The imports must happen after mcp is created to avoid circular dependencies. from reaper_mcp.project_tools import register_tools as _reg_project from reaper_mcp.track_tools import register_tools as _reg_track from reaper_mcp.midi_tools import register_tools as _reg_midi from reaper_mcp.fx_tools import register_tools as _reg_fx from reaper_mcp.audio_tools import register_tools as _reg_audio from reaper_mcp.mixing_tools import register_tools as _reg_mixing from reaper_mcp.render_tools import register_tools as _reg_render from reaper_mcp.mastering_tools import register_tools as _reg_mastering from reaper_mcp.analysis_tools import register_tools as _reg_analysis _reg_project(mcp) _reg_track(mcp) _reg_midi(mcp) _reg_fx(mcp) _reg_audio(mcp) _reg_mixing(mcp) _reg_render(mcp) _reg_mastering(mcp) _reg_analysis(mcp)