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:
@@ -9,20 +9,61 @@ def register(mcp):
|
|||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def browser_get_items(category: str) -> list:
|
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.
|
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:
|
if category not in _CATEGORIES:
|
||||||
raise ValueError(f"category must be one of: {', '.join(_CATEGORIES)}")
|
raise ValueError(f"category must be one of: {', '.join(_CATEGORIES)}")
|
||||||
result = get_client().query(f"/live/browser/get/{category}")
|
result = get_client().query(f"/live/browser/get/{category}")
|
||||||
return list(result)
|
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()
|
@mcp.tool()
|
||||||
def browser_load_item(*path_parts: str) -> None:
|
def browser_load_item(*path_parts: str) -> None:
|
||||||
"""Load a browser item into the currently selected track/slot.
|
"""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)
|
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()
|
@mcp.tool()
|
||||||
def browser_preview_item(*path_parts: str) -> None:
|
def browser_preview_item(*path_parts: str) -> None:
|
||||||
"""Preview a browser item (plays a sample or preset preview)."""
|
"""Preview a browser item (plays a sample or preset preview)."""
|
||||||
@@ -41,5 +82,6 @@ def register(mcp):
|
|||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
def browser_begin_hotswap(track_index: int, device_index: int) -> None:
|
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)
|
get_client().cmd("/live/browser/begin_hotswap", track_index, device_index)
|
||||||
|
|||||||
Reference in New Issue
Block a user