ableton-mcp-extended

ableton-mcp-extended is my fork of uisato/ableton-mcp-extended, which is itself a fork of ahujasid/ableton-mcp. The project exposes Ableton Live as an MCP server so an AI assistant (Claude, Cursor, etc.) can control a live session in natural language. My contribution sits on top of all that: a structured save-generation / load-generation workflow for musical iterations.

Background — what the upstream project does

Two processes, one socket.

  • MCP_Server/server.py runs on the host as a FastMCP server with ~45 @mcp.tool() endpoints (transport, tracks, clips, devices, browser, arrangement, cue points, plugin discovery, etc.). When an AI client calls a tool, it builds a JSON command, sends it over TCP to localhost:9877, parses the response, returns a string.
  • AbletonMCP_Remote_Script/__init__.py runs inside Ableton as a _Framework.ControlSurface subclass. It listens on TCP 9877, dispatches each command to a _<command> method, and (critically) marshals state-modifying calls onto the main thread via schedule_message(0, ...). Read-only calls run inline.

So when you type "create a 4-bar MIDI clip on track 2 with a C major arpeggio," the assistant decomposes that into MCP tool calls, the host server JSON-encodes them, and the in-Ableton script applies them to the Live song model on the audio thread. Round-trip is fast enough that "switching from chat to seeing the change in Live" feels instantaneous.

If you want a deeper look at the architecture, the project's CLAUDE.md is the best reference — including the tripwires (the dual whitelist for state-modifying commands at server.py:107 and __init__.py:229, the 1-based vs 0-based index convention, and the macOS Live 11+ Remote Script path that the README still gets wrong).

What was missing — and what I added

Once you're driving Ableton with an AI, you start producing musical iterations fast. "Try the bass an octave up." "Swap the synth for a felted piano." "Add a bridge." Within an hour you have a dozen versions you'd like to A/B, and three problems become obvious:

  1. .als save is one slot. Cmd+S overwrites. Live's "Save A Copy" is awkward and unstructured.
  2. Git is the wrong tool. A .als is a binary archive — diffs are useless, branches are ceremony, and the intent of a musical revision ("changed the bass") doesn't belong in commit messages.
  3. Crashes happen. Especially when you're loading external plugins via MCP. Lose a session and the AI's state in chat is now decoupled from anything in Live.

So I added a third axis of saving — orthogonal to git and to .als. The change landed as commit 70e65ee, introducing:

  • skills/ableton-generation/SKILL.md — the workflow definition.
  • skills/ableton-generation/references/manifest-template.md — the human-readable summary format.
  • skills/ableton-generation/references/state-schema.md — the machine-readable rebuild schema.
  • .claude/commands/save-generation.md/save-generation <label>.
  • .claude/commands/load-generation.md/load-generation <label>.
  • CLAUDE.md — documents both the existing ableton-songwriter skill and the new ableton-generation one.

How it works

/save-generation <label> snapshots the full creative state of the current Live session into a date-prefixed folder under generations/:

generations/
  2026-04-27_1430_bass-octave-up/
    MANIFEST.md          (human-readable: form, tracks, what changed)
    state.json           (machine-readable: rebuild source of truth)
    notes/
      drums_intro.json
      drums_a.json
      bass_a.json
      ...                (one JSON per session clip)

Under the hood the assistant calls get_session_info (tempo, time sig, track count), get_track_info for each track (name, devices, mixer state, clip slot summary), get_arrangement_info, and get_cue_points — in parallel where possible — then writes the directory. Per-clip notes are dumped into notes/*.json using the same schema add_notes_to_clip accepts: [{pitch, start_time, duration, velocity}, ...]. Plain JSON. No binary, no Ableton dependency to read it later.

/load-generation <label-or-substring> reverses the process: read MANIFEST + state.json, confirm with the user before mutating anything, then rebuild in dependency order — tempo/time signature first, then tracks, then instruments and effects, then mix, then session clips with their notes, then arrangement clips, then cue points.

The rules baked in

The skill is opinionated on purpose. The non-obvious ones:

  • Explicit invocation only. Never auto-save. The assistant may offer a save after a meaningful musical change but never pulls the trigger itself. Music decisions are deliberate; treating them like autosave undermines the point.
  • Never overwrite. Folder name collision → append -2, -3. Old generations are immutable.
  • Don't git add generations/. Generative saves are a separate axis from code commits. They sit under the project root but aren't part of dev PRs unless you specifically ask.
  • Always remind the user to Cmd+S the .als. Claude can't reach into Ableton to trigger a project save. The snapshot captures state at a point in time; the .als is what you'll actually open next time. Keep them in sync.
  • Be honest about gaps. If notes for a clip aren't recoverable (because they weren't in conversation context and Live didn't return them cleanly), say so in MANIFEST under "Limitations" — don't pretend the snapshot is complete.

The full set of guardrails and the save/load steps live in skills/ableton-generation/SKILL.md.

The three-axis model

ConcernToolTrigger
Music iteration/save-generationexplicit user invocation
Code changegitgit commit after edits
Ableton project file.alsuser hits Cmd+S in Live

These are orthogonal. A single session may produce all three; they don't interfere. The mental model is: git for how the tool works, .als for the binary file Live opens, generations for the music you're trying to remember.

Status

Single commit so far (70e65ee, Apr 27 2026), upstream of any merge with uisato. Works for me on Live 12 + macOS + Claude Code. Open issues / things I'd still like:

  • Auto-derive the "Changed from parent" field from the previous generation's state.json instead of asking the user.
  • A /diff-generation A B command that summarizes changes without rebuilding.
  • A way to capture per-clip notes for arrangement (not just session) clips reliably.

Repo

github.com/jtmack6/ableton-mcp-extended — fork of uisato/ableton-mcp-extended (← fork of ahujasid/ableton-mcp).


This article was drafted by Claude (Anthropic's Claude Code, Opus 4.7) from the project's CLAUDE.md, the ableton-generation SKILL definition, and the 70e65ee commit message — the ideas and the code are mine; the prose stitching is Claude's.