Ableton Live MCP
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., "@Ableton Live MCPCreate a new MIDI track and add a chord progression"
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.
Ableton Live MCP
An MCP server for Ableton Live 12, built on the official Ableton Extensions SDK. The MCP server runs inside Live as an extension — no bridge process, no MIDI remote scripts. Any MCP client (Claude Code, Claude Desktop, Cursor, ...) connects over streamable HTTP and gets full programmatic control of the Live Set: tracks, clips, MIDI notes, devices, parameters, mixer, scenes, warping, rendering, and more.
MCP client (Claude, ...) ──streamable HTTP──▶ http://127.0.0.1:8722/mcp
│
Live Extension Host (Node.js)
│
Ableton Live 12Requirements
Ableton Live 12.4.5 or newer (Extensions support)
Node.js >= 24.14 (build only)
Related MCP server: ableton-agent-mcp
Install
npm install
npm run package # builds and produces Ableton-Live-MCP-<version>.ablxThen drag the .ablx file onto Settings → Extensions in Live. The MCP endpoint starts with
Live at http://127.0.0.1:8722/mcp (GET /health for a quick check: version, host API version,
tool count, uptime).
Development mode
Enable Settings → Extensions → Developer Mode in Live, then:
npm start # builds and runs the extension against the running Live.env must point at your Live installation, e.g.
EXTENSION_HOST_PATH=/Applications/Ableton Live 12 Beta.app.
Connect a client
Claude Code:
claude mcp add --transport http ableton-live http://127.0.0.1:8722/mcpClaude Desktop (or any stdio-only client) via mcp-remote:
{
"mcpServers": {
"ableton-live": {
"command": "npx",
"args": ["mcp-remote", "http://127.0.0.1:8722/mcp"]
}
}
}With an auth_token in config.json (see Security), the client sends it as a
bearer token:
claude mcp add --transport http ableton-live http://127.0.0.1:8722/mcp \
--header "Authorization: Bearer <token>"{
"mcpServers": {
"ableton-live": {
"command": "npx",
"args": ["mcp-remote", "http://127.0.0.1:8722/mcp", "--header", "Authorization:${AUTH_HEADER}"],
"env": { "AUTH_HEADER": "Bearer <token>" }
}
}
}Configuration
Settings live in config.json inside the extension's storage directory (created on first run
with the port; the path is reported by song_get under environment.storage_directory and by
the Status dialog in Live). It is read once, when the extension starts (sample_roots excepted):
restart Live after a change. Default:
{ "port": 8722 }Setting | Default | Meaning |
|
| TCP port on 127.0.0.1 |
|
|
|
|
| Folders |
|
|
|
|
| Tool names not to register, e.g. |
|
| Require |
Every value is validated at startup. Problems are logged to ExtensionHost.txt (prefixed
[ableton-live-mcp] config:) and listed in the Status dialog: a port or
progress_notifications of the wrong type falls back to its default, unknown keys are ignored
(misspelled security settings excepted, see below).
The security settings never fall back to something less safe: an invalid read_only,
disabled_tools or auth_token, a misspelled one (readOnly, auth-token, ...), a
disabled_tools entry that names no tool (probably a typo), or a file that exists but cannot be
read or parsed (smart quotes, unquoted keys, ...), makes /mcp refuse every request with 503
and the reason until the file is fixed. The file is read as UTF-8, or as UTF-16 when it starts
with a byte order mark (as Windows PowerShell 5.1 writes it); an empty file means the defaults.
A switched-off tool is not listed at all; batch steps naming it fail with "switched off", the
server instructions tell the agent which tools are off, and a prompt is only offered when every
tool it uses is on.
For example { "port": 8722, "read_only": true },
{ "port": 8722, "disabled_tools": ["execute_command", "track_delete", "scene_delete"] } or
{ "port": 8722, "auth_token": "<a long random string>" }.
sample_roots lists the folders sample_search may search for audio files (absolute paths, or
~/...), in addition to Live's default User Library and Factory Packs folders
(~/Music/Ableton/... on macOS, ~/Documents/Ableton/... on Windows) when they exist. Unlike
the port, it is read on every search, so no restart is needed:
{ "port": 8722, "sample_roots": ["/Users/me/Samples", "~/Splice"] }Long calls (show_dialog, dialog_result, selection_wait, render_track_audio,
audio_track_consolidate, batch, batch_read) send MCP progress notifications when the client
asks for them; such a call is then answered as an SSE stream instead of plain JSON (see
How it works). "progress_notifications": false answers every request as JSON
again (read at startup):
{ "port": 8722, "progress_notifications": false }Device presets saved with device_preset_save are JSON files in presets/ in the same
directory (one file per preset, named after it); they can be copied between machines, and
deleting a file deletes the preset.
How it works
Every Live object (track, clip, device, parameter, ...) is addressed by a stable object id discovered through listing tools (
song_get,track_get,device_get, ...).Tracks and scenes can also be addressed by name or index: every tool that acts on one track takes
track_id,track_index(regular tracks) ortrack_name(exact match first, then case-insensitive; regular, return and main tracks), and the scene tools takescene_id,scene_indexorscene_name. When several are given they must name the same object. An ambiguous name is an error that lists only the matching candidates; deletes need the exact name.Ids stay valid until the object is deleted or moved, or another Live Set is loaded. Stale ids return an error telling the client to re-list. Errors in the arguments themselves (unknown name, index out of range, conflicting fields) come without that hint.
All times/positions are in beats; colors are
#RRGGBB; MIDI pitches are 0–127, or note names in Live's naming ("C3"= 60,"F#1","Bb2") wherever a pitch is taken: notes, patterns, note selections, drum pads in device paths anddrum_chain_set. Drum chains reportreceiving_note_namenext toreceiving_note.Batched writes:
track_set,scene_set,cue_point_set,clip_set,take_lane_setandmixer_settakeitems: [{<address>, ...values}]to change many objects in one call.clip_setalso takesselect: {track, scope, name_contains}(e.g. every clip of the Drums track), andmixer_settakestargets(same values on several mixers) orall_trackswithexclude. Every entry is validated before the first write, and all of a call's changes form one undo step in Live — as do the multi-value writes ofparameter_setandmixer_set. The exception:cue_point_setmoves take up to three undo steps (creates, names, deletes); its renames are one.Create with initial config:
track_createtakes anameanddevices(each a name or{name, parameters}) and returns the new track'sindex(Live inserts it after the selected track);device_inserttakes initialparameters;scene_createandcue_point_createtake anameoritemsfor several at once.Parameters by name:
parameter_get/parameter_setentries take{track_name, device, parameter}(ordevice_id+parameter) instead of aparameter_id.deviceis a name, an index, or a path into racks, e.g.["Drum Rack", {"pad": 36}, "Simpler"](drum pads by receiving note) or[0, {"chain": 1}, "Reverb"](chains by index);parametermatches exactly first, then case-insensitively, then by unique substring. Each write gives exactly one ofvalue(internal units),value_item(a quantized parameter's choice by name, e.g."Saw"),reset: true(default value) ordelta(relative, clamped).device_setswitches a device on or off and sets several of its parameters in one undo step;parameter_findsearches parameter names across the set, a track or a device. Values stay in internal units — the API has no display values (dB, Hz, ms).MIDI shortcuts: MIDI writes (
clip_create,midi_clip_set_notes) take steppatterns({note: "C1", steps: "x...X...", step: 0.25},X= accent) andrepeat: {length, times?}, which copies the notes everylengthbeats up to the clip's end.midi_clip_get_notestakes aselectfilter andformat: "compact"([pitch, start, duration, velocity]tuples plus the fields that differ from the result'sdefaults).midi_clip_edit_notesaddsset,snap_to_scale(the song's scale or{root, intervals}, ties go down),swing, seededhumanize,duration_set/duration_scale/legato(a chord ends together), quantizing to Live's grid (grid: "song"), selecting byin_scale,every_nthor the user's note selection (only_selected), andclip_idsto edit several clips in one undo step. Whether Live reports the note selection (NoteDescription.selected) to extensions is not verified yet; when no note reports one, results carry awarning.Copy, move and presets (lossy by design): Live's API cannot move, reorder or copy devices, so
device_copyinserts a fresh built-in device and copies the parameter values by name (a Simpler also gets its sample);move: truethen deletes the original. Automation, mappings, a custom device name and non-parameter state are lost, and ids change.device_preset_save/device_preset_load/device_preset_listkeep the same kind of snapshot as named JSON files in the extension's storage directory (presets/) — not Live.advpresets. Racks are refused by both.Samples, kits and copies in one call:
drum_kit_build {track_name | rack_device_id, pads: [{note, sample_path, volume?, panning?}]}builds or updates a Drum Rack kit — per pad it reuses or creates the chain receiving that note, inserts a Simpler, imports the sample into the project and loads it; every path is checked before anything changes.device_insert {device_name: "Simpler", sample_path}imports the sample into the project by default (import_into_project), so the set survives being moved;simpler_replace_sampleloads the file as it is, as before, unless it is givenimport_into_project: true(recommended).sample_searchfinds audio files in the configuredsample_rootsand Live's library folders (read-only, confined to those folders). Live's API has no clip copy, soclip_copycreates a new clip: MIDI notes are re-based to where the source plays them (markers, loop) and repeated when the copy is longer, so it plays the same in the arrangement; a session copy loops its whole length, so a source that starts before its loop, or a length that is not whole loops, repeats differently (lostsays so); audio copies get the file, warping and markers/loop but Live warps them itself (fidelity: "approximate",lost,warp_markers_match).move: truedeletes the source once the copy exists.arrangement_copy_rangeoverwrites a target range with a copy of a source range across tracks ("repeat the chorus"): MIDI clips crossing the edges are trimmed, audio clips crossing them are skipped and listed (there is no way to insert time).audio_track_consolidaterenders a range of an audio track, imports the render, clears the range and puts one unwarped clip there.render_track_audio {analyze: true}returns peak/RMS levels (dBFS), clipped samples, a silence flag and a 32-part envelope read from the WAV;persist: trueimports the render;tracksrenders several audio tracks.Whole-set reads:
song_getlists every track with itsindex,depthin the group hierarchy,is_groupand, for group tracks,child_track_ids(theirtypestaysother);include: ["devices", "mixer", "clips"]adds each track's device tree (through racks, with each device's on-state), mixer values (sends labeled with return names) and clips (session and arrangement, in brief), capped at 500 devices / 500 clips (truncatedsays where it stopped).scene_getreturns one session row across all tracks,session_grida compact track × scene matrix, andarrangement_get {start_time, end_time, tracks?, include_take_lanes?}the cue points and every track's clips in a beat range, plus a derivedsong_end(the API has no song length).song_auditlists missing media files, Simplers without a sample, empty drum pads and track names that maketrack_nameambiguous.clip_get,device_getandchain_getreport alocation(track, scene index or take lane, path through racks).The user's selection: Live's API has no selection getter; it hands the selection to an extension only through context-menu commands. So the extension adds "MCP: Use as selection" to every context menu it can (clips, clip slots, tracks, scenes, Drum Racks, Simplers, samples, clip-slot selections and arrangement time selections). The user right-clicks and picks it;
selection_getreturns the last capture (ids, summaries, locations; for a time selection the start/end beats and the selected lanes with their overlapping clips),selection_waitwaits up to 55 s for the next one. Captures are kept until the next one (or a restart of Live).Dialogs in Live:
show_dialog {form: {title?, message?, fields?, choices?}}builds the page itself (every text escaped) and returns the answer as JSON —choice(the button's label),choice_indexandvaluesby field name (text, textarea, number, checkbox, select) — e.g. to confirm a bulk delete;html(your own page) andurl(https:, file: or http://localhost) return the string the page sends. Live's dialogs are modal, have no timeout and can't be closed from code, so a waiting call can outlast the client's request timeout:wait: falsereturns adialog_idat once anddialog_result {dialog_id?, wait_s ≤ 50}polls for the answer (also for a dialog whose waiting call was cut off). One dialog at a time (the next is refused while one is open;forceif Live never reported it closed); answers are kept for 10 minutes.Chains of calls:
batch {steps: [{tool, args, as?}]}runs up to 50 tool calls in order; an argument{"$ref": "<step>.<path>"}takes a value from an earlier step's result (step index orasname, then keys and array indexes, e.g.{"$ref": "0.id"}). Every tool name,$refand the arguments of steps without a$refare checked against the tools' own schemas before the first step runs; each step is its own undo step, andon_errorstops (default) or continues after a failing step.batch_readdoes the same for read-only tools only and refuses any other.show_dialog,execute_commandand the batch tools themselves can't be steps.Long calls:
show_dialog,dialog_result,selection_wait,render_track_audio,audio_track_consolidate,batchandbatch_readsendnotifications/progresswhen the request carries aprogressToken— right away, then every 10 s (batches: one per step, with the total) — so a client that resets its timeout on progress keeps waiting. Only such a call is answered as an SSE stream (with the SDK's 15 s keep-alive); every other request keeps a plain JSON response. When the client goes away (the connection drops), waiting calls stop waiting, multi-track renders and batches start no further render or step (a dialog stays open in Live:dialog_resultgets it). Anotifications/cancelledcannot reach a running call: the server is stateless, so it arrives in a request of its own.Resources (read-only snapshots for clients that attach context):
live://songissong_getwithout arguments,live://tracks/{track}istrack_getfor the track named{track}— percent-encoded withencodeURIComponent, so a/in a name cannot break the match (live://tracks/Drums%2FBus);resources/listhas one entry per uniquely named track and completions for the names — andlive://guideis the full skill. There are no subscriptions: the server is stateless and Live reports no changes, so read again for fresh data. They follow their tools: withsong_getortrack_getswitched off, theirs are gone.Prompts the user picks in the client, each expanding into step-by-step instructions with exact tool calls:
song_skeleton(scenes and locators per section, positions computed from the bar counts, optional tempo and tracks),chord_progression(Roman numerals in a key — default Live's current scale — or chord names, voiced and timed by the server into oneclip_createcall) anddrum_beat(a style's step patterns as oneclip_create, with a kit fromsample_search+drum_kit_buildwhen the pads are empty). Arguments are optional;style,voicingand track names have completions.On
initializethe server sends short instructions (a digest of the skill, kept insrc/instructions.md): ids, beats, pitch and volume conventions, and what the API cannot do. Clients that support server instructions (Claude Code) add them to the agent's prompt.Every tool has a title and MCP annotations, so clients can auto-approve reads and single out destructive calls:
read-only (
readOnlyHint): the*_gettools,midi_clip_get_notes,parameter_find,device_preset_list,session_grid,song_audit,selection_wait,sample_search,dialog_result,batch_read(it refuses steps that are not read-only);setters (
*_set): non-destructive, idempotent — exceptparameter_setanddevice_set, which takedelta(added to the current value on every call), so they are not idempotent;creators (
*_create,*_duplicate,*_insert*,import_file,render_track_audio—persistcopies the render into the project): non-destructive;destructive (
destructiveHint): the deletes,track_clear_clips_in_range,midi_clip_set_notes,midi_clip_edit_notes,simpler_replace_sample,device_copyandclip_copy(movedeletes the original),device_preset_loadanddrum_kit_build(replace Simpler samples),device_preset_save(overwritereplaces a preset),arrangement_copy_range(overwrites its target range),audio_track_consolidate(clears its range),batch(as destructive as its steps);show_dialog(non-destructive) andexecute_command(destructive) are open-world.
Security
The endpoint binds to 127.0.0.1 only, so other computers cannot reach it; every program on
this computer can. Requests go through these checks, in order:
Host and Origin (DNS rebinding): the
Hostheader must be127.0.0.1,localhostor[::1](any port), and anOriginheader, if sent, must be anhttp(s)origin on one of those hosts (Origin: nullis refused). Anything else gets403with a JSON-RPC error, on every path. MCP clients such as Claude Code andmcp-remotesend a loopbackHostand noOrigin, so they are unaffected.GET /healthanswers without a token:ok,name,version,host_api_version,tool_count,read_only,auth_requiredanduptime_s— no paths, no settings beyond those, never the token (503andok: falsewhile config.json locks/mcp).Bearer token (optional,
auth_tokenin config.json):/mcpthen answers401withWWW-Authenticate: Bearerunless the request carriesAuthorization: Bearer <token>— before anything else about the request is looked at, its method and body included. The server keeps only the token's SHA-256 digest and compares digests withcrypto.timingSafeEqual. The token itself is stored in plaintext inconfig.json: anyone who can read that file (your user account) can use it, so treat the file like a password, and pick a long random token (at least 16 visible ASCII characters). It keeps out other users and programs that can reach the port but not your files.A
config.jsonwith an invalid security setting answers503with the reason (see Configuration)./mcponly servesPOST(GET/DELETEanswer405,Allow: POST; the server is stateless); request bodies over 4 MiB are refused with413.
To limit what a connected agent can do at all, set read_only or disabled_tools (see
Configuration): switched-off tools are not registered, so neither a direct
call nor batch can reach them.
Tools
Song
Tool | Description |
| Live Set state: tempo, scale, grid, tracks (with |
| Set song properties (tempo). |
| Read-only check for missing media files (audio clips, Simpler samples), Simplers without a sample, empty drum pads and duplicate track names, each entry with ids and location. |
Tracks
Tool | Description |
| Track detail: clip slots + clips, take lanes, arrangement clips, devices, mixer with values. Address by |
| Name / mute / solo / arm, by id, index or name; |
| New audio or MIDI track, optionally with a |
| Delete track (by id, index, or exact name). |
| Duplicate track. |
| Delete/truncate arrangement clips in a beat range. |
| Add a take lane to a track. |
| Rename take lanes ( |
Every tool above except take_lane_set takes the track as track_id, track_index or track_name.
Scenes, session & arrangement
Tool | Description |
| One session row: a scene (by id, index or name) with every regular track's clip slot and clip in it ( |
| The Session View as a compact track × scene matrix ( |
| A beat range of the arrangement ( |
| Copy a beat range of the arrangement to |
| Manage scenes, addressed by |
| Manage arrangement locators. Create with a |
Clips
Tool | Description |
| Create MIDI or audio clips in a session slot (by id, or track + |
| Full clip detail (audio: warp settings + markers; MIDI: note count) and its |
| Name, color, mute, looping, warping, warp mode — for one clip, several ( |
| Delete a session or arrangement clip. |
| Copy a clip to an empty session slot, an arrangement position or a take lane ( |
| Read MIDI notes: all, or those matching |
| Write notes: |
| Server-side note transforms — transpose, snap to scale, set fields, velocity scale/offset, time-shift, quantize (beats or Live's grid), swing, duration set/scale, legato, seeded humanize, delete — on the notes matching |
Devices & racks
Tool | Description |
| Device detail: parameters with ranges/value items ( |
| Insert a built-in Live device into a track (by id, index or name) or rack chain, optionally with initial |
| Switch a device on/off ( |
| Remove or copy devices (the duplicate lands right after the original). |
| Copy a device to another track or chain, or with |
| Named parameter snapshots (plus a Simpler's sample path) stored as JSON under the extension's storage directory. Load onto an existing device or insert a new one. Not Live presets. |
| Rack chain detail: devices + chain mixer and |
| Add a chain to a rack. |
| Set the MIDI note of a drum rack pad (a number or a name like |
| Swap the sample in a Simpler; |
| Build or update a Drum Rack kit from sample files: per pad |
Parameters & mixing
Tool | Description |
| Read device/mixer parameter values (batch) by id or by |
| Write parameter values (batch, single undo step, nothing written if any entry is invalid): each entry by id or |
| Search parameter names (and quantized choices) across the set, a track or a device, through racks; returns ids, devices and readable paths. |
| Volume / pan / sends of a track (by id, index or name) or chain, with parameter ids and unit hints. |
| Set volume / pan / sends (by index or |
Files & rendering
Tool | Description |
| Copy a file into the Live project. |
| Render the pre-FX audio of an audio track (by id, index or name; clip audio before the device chain) to a WAV in the extension's temp directory, which may be cleaned — |
| Render a range of an audio track (pre-FX), import it, clear the range and create one unwarped clip of the render there, optionally named and colored — Live's Consolidate for comps. Destructive. |
| Find audio files by words in their path, read-only, in the |
Selection
Tool | Description |
| The selection the user last handed over with the context-menu entry "MCP: Use as selection": ids, summaries and locations of the clicked object or selected clip slots, or an arrangement time selection (start/end beats, selected lanes with their overlapping clips). |
| Wait (up to |
UI & commands
Tool | Description |
| Show a modal dialog inside Live: a |
| The answer of a dialog (by |
| Invoke Extension Host commands, e.g. |
Batches
Tool | Description |
| Run up to 50 tool calls in order ( |
| The same for read-only tools only (any other step is refused); continues after a failed step by default. |
Resources
URI | Content |
| JSON: |
| JSON: |
| Markdown: the full agent guide ( |
Prompts
Prompt | Arguments (all optional) | Produces |
|
|
|
|
| The chords spelled and voiced, a track with an instrument if needed, and one |
|
| The pattern per pad, |
Skills
skills/ableton-live/SKILL.md is an installable agent skill
that teaches an MCP client how to use these tools well (id discovery flow, beats vs. seconds,
note-editing patterns, device workflows). The server sends a short digest of it as MCP
instructions anyway; the full skill adds the detailed workflows. Install it for Claude Code with:
mkdir -p ~/.claude/skills && cp -r skills/ableton-live ~/.claude/skills/Typical things you can ask a connected assistant to do:
"Create a 4-bar house drum pattern on a new MIDI track with a Drum Rack"
"Warp all clips on the Drums track in Complex Pro mode"
"Turn down every track that isn't the vocal bus by 3 dB"
"Build a song skeleton: intro, verse, chorus scenes with locators"
"Replace the sample in Simpler with /path/to/kick.wav and map it to C1"
"Build an 8-pad kit from the 808 samples in my sample folder" (
sample_search, thendrum_kit_build)"Repeat the chorus at bar 33" / "Put this clip in the arrangement at bar 17" (
arrangement_copy_range,clip_copy)"Is the vocal take clipping anywhere?" (
render_track_audio {analyze: true})"Bypass the compressor on the vocals and switch the bass filter to high-pass"
"Move the reverb before the EQ" (a lossy copy-and-delete: see
device_copy)"What happens in bars 17 to 33?" / "Check the set for missing samples"
"Quantize the clip I selected" (after right-clicking it → "MCP: Use as selection")
"Delete the unused scenes, but ask me in Live first" (
show_dialog {form, wait: false},dialog_result)
Limitations
The extension (and thus the MCP endpoint) only runs while Live is open.
Only built-in Live devices can be inserted; third-party plug-ins can't be loaded by the SDK. Devices cannot be moved, renamed or loaded from Live presets:
device_copyand the JSONdevice_preset_*tools rebuild a device from its parameter values only.No transport control (play/stop/record) or clip launching — the Extensions API v1.0.0 does not expose them. Same for browser access, parameter automation curves, track color, routing and sidechain, reordering or grouping tracks, creating return tracks, metering, editing clip markers or loops after creation, and the song time signature.
The user's selection can't be read directly: it is only available once the user hands it over with the "MCP: Use as selection" context-menu entry.
Clips can't be copied, moved or trimmed by the API:
clip_copyandarrangement_copy_rangecreate new clips. Audio copies are re-warped by Live (warp markers can't be set), clip envelopes, groove and launch settings are lost, and time can't be inserted into the arrangement. Racks can't delete chains, sodrum_kit_buildkeeps a chain it created even if its pad fails.sample_searchonly sees the folders it is given (sample_roots) and Live's default library folders — the API has no browser access.Dialogs are modal: they block Live's UI until the user closes them, and Live can't close them from code. A
show_dialogcall waits by default (wait: false+dialog_resultdon't).batchis not atomic: each step is its own undo step, and a failing step leaves the earlier ones applied. Live'swithinTransactiononly groups synchronous changes, so steps that wait for Live can't share one undo step.
Tests
npm test # E2E against a mock Extension Host: MCP client ↔ HTTP ↔ all tools,
# resources and prompts, extra instances for the config.json switches,
# then the bundle loaded in a bare VM context (test/vm-context.mjs)
node test/live-smoke.mjs # against a real running Live with the extension loaded
# (MCP_TOKEN=<token> when config.json sets an auth_token)The prompts' tool calls are checked by running them: every `tool {json}` in a prompt's text
without a "<...>" placeholder is called against the mock, and every tool in tools/list must
appear in README.md's tool tables and in the skill.
test/vm-context.mjs evaluates dist/extension.cjs the way the Extension Host does (no
global, no web globals), so a broken build.ts banner fails CI instead of failing in Live. It
also runs an SSE (progress) response inside that context; the real Extension Host has not been
tested with SSE responses yet (test/live-smoke.mjs checks them against Live) — if long calls
fail there, set "progress_notifications": false.
The live smoke test creates its own tracks/clips/devices, verifies every tool family (MIDI notes, warping, drum racks, rendering, ...), and deletes everything it created.
Extension Host quirks (worth knowing)
Two behaviors of the beta Extension Host that this project works around:
Bare VM context. Extensions are evaluated in a V8 context without
globalor web globals (Request,Response,ReadableStream,fetch,EventTarget, ...), which the MCP SDK needs at load time.build.tsinjects a banner that pulls them in from the main Node context (core-module functions are shared, so theirFunctionconstructor evaluates there). Seebuild.tsfor details.bigint numerics. The host returns
bigintfor some values the SDK types asnumber(clip colors, note pitches, ...).src/serialize.tsnormalizes withnum()before arithmetic/JSON.Asynchronous writes. SDK property setters (notes, names, values) return before Live has applied the change; a read within a few tens of ms can return the previous state. Agents rarely notice, but read-after-write tests must retry briefly (see
eventually()intest/live-smoke.mjs).
Also: if the dev Extension Host crashes, Live may refuse the next control-channel handshake
("bring-up timed out") — restart Live and run npm start again.
Troubleshooting
Endpoint not responding: check
curl http://127.0.0.1:8722/health. Right-click any track in Live — the "Ableton Live MCP: Status" context-menu action shows the endpoint the extension actually bound, or why it failed to start, and the path of itsconfig.json.selection_getreturns an error per item: the object was deleted or moved after the user chose "MCP: Use as selection", or Live passed something the extension could not resolve — ask the user to select again.Port already in use: if another process holds the configured port, the server logs the failure to
ExtensionHost.txtand does not start; the Status dialog shows the error (EADDRINUSE) and theconfig.jsonpath. Changeportthere — the file is read once at startup, so restart Live afterwards.403 Forbidden: the request's
HostorOriginis not a loopback address (see Security). Connect tohttp://127.0.0.1:<port>/mcporhttp://localhost:<port>/mcp.401 Unauthorized:
config.jsonhas anauth_tokenand the client did not send it asAuthorization: Bearer <token>(see Connect a client).503 Service unavailable: a security setting in
config.json(read_only,disabled_tools,auth_token) is invalid or misspelled, or the file exists but cannot be read or parsed; the error and the Status dialog say which. Fix it and restart Live.A tool is missing:
read_onlyordisabled_toolsinconfig.jsonswitched it off."A dialog is already open in Live": the server shows one dialog at a time.
dialog_resultreturns the open one's answer once the user closes it; if the user did close it and Live never reported it,show_dialog {force: true}opens the next one anyway.Failed tool calls are logged to
ExtensionHost.txtwith the tool name, duration and error message (never the arguments).Changed config.json but nothing happened: config is only read when the extension starts (
sample_rootsexcepted). Restart Live (or the dev Extension Host). Invalid values are logged with the prefix[ableton-live-mcp] config:and listed in the Status dialog.
License
MIT for the code in this repository. The vendor/ tarballs (Ableton Extensions SDK & CLI) are
Ableton's and covered by their own license (see sdk/LICENSE.md in the SDK distribution).
This server cannot be deployed
Maintenance
Related MCP Connectors
Remote MCP server for audio stem separation, denoise, reverb, and MIDI export.
MCP server for Producer/Riffusion AI music generation
Create, co-edit, analyze, publish, and export collaborative step-sequencer sessions through MCP.
Official MCP server for MAQAMI, a hotel and flight booking platform with 3M+ hotels. Search live hotel rates and flights, look up places, airports and hotel details, then prebook and book. Remote Streamable HTTP endpoint, no API key required.
Related MCP Servers
- FlicenseBqualityDmaintenanceMCP server for controlling Ableton Live, enabling AI assistants to interact with Live sessions through tools for track/clip/scene management, playback control, and device parameter adjustments.48-
- AlicenseNot gradedqualityCmaintenanceAn MCP server that exposes Ableton Live control (session state, transport, tracks, devices, clips, MIDI note editing) as tools for LLM agents, enabling natural language manipulation of a Live session.1MIT
- AlicenseBqualityBmaintenanceLocal MCP server for inspecting and controlling Ableton Live through a local HTTP bridge. Enables LLMs to perform production workflows like MIDI import, track editing, mixing, mastering, and export.5921 npmMIT
- FlicenseBqualityDmaintenanceMCP server for Ableton Live 12 enabling full DAW control (tracks, devices, mixer, clips, transport) and audio analysis via loopback capture, allowing LLMs to create, modify, and measure music in real time.622-