Add return_track / duplicate_track / capture_midi / tap_tempo / set_track_color#94
Add return_track / duplicate_track / capture_midi / tap_tempo / set_track_color#94lukegimza wants to merge 1 commit into
Conversation
…rack_color Five small additions to expose LOM methods that aren't currently surfaced. - create_return_track — long-requested. Live's LOM has Song.create_return_track(); just wires it through. - duplicate_track — Song.duplicate_track(index). Useful for layering tracks. - capture_midi — Song.capture_midi(). Recovers MIDI played in the recent past on the armed track. - tap_tempo — Song.tap_tempo(). Registers a tap; Live settles to the tap interval. - set_track_color — accepts color_index (Live 10+ palette 0-69, preferred) or RGB integer (legacy fallback). Each handler is added to the existing main_thread_task dispatch (state-changing, needs scheduled execution on Live's main thread). Server-side @mcp.tool() wrappers mirror the existing style — one short function per command that calls ableton.send_command. No changes to existing handlers. Strictly additive.
📝 WalkthroughWalkthroughAdds Ableton operations (create return track, duplicate track, capture MIDI, tap tempo, set track color) implemented as main-thread helpers in AbletonMCP and exposes matching MCP_Server tool endpoints that send commands and return formatted responses. ChangesAbleton command extensions
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Review Summary by QodoFix browser item URI lookup for plugins and additional categories
WalkthroughsDescription• Extend browser item URI lookup to include plugins and additional categories • Add hasattr guards to safely access optional browser attributes • Fix issue where URIs from get_browser_items_at_path for plugins failed to load • Align URI lookup coverage with available browser navigation paths Diagramflowchart LR
A["_find_browser_item_by_uri"] -->|"walks categories"| B["instruments, sounds, drums,<br/>audio_effects, midi_effects"]
A -->|"NEW: walks extra categories"| C["plugins, max_for_live,<br/>user_library, packs, samples"]
B -->|"returns"| D["BrowserItem or None"]
C -->|"returns"| D
D -->|"enables"| E["load_browser_item for plugins"]
File Changes1. AbletonMCP_Remote_Script/__init__.py
|
Code Review by Qodo
1. Swallowed category access errors
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@AbletonMCP_Remote_Script/__init__.py`:
- Around line 787-790: The code currently swallows all exceptions when looking
up optional attributes (categories.append(getattr(browser_or_item, extra_attr)))
which hides failures; change the except block to catch Exception as e and log
the failure (including extra_attr and a representation of browser_or_item and
the exception details) before continuing so URI resolution issues are
diagnosable—use an existing logger or the logging module and prefer
logger.warning or logger.exception to record the context and stack/err text
while leaving the flow unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 42ff9b8a-6a77-46e4-929a-c7602d99e284
📒 Files selected for processing (1)
AbletonMCP_Remote_Script/__init__.py
| try: | ||
| categories.append(getattr(browser_or_item, extra_attr)) | ||
| except Exception: | ||
| pass |
There was a problem hiding this comment.
Don’t silently swallow optional-category lookup failures.
If an optional browser root access fails here, URI resolution quietly loses coverage with no trace. Log and continue so failures are diagnosable.
Proposed fix
for extra_attr in ('plugins', 'max_for_live', 'user_library', 'packs', 'samples'):
if hasattr(browser_or_item, extra_attr):
try:
categories.append(getattr(browser_or_item, extra_attr))
- except Exception:
- pass
+ except Exception as e:
+ self.log_message(
+ "Skipping browser root '{0}': {1}".format(extra_attr, str(e))
+ )📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| try: | |
| categories.append(getattr(browser_or_item, extra_attr)) | |
| except Exception: | |
| pass | |
| try: | |
| categories.append(getattr(browser_or_item, extra_attr)) | |
| except Exception as e: | |
| self.log_message( | |
| "Skipping browser root '{0}': {1}".format(extra_attr, str(e)) | |
| ) |
🧰 Tools
🪛 Ruff (0.15.12)
[error] 789-790: try-except-pass detected, consider logging the exception
(S110)
[warning] 789-789: Do not catch blind exception: Exception
(BLE001)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@AbletonMCP_Remote_Script/__init__.py` around lines 787 - 790, The code
currently swallows all exceptions when looking up optional attributes
(categories.append(getattr(browser_or_item, extra_attr))) which hides failures;
change the except block to catch Exception as e and log the failure (including
extra_attr and a representation of browser_or_item and the exception details)
before continuing so URI resolution issues are diagnosable—use an existing
logger or the logging module and prefer logger.warning or logger.exception to
record the context and stack/err text while leaving the flow unchanged.
9415157 to
a85de1f
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
MCP_Server/server.py (1)
104-109:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd the new state-mutating commands to
is_modifying_command.The new commands (
create_return_track,duplicate_track,set_track_color, and arguablycapture_midi/tap_tempo) all modify Live state but are not in this allowlist, so they bypass the 100ms pre/post buffer and the extended 15s timeout that other mutators rely on. The Remote Script already routes them through the main-thread queue (seeAbletonMCP_Remote_Script/__init__.pylines 229-234), so it is worth keeping the two sides in sync.♻️ Proposed update
is_modifying_command = command_type in [ "create_midi_track", "create_audio_track", "set_track_name", "create_clip", "add_notes_to_clip", "set_clip_name", "set_tempo", "fire_clip", "stop_clip", "set_device_parameter", - "start_playback", "stop_playback", "load_instrument_or_effect" + "start_playback", "stop_playback", "load_instrument_or_effect", + "create_return_track", "duplicate_track", "set_track_color", + "capture_midi", "tap_tempo" ]🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@MCP_Server/server.py` around lines 104 - 109, The is_modifying_command allowlist in MCP_Server/server.py currently omits several Live state-mutating commands; update the list used to compute is_modifying_command to include "create_return_track", "duplicate_track", "set_track_color" and also consider adding "capture_midi" and "tap_tempo" if you want them treated the same way; modify the array literal where is_modifying_command is assigned so these string command names are present (the variable name is is_modifying_command and the surrounding context checks command_type), ensuring these commands will use the same 100ms pre/post buffer and extended 15s timeout behavior as the other mutators.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@AbletonMCP_Remote_Script/__init__.py`:
- Around line 704-730: The method _set_track_color currently only checks
color_index >= 0 but not the upper bound; add a range check such that if
color_index is provided it must be between 0 and 69 inclusive (and raise
ValueError with a clear message if out of range) before assigning
track.color_index, leaving the existing fallback to track.color intact; update
the validation around color_index/int conversion near the start of
_set_track_color and ensure the error is logged via self.log_message and
re-raised as the existing except block does.
---
Outside diff comments:
In `@MCP_Server/server.py`:
- Around line 104-109: The is_modifying_command allowlist in
MCP_Server/server.py currently omits several Live state-mutating commands;
update the list used to compute is_modifying_command to include
"create_return_track", "duplicate_track", "set_track_color" and also consider
adding "capture_midi" and "tap_tempo" if you want them treated the same way;
modify the array literal where is_modifying_command is assigned so these string
command names are present (the variable name is is_modifying_command and the
surrounding context checks command_type), ensuring these commands will use the
same 100ms pre/post buffer and extended 15s timeout behavior as the other
mutators.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b3440d89-00af-4722-9a8c-b8df256bd716
📒 Files selected for processing (2)
AbletonMCP_Remote_Script/__init__.pyMCP_Server/server.py
| def _set_track_color(self, track_index, color_index, color): | ||
| """Set track color. | ||
|
|
||
| ``color_index`` is the preferred Live 10+ palette index (0-69). | ||
| ``color`` (RGB integer 0xRRGGBB) is the legacy fallback. | ||
| Pass -1 for whichever you're not using. | ||
| """ | ||
| try: | ||
| if track_index < 0 or track_index >= len(self._song.tracks): | ||
| raise IndexError("Track index out of range") | ||
| track = self._song.tracks[track_index] | ||
| color_index = int(color_index) | ||
| color = int(color) | ||
| if color_index >= 0: | ||
| track.color_index = color_index | ||
| elif color >= 0: | ||
| track.color = color | ||
| else: | ||
| raise ValueError("Provide color_index (0-69) or color (RGB int).") | ||
| return { | ||
| "track_index": track_index, | ||
| "color_index": getattr(track, 'color_index', None), | ||
| "color": getattr(track, 'color', None), | ||
| } | ||
| except Exception as e: | ||
| self.log_message("Error setting track color: " + str(e)) | ||
| raise |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Ableton Live Object Model track.color_index valid range palette
💡 Result:
In Ableton Live's Live Object Model (LOM), used in Max for Live and Python Remote Scripts APIs, the Track object has a 'color_index' property, which is an integer representing the index of the track's color in Ableton's predefined color palette [1][2][3]. The valid range for track.color_index is 0 to 69 (inclusive), corresponding to the 70 colors available in the palette as of Live 11 and later [4][5][6]. This is evidenced by Ableton Remote Scripts for controllers like APC64 mapping color_index 0-69 to specific RGB values [5], Python scripts assigning indices up to 69 [6][7], and user reports confirming 70 selectable colors [4][8]. Setting color_index to values outside this range may snap to the nearest palette color or be invalid, similar to how arbitrary RGB values are quantized to the palette when setting the 'color' property (an int in 0x00rrggbb format) [9][2]. Official Cycling '74 LOM docs confirm the property exists but do not specify the range explicitly [2].
Citations:
- 1: https://ricardomatias.net/ableton-live/interfaces/TrackGetProperties.html
- 2: https://docs.cycling74.com/apiref/lom/track
- 3: https://docs.cycling74.com/apiref/lom/clip/
- 4: https://www.reddit.com/r/ableton/comments/lqw3w9/swatches_m4l_manage_the_colors_of_all_the_clips/
- 5: https://github.com/gluon/AbletonLive12_MIDIRemoteScripts/blob/810ef774/APC64/colors.py
- 6: https://github.com/CoryWBoris/AbletonAutoColor
- 7: https://github.com/J0rgeSerran0/Ableton-Track-Color-Changer
- 8: https://forum.ableton.com/viewtopic.php?t=229657
- 9: https://cycling74.com/forums/getting-the-color-of-a-track-in-m4l-api-is-it-possible
🏁 Script executed:
# Search for master track or return track color handling in the file
rg -n "master|return" AbletonMCP_Remote_Script/__init__.py | head -30
# Also search for other color-related methods
rg -n "def.*color" AbletonMCP_Remote_Script/__init__.pyRepository: ahujasid/ableton-mcp
Length of output: 1525
🏁 Script executed:
# Check if there are other methods that set colors on master or return tracks
rg -A 10 "def.*color" AbletonMCP_Remote_Script/__init__.py
# Look for any code that sets color on master_track or return_tracks
rg "master_track.*color|return_tracks.*color" AbletonMCP_Remote_Script/__init__.pyRepository: ahujasid/ableton-mcp
Length of output: 553
🏁 Script executed:
# Check what track operations are available and their scope
rg -B 2 -A 15 "def _get_track_info|def _set_track" AbletonMCP_Remote_Script/__init__.py | head -60
# Also check the overall structure of track handling
rg -n "_song\.tracks|_song\.return_tracks|_song\.master_track" AbletonMCP_Remote_Script/__init__.py | head -20Repository: ahujasid/ableton-mcp
Length of output: 3353
Validate color_index upper bound (0-69).
The color_index parameter is checked for >= 0 but not against the upper bound of 69. Ableton Live's palette supports exactly 70 colors (indices 0–69). A simple range check prevents silent truncation or unexpected behavior when an out-of-range index is passed:
Proposed range guard
track = self._song.tracks[track_index]
color_index = int(color_index)
color = int(color)
if color_index >= 0:
+ if color_index > 69:
+ raise ValueError("color_index must be in 0-69")
track.color_index = color_index
elif color >= 0:
track.color = color
else:
raise ValueError("Provide color_index (0-69) or color (RGB int).")Note: This method only indexes self._song.tracks (regular tracks) and does not support return or master tracks, though they also have color_index properties. If master/return track coloring is needed, either expand the lookup or document this scope in the docstring.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@AbletonMCP_Remote_Script/__init__.py` around lines 704 - 730, The method
_set_track_color currently only checks color_index >= 0 but not the upper bound;
add a range check such that if color_index is provided it must be between 0 and
69 inclusive (and raise ValueError with a clear message if out of range) before
assigning track.color_index, leaving the existing fallback to track.color
intact; update the validation around color_index/int conversion near the start
of _set_track_color and ensure the error is logged via self.log_message and
re-raised as the existing except block does.
Five small additions to expose LOM methods that aren't currently surfaced. All strictly additive — no changes to existing handlers.
Added commands
create_return_trackSong.create_return_track()duplicate_trackSong.duplicate_track(index)capture_midiSong.capture_midi()tap_tempoSong.tap_tempo()set_track_colortrack.color_index/track.colorImplementation
main_thread_taskdispatch (state-changing, needs scheduled execution on Live's main thread).@mcp.tool()wrappers mirror the existing style — one short function per command that callsableton.send_command.Testing
Verified all five on Live 12 Suite (macOS):
create_return_trackadds a return at the end;return_tracks_countincrements correctly.duplicate_trackproduces a copy at index+1 with devices and clips preserved.capture_midirecovers MIDI from the armed track's recent buffer.tap_tempoaccepts a single tap (multiple taps settle to the tap interval).set_track_coloraccepts both the Live 10+ palette index (preferred) and the legacy RGB integer.Not included
group_tracks/ungroup_track— Live's Python LOM doesn't expose these even thoughSong.group_tracksexists as a Push-3 message internally. They're UI-only operations, so no wiring is possible from the Remote Script. Manual Cmd+G remains the only path for grouping.Summary by CodeRabbit