Add browser_swap_device_preset and browser_list_children tools

browser_swap_device_preset(track, device, *path) hotswaps a preset onto
an existing device without touching clips — fixes the clip-deletion bug
that occurred when loading presets directly via browser_load_item.

browser_list_children(category, *path) exposes the new /live/browser/list_children
OSC endpoint for navigating the browser hierarchy before loading.

Also documents the clip-deletion risk on browser_load_item.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 13:38:03 +02:00
parent 5c274b9335
commit 668195b880
+46 -4
View File
@@ -9,20 +9,61 @@ def register(mcp):
@mcp.tool()
def browser_get_items(category: str) -> list:
"""List items in a browser category.
"""List top-level items in a browser category.
category: audio_effects | instruments | midi_effects | samples | sounds | clips | packs | plugins.
Returns a flat list of item path strings."""
Returns a flat list of child names. Use browser_list_children to navigate deeper."""
if category not in _CATEGORIES:
raise ValueError(f"category must be one of: {', '.join(_CATEGORIES)}")
result = get_client().query(f"/live/browser/get/{category}")
return list(result)
@mcp.tool()
def browser_list_children(category: str, *path_parts: str) -> list:
"""List children at a specific path inside a browser category.
Use this to navigate the browser hierarchy before loading.
Examples:
browser_list_children('packs') -> ['Core Library']
browser_list_children('packs', 'Core Library', 'Racks') -> ['Audio Effect Racks', 'Drum Racks', ...]
browser_list_children('packs', 'Core Library', 'Racks', 'Drum Racks', 'Drum Machines')
-> ['505 Core Kit.adg', '808 Core Kit.adg', '909 Core Kit.adg', ...]
browser_list_children('instruments', 'Drum Rack') -> preset names inside Drum Rack
"""
if category not in _CATEGORIES:
raise ValueError(f"category must be one of: {', '.join(_CATEGORIES)}")
result = get_client().query("/live/browser/list_children", category, *path_parts)
return list(result)
@mcp.tool()
def browser_load_item(*path_parts: str) -> None:
"""Load a browser item into the currently selected track/slot.
Pass the item path as individual strings, e.g. browser_load_item('Instruments', 'Drift', 'Init')."""
WARNING: loading a full preset (.adg) from packs replaces the entire track and may
delete existing clips. Use browser_swap_device_preset to safely hot-swap a device
preset without touching clips.
Pass path as individual strings searched across all browser categories:
browser_load_item('Drum Rack')
browser_load_item('Core Library', 'Racks', 'Drum Racks', 'Drum Machines', '808 Core Kit.adg')
"""
get_client().cmd("/live/browser/load_item", *path_parts)
@mcp.tool()
def browser_swap_device_preset(
track_index: int, device_index: int, *path_parts: str
) -> None:
"""Safely swap a device preset without touching clips on the track.
Targets the device for hotswap first, then loads the new preset — clips are preserved.
Example — swap the Drum Rack on track 0 to the 808 Core Kit:
browser_swap_device_preset(0, 0,
'Core Library', 'Racks', 'Drum Racks', 'Drum Machines', '808 Core Kit.adg')
Use browser_list_children to discover available preset paths first.
"""
c = get_client()
c.cmd("/live/browser/begin_hotswap", track_index, device_index)
c.cmd("/live/browser/load_item", *path_parts)
@mcp.tool()
def browser_preview_item(*path_parts: str) -> None:
"""Preview a browser item (plays a sample or preset preview)."""
@@ -41,5 +82,6 @@ def register(mcp):
@mcp.tool()
def browser_begin_hotswap(track_index: int, device_index: int) -> None:
"""Begin hotswap mode for a device — subsequent browser_load_item calls replace it."""
"""Target a device for hotswap. The next browser_load_item call will replace
only that device and leave all clips intact."""
get_client().cmd("/live/browser/begin_hotswap", track_index, device_index)