diff --git a/AbletonMCP_Remote_Script/__init__.py b/AbletonMCP_Remote_Script/__init__.py index 45ede99e..a8662884 100644 --- a/AbletonMCP_Remote_Script/__init__.py +++ b/AbletonMCP_Remote_Script/__init__.py @@ -225,6 +225,10 @@ def _process_command(self, command): elif command_type == "get_track_info": track_index = params.get("track_index", 0) response["result"] = self._get_track_info(track_index) + elif command_type == "get_rack_device_info": + track_index = params.get("track_index", 0) + device_index = params.get("device_index", 0) + response["result"] = self._get_rack_device_info(track_index, device_index) # Commands that modify Live's state should be scheduled on the main thread elif command_type in ["create_midi_track", "set_track_name", "create_clip", "add_notes_to_clip", "set_clip_name", @@ -389,12 +393,19 @@ def _get_track_info(self, track_index): # Get devices devices = [] for device_index, device in enumerate(track.devices): - devices.append({ + device_info = { "index": device_index, "name": device.name, "class_name": device.class_name, "type": self._get_device_type(device) - }) + } + if device.can_have_chains: + device_info["chains"] = [{ + "index": i, + "name": chain.name, + "device_count": len(chain.devices) + } for i, chain in enumerate(device.chains)] + devices.append(device_info) result = { "index": track_index, @@ -414,6 +425,49 @@ def _get_track_info(self, track_index): self.log_message("Error getting track info: " + str(e)) raise + def _serialize_device(self, device): + """Serialize a device, recursively including chains for rack devices""" + device_info = { + "name": device.name, + "class_name": device.class_name, + "type": self._get_device_type(device) + } + if device.can_have_chains: + chains = [] + for chain_index, chain in enumerate(device.chains): + chain_devices = [self._serialize_device(dev) for dev in chain.devices] + chains.append({ + "index": chain_index, + "name": chain.name, + "devices": chain_devices + }) + device_info["chains"] = chains + return device_info + + def _get_rack_device_info(self, track_index, device_index): + """Get detailed information about a rack device's chains and nested devices""" + 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] + + if device_index < 0 or device_index >= len(track.devices): + raise IndexError("Device index out of range") + + device = track.devices[device_index] + + if not device.can_have_chains: + raise ValueError("Device '{}' is not a rack and does not have chains".format(device.name)) + + result = self._serialize_device(device) + result["track_index"] = track_index + result["device_index"] = device_index + return result + except Exception as e: + self.log_message("Error getting rack device info: " + str(e)) + raise + def _create_midi_track(self, index): """Create a new MIDI track at the specified index""" try: diff --git a/MCP_Server/server.py b/MCP_Server/server.py index fdddb478..9bf5901e 100644 --- a/MCP_Server/server.py +++ b/MCP_Server/server.py @@ -284,6 +284,23 @@ def get_track_info(ctx: Context, track_index: int) -> str: logger.error(f"Error getting track info from Ableton: {str(e)}") return f"Error getting track info: {str(e)}" +@mcp.tool() +def get_rack_device_info(ctx: Context, track_index: int, device_index: int) -> str: + """ + Get detailed information about a rack device's chains and nested devices. + + Parameters: + - track_index: The index of the track containing the rack device + - device_index: The index of the device on the track (must be a rack) + """ + try: + ableton = get_ableton_connection() + result = ableton.send_command("get_rack_device_info", {"track_index": track_index, "device_index": device_index}) + return json.dumps(result, indent=2) + except Exception as e: + logger.error(f"Error getting rack device info from Ableton: {str(e)}") + return f"Error getting rack device info: {str(e)}" + @mcp.tool() def create_midi_track(ctx: Context, index: int = -1) -> str: """