get_browser_items_at_path
Retrieve browser items from Ableton Live at a specified path to access instruments, samples, and effects for music production workflows.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes |
Implementation Reference
- MCP_Server/server.py:453-481 (handler)MCP tool handler for 'get_browser_items_at_path'. Proxies the request to Ableton remote script via socket and returns formatted JSON response with error handling.@mcp.tool() def get_browser_items_at_path(ctx: Context, path: str) -> str: try: ableton = get_ableton_connection() result = ableton.send_command("get_browser_items_at_path", {"path": path}) if "error" in result and "available_categories" in result: error = result.get("error", "") available_cats = result.get("available_categories", []) return (f"Error: {error}\n" f"Available browser categories: {', '.join(available_cats)}") return json.dumps(result, indent=2) except Exception as e: error_msg = str(e) if "Browser is not available" in error_msg: logger.error(f"Browser is not available in Ableton: {error_msg}") return f"Error: The Ableton browser is not available. Make sure Ableton Live is fully loaded and try again." elif "Could not access Live application" in error_msg: logger.error(f"Could not access Live application: {error_msg}") return f"Error: Could not access the Ableton Live application. Make sure Ableton Live is running and the Remote Script is loaded." elif "Unknown or unavailable category" in error_msg: logger.error(f"Invalid browser category: {error_msg}") return f"Error: {error_msg}. Please check the available categories using get_browser_tree." elif "Path part" in error_msg and "not found" in error_msg: logger.error(f"Path not found: {error_msg}") return f"Error: {error_msg}. Please check the path and try again." else: logger.error(f"Error getting browser items at path: {error_msg}") return f"Error getting browser items at path: {error_msg}"
- Core implementation of browser items retrieval in Ableton Live remote script. Navigates browser path using Live API (app.browser) and lists child items with their properties.def get_browser_items_at_path(self, path): """ Get browser items at a specific path. Args: path: Path in the format "category/folder/subfolder" where category is one of: instruments, sounds, drums, audio_effects, midi_effects or any other available browser category Returns: Dictionary with items at the specified path """ try: # Access the application's browser instance instead of creating a new one app = self.application() if not app: raise RuntimeError("Could not access Live application") # Check if browser is available if not hasattr(app, 'browser') or app.browser is None: raise RuntimeError("Browser is not available in the Live application") # Log available browser attributes to help diagnose issues browser_attrs = [attr for attr in dir(app.browser) if not attr.startswith('_')] self.log_message("Available browser attributes: {0}".format(browser_attrs)) # Parse the path path_parts = path.split("/") if not path_parts: raise ValueError("Invalid path") # Determine the root category root_category = path_parts[0].lower() current_item = None # Check standard categories first if root_category == "instruments" and hasattr(app.browser, 'instruments'): current_item = app.browser.instruments elif root_category == "sounds" and hasattr(app.browser, 'sounds'): current_item = app.browser.sounds elif root_category == "drums" and hasattr(app.browser, 'drums'): current_item = app.browser.drums elif root_category == "audio_effects" and hasattr(app.browser, 'audio_effects'): current_item = app.browser.audio_effects elif root_category == "midi_effects" and hasattr(app.browser, 'midi_effects'): current_item = app.browser.midi_effects else: # Try to find the category in other browser attributes found = False for attr in browser_attrs: if attr.lower() == root_category: try: current_item = getattr(app.browser, attr) found = True break except Exception as e: self.log_message("Error accessing browser attribute {0}: {1}".format(attr, str(e))) if not found: # If we still haven't found the category, return available categories return { "path": path, "error": "Unknown or unavailable category: {0}".format(root_category), "available_categories": browser_attrs, "items": [] } # Navigate through the path for i in range(1, len(path_parts)): part = path_parts[i] if not part: # Skip empty parts continue if not hasattr(current_item, 'children'): return { "path": path, "error": "Item at '{0}' has no children".format('/'.join(path_parts[:i])), "items": [] } found = False for child in current_item.children: if hasattr(child, 'name') and child.name.lower() == part.lower(): current_item = child found = True break if not found: return { "path": path, "error": "Path part '{0}' not found".format(part), "items": [] } # Get items at the current path items = [] if hasattr(current_item, 'children'): for child in current_item.children: item_info = { "name": child.name if hasattr(child, 'name') else "Unknown", "is_folder": hasattr(child, 'children') and bool(child.children), "is_device": hasattr(child, 'is_device') and child.is_device, "is_loadable": hasattr(child, 'is_loadable') and child.is_loadable, "uri": child.uri if hasattr(child, 'uri') else None } items.append(item_info) result = { "path": path, "name": current_item.name if hasattr(current_item, 'name') else "Unknown", "uri": current_item.uri if hasattr(current_item, 'uri') else None, "is_folder": hasattr(current_item, 'children') and bool(current_item.children), "is_device": hasattr(current_item, 'is_device') and current_item.is_device, "is_loadable": hasattr(current_item, 'is_loadable') and current_item.is_loadable, "items": items } self.log_message("Retrieved {0} items at path: {1}".format(len(items), path)) return result except Exception as e: self.log_message("Error getting browser items at path: {0}".format(str(e))) self.log_message(traceback.format_exc()) raise
- MCP_Server/server.py:93-159 (helper)Helper method in AbletonConnection for sending commands to remote script over socket and receiving responses.def send_command(self, command_type: str, params: Dict[str, Any] = None) -> Dict[str, Any]: """Send a command to Ableton and return the response""" if not self.sock and not self.connect(): raise ConnectionError("Not connected to Ableton") command = { "type": command_type, "params": params or {} } # Commands that modify state (give them extra tolerance) 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", "load_browser_item" ] try: logger.info(f"Sending command: {command_type} with params: {params}") self.sock.sendall(json.dumps(command).encode('utf-8')) logger.info("Command sent, waiting for response...") # small delay for modifying commands (gives Ableton a moment) if is_modifying_command: import time time.sleep(0.1) timeout = 15.0 if is_modifying_command else 10.0 self.sock.settimeout(timeout) response_data = self.receive_full_response(self.sock) logger.info(f"Received {len(response_data)} bytes of data") response = json.loads(response_data.decode('utf-8')) logger.info(f"Response parsed, status: {response.get('status', 'unknown')}") if response.get("status") == "error": logger.error(f"Ableton error: {response.get('message')}") raise Exception(response.get("message", "Unknown error from Ableton")) if is_modifying_command: import time time.sleep(0.1) return response.get("result", {}) except socket.timeout: logger.error("Socket timeout while waiting for response from Ableton") self.sock = None raise Exception("Timeout waiting for Ableton response") except (ConnectionError, BrokenPipeError, ConnectionResetError) as e: logger.error(f"Socket connection error: {str(e)}") self.sock = None raise Exception(f"Connection to Ableton lost: {str(e)}") except json.JSONDecodeError as e: logger.error(f"Invalid JSON response from Ableton: {str(e)}") if 'response_data' in locals() and response_data: logger.error(f"Raw response (first 200 bytes): {response_data[:200]}") self.sock = None raise Exception(f"Invalid response from Ableton: {str(e)}") except Exception as e: logger.error(f"Error communicating with Ableton: {str(e)}") self.sock = None raise Exception(f"Communication error with Ableton: {str(e)}")
- AbletonMCP_Remote_Script/__init__.py:415-419 (registration)Command dispatch/registration in remote script's _process_command method routing "get_browser_items_at_path" to the handler function.elif command_type == "get_browser_items_at_path": path = params.get("path", "") response["result"] = self.get_browser_items_at_path(path) else: response["status"] = "error"