LemonStudioMCP
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@LemonStudioMCP make a 4-bar lo-fi beat at 85 BPM and play it"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
LemonStudioMCP
An MCP server that lets Claude (or any MCP client) make music in Lemon Studio, the open-source FL Studio-style DAW written in Rust.
How it works
Lemon Studio has a built-in control plane: started with --control <socket>, it listens on a local Unix socket and
answers JSON-RPC 2.0 requests, one per line. This server is an MCP front end for that socket:
Claude ──MCP──▶ lemon-studio-mcp ──JSON-RPC over /tmp/lemon.sock──▶ Lemon StudioReading.
get_project_statereturns the whole project in one call: tempo, channels, mixer tracks, patterns and the playlist. Everything has a stable id that stays the same when other things are added, moved or deleted, and every other tool refers to things by those ids.Editing. Each edit tool sends one
applyrequest of one or more commands (set_notes,add_channel,add_clip, …). Lemon Studio applies the batch all or nothing and records it as one undo step named after the client ("Claude: Write notes"), so you can undo it with Cmd+Z like any edit of your own.Units. Tools take positions and lengths in beats and convert them to ticks with the project's
ppq. Notes are MIDI numbers (60 = middle C).Playing.
play,pauseandstopdrive the transport the same way the toolbar buttons do.Changes from the app.
get_changeslists what changed since a given point, including your own edits in the window, so Claude can catch up on what you did.Errors. When Lemon Studio refuses a command ("no pattern 99999"), the message comes back to Claude as a tool error.
Nothing needs installing inside the app: the control plane is part of Lemon Studio.
Related MCP server: Ableton Live MCP
Requirements
macOS (Linux works too), Python 3.10+
Lemon Studio built from source, with the
play/pause/stop/transportcontrol methods. Until they are upstream, build it from thecontrol-plane-transportbranch of calvinw/Lemon-Studio:git clone -b control-plane-transport https://github.com/calvinw/Lemon-Studio.git cd Lemon-Studio && cargo build -p os-app --releaseUpstream Lemon Studio works too, without the transport and view tools (
play,pause,stop,transport_status,select).
Install
cd LemonStudioMCP
./install.sh /path/to/Lemon-Studio/target/release/lemonstudioThe installer creates .venv, installs the package, and registers the server with Claude Code (claude mcp add)
with LEMON_STUDIO_BIN pointing at the app so Claude can start it.
Use
Start Lemon Studio listening on the socket:
lemonstudio --control /tmp/lemon.sock # with its window
lemonstudio --serve /tmp/lemon.sock song.flp # no windowor just ask Claude: the lemon_launch tool starts it. Then, for example: "Make a 4-bar lo-fi beat at 85 BPM with a
Lemon Trio bass line and play it."
Claude.ai (remote connector)
Run the server over HTTP and expose it through a tunnel:
.venv/bin/python -m lemon_studio_mcp --transport http --port 8000 --host 0.0.0.0
cloudflared tunnel --url http://localhost:8000Add https://<random>.trycloudflare.com/mcp as a custom connector at claude.ai/customize/connectors. Only the MCP
server is exposed; the app's socket stays local. The tunnel URL gives anyone who has it control of the app, so stop
the tunnel when you are done.
Settings (environment)
Variable | Default | Meaning |
|
| The app's control socket |
| Use a local TCP port instead (the app prints the token) | |
|
| The app, for |
|
| The name on undo steps |
To have every change confirmed in the app, start Lemon Studio with --control-level propose and ask Claude to use
propose_commands.
Tools
Area | Tools |
Connection |
|
Reading |
|
Notes |
|
Channels |
|
Patterns, playlist |
|
Mixer, plugins |
|
Project |
|
Transport, view |
|
Undo, raw |
|
Positions and lengths are in beats by default (unit="ticks" for raw ticks). Note keys are MIDI numbers, 60 =
middle C.
Lemon Studio changes
The control plane had no transport control and no way to change what the window shows. Five methods were added to
Lemon Studio in the
control-plane-transport branch of the fork
(crates/os-app/src/control/rpc.rs, with tests, documented in docs/control-plane.md). They are not yet proposed
upstream.
Method | Level | |
| read | state (playing / paused / stopped), mode, current pattern, position, ppq, bpm |
| edit |
|
| edit | as the Pause and Stop buttons |
| edit |
|
Not yet
What Lemon Studio's control plane doesn't offer yet: automation clips, markers, presets, channel envelopes, note
fields beyond key/velocity/pan, rendering the open project without saving it first, and pushed change events (the
get_changes tool polls).
Found while using it:
No presets. Instrument sounds are picked by hand in each plugin's window.
Development
docs/DEVELOPMENT.md covers both repositories and the fork branch, the local clone setup, the Lemon Studio change in detail, how the server is built, known gaps, and next steps.
Tests
LEMON_STUDIO_BIN=/path/to/lemonstudio .venv/bin/pytest -vThey start Lemon Studio headless on a temporary socket and drive it through a real MCP client over stdio.
Available Tools
42 toolsadd_channelA
Add a channel played by instrument plugin (a name from list_plugins, e.g. "Lemon Trio"), or a sampler
with sample (an audio file path) loaded. mixer_track: a mixer track id to route it to. Returns its id.
Inputs: name, plugin, sample, mixer_track.
add_effectA
Add effect plugin (a name from list_plugins) to mixer track track (an id), in slot 0..9 or the first
free one. Returns the slot plugin's id (for get_plugin_params / set_plugin_param).
Inputs: track (required), plugin (required), slot.
add_mixer_trackB
Add a mixer insert (at insert number at, else after the last). Returns its id.
Inputs: at.
add_patternC
Add a pattern. Returns its id.
Inputs: name, color.
apply_commandsA
Run raw control-plane commands (Lemon Studio docs/control-plane.md) as ONE undo step, all or nothing. Positions and lengths here are raw ticks. Each command is {"op": ..., fields...}, e.g. {"op": "add_channel", "name": "Bass", "plugin": "Lemon Trio"}. Returns each command's result.
Inputs: commands (required), name.
clear_notesC
Remove the notes of channel from pattern pattern (or all of its notes when channel is None).
Inputs: pattern (required), channel.
delete_channelB
Delete channel channel (an id), with its notes, clips and automation.
Inputs: channel (required).
delete_clipC
Remove playlist clip clip (an id).
Inputs: clip (required).
delete_patternB
Delete pattern pattern (an id) and its playlist clips.
Inputs: pattern (required).
get_changesA
What changed after event number since (0: all kept), including the user's own edits in the app, and
last, the newest event number to pass next time.
Inputs: since.
get_historyA
The undo history: each step's name and whether it is undone.
Inputs: no named parameters.
get_notesC
The notes of pattern pattern (an id): channel id, position, length, key, velocity, pan.
Inputs: pattern (required), unit.
get_plugin_paramsC
The parameters (id, name, value 0..1, value text, ...) of the plugin playing channel id or effect slot id owner.
Inputs: owner (required).
get_project_stateA
The whole project: tempo, ppq, time signature, channels, mixer tracks (with effect slots and sends), patterns, the playlist's clips, the current pattern and song/pattern mode. Positions here are raw ticks.
Inputs: no named parameters.
lemon_launchA
Start Lemon Studio listening on the control socket (does nothing if it already is).
project: an .flp file to open. headless: run with no window (--serve) instead of the app with its window.
Inputs: project, headless.
lemon_statusB
Whether Lemon Studio is connected, the app's version, this client's permission level, and the transport.
Inputs: no named parameters.
list_pluginsB
The instrument and effect plugin names that add_channel and add_effect take.
Inputs: no named parameters.
load_sampleB
Load audio file path into sampler channel channel (an empty path clears it).
Inputs: path (required), channel (required).
pauseA
Pause playback in place (play resumes).
Inputs: no named parameters.
place_patternB
Place pattern pattern (an id) in the playlist on track track (1-based) at position. Returns the clip id.
Inputs: pattern (required), position (required), unit, track, +1 more.
playB
Start playback, as the Play button (resumes when paused).
mode: "pattern" loops the current pattern, "song" plays the playlist (default: the app's PAT/SONG switch). pattern: make this pattern (an id) the current one. from_position: where to start.
Inputs: mode, unit, pattern, from_position.
propose_commandsB
Like apply_commands, but Lemon Studio asks the user to Apply or Discard. Returns the proposal number and what it would change.
Inputs: commands (required), name.
redoB
Redo the last undone step.
Inputs: no named parameters.
remove_effectB
Remove the effect in slot slot (0..9) of mixer track track (an id).
Inputs: slot (required), track (required).
render_projectA
Render a saved .flp project to an audio file (.wav), with no window and no audio device. Save the open project first (save_project) to render what is in the app.
Inputs: out (required), project (required), rate, seconds, +1 more.
route_mixerA
Send mixer track from_track to to_track (ids) at level 0..1; level None removes the route.
Inputs: to_track (required), from_track (required), level.
save_projectB
Save the project (to path, else its own file). With the app's window open, the user is asked first.
Inputs: path.
selectA
Change what the user sees, not the music: make pattern (an id) the current pattern, select channel (an
id) in the channel rack, and with piano_roll=True open the piano roll on them (False closes it). While the
piano roll is shown it scrolls to the channel's notes. No undo step. Use it after writing notes so the user
sees them.
Inputs: channel, pattern, piano_roll.
set_channelA
Change channel channel (an id): volume 0..1, pan -1..1, pitch in cents, color "#rrggbb", enabled
(False mutes it), mixer_track (a mixer track id). Only the fields given change.
Inputs: channel (required), pan, name, color, +4 more.
set_clipC
Move, resize or mute playlist clip clip (an id).
Inputs: clip (required), unit, muted, track, +2 more.
set_effectA
Turn effect slot slot (0..9) of mixer track track (an id) on or off, or set its mix 0..1.
Inputs: slot (required), track (required), mix, enabled.
set_eqA
Set band 0..2 of mixer track track's built-in 3-band EQ: gain in dB (-18..18), frequency and width as
stored (0..65536).
Inputs: band (required), track (required), width, gain_db, +1 more.
set_mixer_trackC
Change mixer track track (an id): volume 0..1 (0.8 = unity), pan and stereo -1..1, enabled (False mutes).
Inputs: track (required), pan, name, color, +4 more.
set_patternC
Rename, recolor, or set the length of pattern pattern (an id). length 0 = automatic.
Inputs: pattern (required), name, unit, color, +1 more.
set_plugin_paramA
Set parameter param (an id from get_plugin_params) of the plugin of channel or slot owner to value 0..1.
Inputs: owner (required), param (required), value (required).
set_project_settingsC
Swing 0..1, main volume 0..1 (0.8 = unity), main pitch in cents, and the song info.
Inputs: genre, swing, title, author, +3 more.
set_tempoB
Set the tempo (10..522 BPM).
Inputs: bpm (required).
set_time_signatureB
Set the time signature (denominator 2, 4, 8 or 16).
Inputs: numerator (required), denominator (required).
stopA
Stop playback and rewind to where it started.
Inputs: no named parameters.
transport_statusC
Playing, paused or stopped; pattern or song mode; the current pattern; the playhead position; the tempo.
Inputs: unit.
undoA
Undo the last step (anyone's: this client's or the user's).
Inputs: no named parameters.
write_notesA
Write notes into pattern pattern (an id).
notes: [{"channel": id, "position": 0, "length": 0.5, "key": 60, "velocity": 100, "pan": 64}, ...];
position/length in unit (beats by default). channel fills in notes that leave theirs out.
replace=True: these become the notes of channel in the pattern (or all of its notes when channel is None);
replace=False: they are added to what is there.
Inputs: notes (required), pattern (required), name, unit, +2 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 42 tools
Nearly every tool pairs a distinct resource with a distinct action (channel vs pattern vs clip vs mixer track vs effect slot vs plugin param), and descriptions explicitly disambiguate near-pairs like set_clip vs place_pattern and set_effect vs set_plugin_param. The main overlap is apply_commands, which is a raw escape hatch that can do anything the individual tools do, and there is mild redundancy between transport_status, lemon_status and get_project_state on transport/mode info.
The dominant convention is verb_noun (get_notes, write_notes, add_channel, set_mixer_track, delete_pattern, route_mixer, remove_effect, save_project), applied consistently across resources. Deviations are minor and idiomatic: bare transport verbs (play, pause, stop, undo, redo), the select verb, a noun-first transport_status, and the lemon_ prefixed lifecycle pair (lemon_launch, lemon_status).
42 tools is heavy even for a full DAW control surface; the domain (channels, patterns, notes, playlist, mixer, effects, plugins, transport, project, render) justifies breadth, but many setter tools are one-field wrappers that apply_commands already covers generically. The surface could be consolidated (e.g. generic update_* tools or dropping per-field setters) without losing capability.
Coverage is broad and lifecycle-complete for most objects: channels, patterns, notes, clips, mixer tracks, effects and plugin params all have create/read/update/delete, plus transport, undo/redo, save and render. Gaps are moderate rather than blocking: no delete_mixer_track, no tools for automation clips/points despite delete_channel mentioning automation, and no way to open/close a project beyond the lemon_launch argument.
Related MCP Connectors
Create, co-edit, analyze, publish, and export collaborative step-sequencer sessions through MCP.
Read and edit DAW projects as an agent, save native Logic projects, convert projects between DAWs
Remote MCP server for audio stem separation, denoise, reverb, and MIDI export.
MCP server for Producer/Riffusion AI music generation
Related MCP Servers
- AlicenseBqualityBmaintenanceMCP server for headless, agent-native control of openDAW, enabling programmatic music production with tracks, instruments, effects, MIDI, automation, and rendering via 543 tools.100Apache 2.0
- AlicenseNot gradedqualityCmaintenanceEnables MCP clients to programmatically control an Ableton Live 12 session, including tracks, clips, MIDI notes, devices, parameters, mixer, scenes, warping, and rendering, over streamable HTTP.1MIT
- AlicenseCqualityCmaintenanceEnables end-to-end control of FL Studio on macOS through MCP clients, including transport, patterns, mixer, piano roll, playlist, live metering, browser navigation, and rendering.160MIT
- AlicenseCqualityBmaintenanceEnables podcast producers and agents to manage multitrack projects, run per-track transcription, perform transcript-driven edits, process audio via FFmpeg, and inspect a read-only DAW timeline over MCP.183MIT