Model Context Protocol server that gives Claude (or any MCP client) end-to-end control of FL Studio — transport, patterns, channels, mixer, plugins, piano roll, playlist, arrangement, automation, rendering, high-level generators for beats / chords / basslines / arpeggios / melodies, plus voice-to-MIDI humming and full audio-file analysis (MP3 → FL piano roll).
Built from scratch to improve on three earlier community attempts (karl-andres · ohhalim · veenastudio):
| Feature | karl-andres | ohhalim | veenastudio | fLMCP |
|---|---|---|---|---|
| Transport controls | ✅ | ❌ | ◐ | ✅ |
| Tempo write (undoable) | ❌ | ❌ | ✅ | ✅ |
| Time signature | ❌ | ❌ | ❌ | ✅ |
| Patterns (create / rename / clone) | ❌ | ❌ | ❌ | ✅ |
| Full channel rack | ✅ | ❌ | ❌ | ✅ |
| Step sequencer get / set | ✅ | ❌ | ❌ | ✅ |
| Mixer (vol / pan / mute / solo / arm / routes / EQ / FX) | ◐ | ❌ | ❌ | ✅ |
| Plugin params (get / set / search / presets) | ◐ | ❌ | ❌ | ✅ |
| Piano roll (add / edit / clear / read / quantize / transpose / humanize / duplicate) | ◐ | ❌ | ◐ | ✅ |
| Playlist tracks / markers | ❌ | ❌ | ❌ | ✅ |
| Arrangement switching | ❌ | ❌ | ❌ | ✅ |
| Parameter automation via REC events | ❌ | ❌ | ◐ | ✅ |
| Undo / redo / save undo | ❌ | ❌ | ❌ | ✅ |
MCP resources (fl://project, fl://mixer, …) |
◐ | ❌ | ❌ | ✅ |
| High-level music generators (beats / chords / arps / basslines / melodies) | ❌ | ❌ | ❌ | ✅ |
| Voice-to-MIDI (hum → piano roll) | ❌ | ❌ | ❌ | ✅ |
| Polyphonic transcription (chords, via Basic Pitch) | ❌ | ❌ | ❌ | ✅ |
| Audio-file analysis (tempo / key / onsets / melody) | ❌ | ❌ | ❌ | ✅ |
| MP3 → DnB flip one-shot | ❌ | ❌ | ❌ | ✅ |
| Dear PyGui voice GUI | ❌ | ❌ | ❌ | ✅ |
| Transport: TCP (no loopMIDI required to run) | ❌ (MIDI + JSON-poll) | ❌ (MIDI) | ❌ (MIDI) | ✅ |
| Push notifications (transport tick / refresh / project load) | ❌ | ❌ | ❌ | ✅ |
| PowerShell installer for Windows | ❌ | ❌ | ❌ | ✅ |
| Tool count | ≈ 60 | ≈ 14 | ≈ 5 | 159 |
- No loopMIDI dance. FL Studio 2025 ships Python 3.12 with
_socket.pyd, so the bridge opens a tiny TCP server inside FL (127.0.0.1:9876). No virtual-MIDI port, no 7-bit byte encoding, true bidirectional streaming. - Piano-roll editing that actually works. FL's
flpianorollmodule is sandboxed to piano-roll scripts; fLMCP stages edits to a JSON file, then fires the companion pyscript via a synthesisedCtrl+Alt+Yso the notes are inserted on FL's own main thread. - High-level music generators.
gen_emit_chord_progression,gen_emit_bassline,gen_emit_drum_pattern(boom-bap / trap / DnB / reggaeton / house / amen-break / rock …),gen_emit_arpeggio,gen_emit_melody— write full musical ideas with one tool call. - Voice-to-MIDI. Hum a melody, get it in the piano roll. Optional scale
snap, quantize, transpose, confidence filtering. Standalone Dear PyGui
window with live waveform + pitch readout for interactive edits. Pass
polyphonic=Trueto transcribe chords / strums / full-mix content with Spotify Basic Pitch instead of the default monophonic engine. - Audio-file analysis. Drop any MP3 / WAV / FLAC / OGG on it — get back tempo, detected key, onsets, and (optionally) the dominant melody transcribed into FL's piano roll.
- Push events.
transport.tick,refresh,projectLoadare streamed back so Claude can react to what the user is doing in FL without polling.
+---------------------+ stdio +----------------------+
| Claude / MCP host | <--------------- | fl-studio-mcp |
+---------------------+ | Python server |
+-----------+----------+
|
length-prefixed JSON
over TCP (127.0.0.1:9876)
|
+------------------------------------ FL Studio 2025 -----------------+
| |
| [ Hardware/fLMCP Bridge/device_FLStudioMCP.py ] |
| OnInit() -> threading.Thread -> socket.accept loop |
| OnIdle() -> drains queue, executes FL API on main thread |
| OnRefresh() / OnProjectLoad() -> push notifications to MCP |
| |
| [ Piano roll scripts/ComposeWithLLM.pyscript ] |
| Triggered by Ctrl+Alt+Y (SendInput from MCP server): |
| reads fLMCP_request.json, edits piano roll via flpianoroll, |
| writes fLMCP_state.json back. |
+-----------------------------------------------------------------------+
Deeper dive in docs/ARCHITECTURE.md.
The earlier projects all relied on loopMIDI plus 7-bit byte encoding, which is
painful and fragile. FL Studio 2025 ships Python 3.12 with _socket.pyd, so
the bridge simply opens a local TCP server inside FL. No extra driver, no 7-bit
limit, true bidirectional streaming, free push notifications.
The flpianoroll module (the only way to insert / delete notes) is only
available inside piano-roll scripts. Device / controller scripts can't
import it. fLMCP stages the requested edits into a JSON file, then
synthesises Ctrl+Alt+Y in the FL Studio window via Win32 SendInput, which
fires the companion pyscript to apply the edits and write the new state back.
Requirements:
- Windows 10 / 11
- FL Studio 2025 (Producer Edition or higher — needs MIDI scripting)
- Python 3.10+ on
PATH(used once to create the bundled venv)
git clone https://github.com/geezoria/FLStudioMCP.git fLMCP
cd fLMCP
./scripts/install_windows.ps1What the installer does:
- Copies
fl_bridge/device_FLStudioMCP.pyto%USERPROFILE%\Documents\Image-Line\FL Studio\Settings\Hardware\fLMCP Bridge\ - Copies
fl_bridge/piano_roll/ComposeWithLLM.pyscriptto...\Settings\Piano roll scripts\ - Creates
.venv\and installs the MCP package editable, with the[audio,gui]extras (voice-to-MIDI + audio analysis). Pass-SkipAudioto install the core only. - Adds an
fl-studio-mcpentry to%APPDATA%\Claude\claude_desktop_config.json(and to Claude Code's config if present).
- Launch FL Studio 2025.
Options > MIDI Settings > Input: pick any row (even a device you don't use) and set Controller type =fLMCP Bridge, then click Enable.- If you don't own a MIDI device at all, install
loopMIDI, create
a port called
fLMCP loop, and bind that row. The bridge just needs any input line wired so FL loads the script — no MIDI bytes actually flow.
- If you don't own a MIDI device at all, install
loopMIDI, create
a port called
- Open FL's script output (
Options > MIDI > View script output) and look for[fLMCP] TCP server listening on 127.0.0.1:9876. - Open any piano roll, click the scripts dropdown (top-right), and pick
ComposeWithLLM as the active script. This binds
Ctrl+Alt+Yto our pyscript. FL remembers the choice across sessions. - Restart Claude Desktop / Claude Code so it picks up the new MCP entry.
Voice-to-MIDI and audio-file analysis bring in heavier dependencies
(sounddevice, librosa, soundfile, numpy, scipy, dearpygui). The
Windows installer already installs them; on other platforms:
pip install "fl-studio-mcp[audio]"Polyphonic transcription (the polyphonic=True flag — chords, strums,
full-mix content via Spotify Basic Pitch) is a separate, heavier extra. Because
basic-pitch pins a TensorFlow build that doesn't exist on Python 3.12, install
Basic Pitch with --no-deps (it runs on the ONNX runtime — no TensorFlow):
pip install "fl-studio-mcp[polyphonic]"
pip install basic-pitch --no-depsOn Windows, ./scripts/install_windows.ps1 -Polyphonic does both steps for you.
Without it, the polyphonic=True tools return a clear install hint and the
default monophonic engine keeps working.
.\.venv\Scripts\python.exe scripts\smoke_test.pyExpected output: project metadata, transport status, the first few channels / mixer tracks / patterns, and a tempo ramp demonstration.
"Open FL Studio, create a new pattern called 'Verse Beat', make a boom-bap drum groove on channels 0–2, a minor-key bassline in C2 on channel 3, and a i–VII–VI–V chord progression on channel 4."
pattern_create(name="Verse Beat")
gen_emit_drum_pattern(channel_map={"kick": 0, "snare": 1, "clhat": 2},
style="boom_bap", repeats=1)
gen_emit_bassline(channel=3, root="C2", scale="minor",
progression="i-VII-VI-V", pattern_style="octaves")
gen_emit_chord_progression(channel=4, progression="i-VII-VI-V",
root="C4", scale="minor")
transport_play()"Take this 90s breakbeat, detect the key and tempo, and flip it into a 174 BPM DnB loop — amen-break drums, sub-bass on the detected root, and the track's own melody on top."
song_to_dnb_flip(audio_path="C:/samples/break.wav",
target_bpm=174, dnb_style="amen",
include_melody=True, include_bass=True)"I'll hum a riff for 8 seconds — snap it to C minor, quantize to 1/16 at 120 BPM, and write it to the open piano roll."
voice_to_piano_roll(duration_sec=8, bpm=120,
scale_root="C", scale="minor",
quantize_grid_sec=0.125)Or the interactive route — Claude opens the GUI:
voice_open_gui() # Dear PyGui window with live waveform + pitch readoutFull reference with signatures: docs/TOOLS.md.
| Area | Count | Highlights |
|---|---|---|
| Meta | 4 | ping, reconnect, bridge info, raw escape hatch |
| Transport | 14 | play / stop / record, tempo, time sig, metronome, jog |
| Patterns | 13 | create, rename, clone, color, length, find-by-name |
| Channels | 20 | full rack, step sequencer get / set, routing, trigger |
| Mixer | 18 | vol / pan / mute / solo / arm, sends, EQ, FX slots, routing |
| Plugins | 13 | get / set / search params, preset navigation, editor |
| Piano roll | 10 | add, read, clear, quantize, transpose, humanize, duplicate |
| Playlist | 14 | tracks, clips (limited), markers |
| Arrangement | 5 | current, list, select, jump marker |
| Automation | 5 | tempo, channel vol / pan, mixer vol, plugin params |
| Project | 11 | metadata, save, save-as, undo / redo, render |
| UI | 7 | show / hide windows, hints, focus |
| Generators | 14 | scales, chords, progressions, arpeggios, basslines, drums |
| Voice-to-MIDI | 5 | record + transcribe + scale-snap + send to FL, GUI |
| Audio | 5 | analyze MP3 / WAV, slice at onsets, melody → piano roll, DnB flip |
| MCP resources | 7 | fl://project, fl://mixer, fl://patterns, … |
fl://status— bridge + transport snapshotfl://project— full project metadatafl://transport— live transport statefl://channels— entire channel rackfl://mixer— all mixer tracksfl://patterns— every patternfl://playlist— playlist tracks, clips, markers
Great for getting oriented ("what does the project look like right now?") before issuing edits.
| Symptom | Fix |
|---|---|
bridge unavailable from tool calls |
FL not running, or the controller script isn't loaded. Open Options > MIDI Settings > Input and enable a row with Controller type = fLMCP Bridge. |
| TCP bind error in FL output | Another copy of fLMCP is running, or firewall blocking 127.0.0.1:9876. Close the other instance and allow FL64.exe in Windows Defender. |
| Piano-roll edits don't apply | fLMCP focuses FL Studio and sends Ctrl+Alt+Y. If you rebound the hotkey, or FL can't be foregrounded, press Ctrl+Alt+Y manually once after the tool call. |
| Hotkey fires the wrong script | Open any piano roll → right-click the script dropdown → set ComposeWithLLM as the default. |
playlist_place_pattern returns a structured error |
FL's public Python API does not yet expose clip placement. Documented in docs/LIMITATIONS.md. |
voice_* tools error with no module named sounddevice |
Install the audio extras: pip install "fl-studio-mcp[audio]" (or re-run install_windows.ps1). |
voice_to_piano_roll returns 0 notes |
Hum louder, check voice_list_devices() for the right mic, or lower min_confidence. |
audio_analyze can't open MP3 |
Install ffmpeg and make sure it's on PATH (librosa falls back to it for compressed formats). |
docs/ARCHITECTURE.md— transport, threading, request lifecycle.docs/TOOLS.md— complete tool reference with signatures.docs/LIMITATIONS.md— what FL's public Python API does / doesn't expose, with workarounds.
python -m venv .venv
.venv/Scripts/activate # Windows
# source .venv/bin/activate # macOS / Linux
pip install -e ".[dev]" # core + pytest (add [audio,gui] for the full stack)
pytestTests in tests/ run entirely offline — the TCP bridge is faked, the FL-side
script is imported with its FL-only modules stubbed out, and the core server
builds without the audio extras. You don't need FL Studio (or numpy/librosa)
running to execute the suite.
See CHANGELOG.md for what changed between releases.
Issues and PRs welcome at https://github.com/geezoria/FLStudioMCP. Please
run pytest before opening a PR. Bug reports are much easier to act on if
they include the FL build number (Help > About) and the snippet of MIDI script output around the failure.
MIT. See LICENSE if present, or the license field in
pyproject.toml.