Skip to content

Add more useful commands#16

Closed
Jakeway wants to merge 8 commits intoahujasid:mainfrom
Jakeway:feature/add_more_commands
Closed

Add more useful commands#16
Jakeway wants to merge 8 commits intoahujasid:mainfrom
Jakeway:feature/add_more_commands

Conversation

@Jakeway
Copy link
Copy Markdown

@Jakeway Jakeway commented Apr 8, 2025

User description

I saw this PR and thought these commands are super useful and wanted to get the code in a better working state than what I saw it in. This PR cleans up a lot of extra stuff from that PR to only really include what's needed. Happy to continue working towards improving this code if you see anything out of the ordinary.


PR Type

Enhancement, Bug fix


Description

  • Added new commands for device and clip management.

  • Enhanced state-modifying command handling with centralized list.

  • Improved error handling and logging for command execution.

  • Removed deprecated or redundant commands for cleaner codebase.


Changes walkthrough 📝

Relevant files
Enhancement
__init__.py
Enhanced device and clip management in Ableton MCP script

AbletonMCP_Remote_Script/init.py

  • Added new commands for device and clip management.
  • Centralized state-modifying commands into a single list.
  • Enhanced error handling and logging for device and clip operations.
  • Removed redundant commands like set_clip_name.
  • +402/-47
    server.py
    Updated server to support new device and clip commands     

    MCP_Server/server.py

  • Integrated new commands for device and clip management.
  • Centralized state-modifying commands for consistency.
  • Improved logging and error handling for server commands.
  • Removed deprecated set_clip_name command.
  • +236/-32

    Need help?
  • Type /help how to ... in the comments thread for any questions about Qodo Merge usage.
  • Check out the documentation for more information.
  • Summary by CodeRabbit

    • New Features
      • Added remote controls for viewing detailed master track information, retrieving device parameters, modifying clip properties in bulk, and searching browser items.
    • Bug Fixes
      • Enhanced error handling with clearer messaging for connection issues, timeouts, and response errors to improve overall system reliability.

    @coderabbitai
    Copy link
    Copy Markdown

    coderabbitai Bot commented Apr 8, 2025

    Walkthrough

    The changes refactor how state-modifying commands are processed in both the remote script and server components. A centralized list is now used to identify these commands, and dedicated methods have been added to handle various functionalities such as retrieving master track info, device parameters, and setting clip properties. Additionally, some legacy command handling (like setting clip names directly) has been removed, and error handling in the server for socket and JSON issues has been enhanced.

    Changes

    File(s) Change Summary
    AbletonMCP_Remote.../__init__.py Introduced new list STATE_MODIFYING_COMMANDS to consolidate state-modifying commands. Updated _process_command to support "get_master_track_info" and route commands to new methods. Added methods: _get_master_track_info, _get_device_parameters, _set_clip_properties, _set_device_parameters, _search_browser_items, and _get_item_path. Removed legacy commands ("set_clip_name", "load_instrument_or_effect").
    MCP_Server/server.py Added constant STATE_MODIFYING_COMMANDS and modified send_command to reference it, improving maintainability. Enhanced error handling by addressing socket timeouts, connection errors, and JSON decoding errors (with raw data logging). Introduced new command functions: get_device_parameters, get_master_track_info, search_browser_items, set_clip_properties, and set_device_parameters; removed set_clip_name.

    Sequence Diagram(s)

    sequenceDiagram
        participant Client
        participant AbletonMCP_Remote
        Client->>AbletonMCP_Remote: Send command (e.g., "get_master_track_info")
        AbletonMCP_Remote->>AbletonMCP_Remote: _process_command()
        AbletonMCP_Remote->>AbletonMCP_Remote: Check if command is in STATE_MODIFYING_COMMANDS
        alt "Command is state modifying"
            AbletonMCP_Remote->>AbletonMCP_Remote: Schedule on main thread
            AbletonMCP_Remote->>AbletonMCP_Remote: Call corresponding method (e.g., _get_master_track_info)
        else "Other command"
            AbletonMCP_Remote->>AbletonMCP_Remote: Process via existing flow
        end
        AbletonMCP_Remote-->>Client: Return response
    
    Loading
    sequenceDiagram
        participant Client
        participant MCP_Server
        Client->>MCP_Server: send_command("get_device_parameters", ...)
        MCP_Server->>MCP_Server: Check command against STATE_MODIFYING_COMMANDS
        MCP_Server->>MCP_Server: Call specific command function (e.g., get_device_parameters)
        MCP_Server->>MCP_Server: Handle errors (socket, JSON decoding)
        MCP_Server-->>Client: Return processed result or error message
    
    Loading

    Poem

    I'm a bunny coder, hopping through the code,
    New commands in my burrow lighten up the load.
    Methods sprout like carrots in a neatly trimmed row,
    Errors caught and logged, making stability grow.
    With each refactor I celebrate with a joyful jump,
    CodeRabbit's garden blooms—this change gives me a thump!
    🐰💻 Happy hopping!

    ✨ Finishing Touches
    • 📝 Generate Docstrings

    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.

    ❤️ Share
    🪧 Tips

    Chat

    There are 3 ways to chat with CodeRabbit:

    • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
      • I pushed a fix in commit <commit_id>, please review it.
      • Generate unit testing code for this file.
      • Open a follow-up GitHub issue for this discussion.
    • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
      • @coderabbitai generate unit testing code for this file.
      • @coderabbitai modularize this function.
    • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
      • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
      • @coderabbitai read src/utils.ts and generate unit testing code.
      • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
      • @coderabbitai help me debug CodeRabbit configuration file.

    Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

    CodeRabbit Commands (Invoked using PR comments)

    • @coderabbitai pause to pause the reviews on a PR.
    • @coderabbitai resume to resume the paused reviews.
    • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
    • @coderabbitai full review to do a full review from scratch and review all the files again.
    • @coderabbitai summary to regenerate the summary of the PR.
    • @coderabbitai generate docstrings to generate docstrings for this PR.
    • @coderabbitai resolve resolve all the CodeRabbit review comments.
    • @coderabbitai plan to trigger planning for file edits and PR creation.
    • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
    • @coderabbitai help to get help.

    Other keywords and placeholders

    • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
    • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
    • Add @coderabbitai anywhere in the PR title to generate the title automatically.

    CodeRabbit Configuration File (.coderabbit.yaml)

    • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
    • Please see the configuration documentation for more information.
    • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

    Documentation and Community

    • Visit our Documentation for detailed information on how to use CodeRabbit.
    • Join our Discord Community to get help, request features, and share feedback.
    • Follow us on X/Twitter for updates and announcements.

    @qodo-code-review
    Copy link
    Copy Markdown

    PR Reviewer Guide 🔍

    Here are some key observations to aid the review process:

    ⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
    🧪 No relevant tests
    🔒 No security concerns identified
    ⚡ Recommended focus areas for review

    Error Handling

    The get_device_parameters function has custom error handling that returns a structured response with empty parameters on error, which differs from other functions that raise exceptions. This inconsistency could lead to unexpected behavior.

    elif command_type == "get_device_parameters":
        track_index = params.get("track_index", 0)
        device_index = params.get("device_index", 0)
        try:
            result = self._get_device_parameters(track_index, device_index)
            self.log_message(f"[GET_DEVICE_PARAMS] Got result from _get_device_parameters: {result}")
            # Make sure we return a properly structured response
            response = {
                "status": "success",
                "result": result
            }
            self.log_message(f"[GET_DEVICE_PARAMS] Returning response: {response}")
            return response
        except Exception as e:
            self.log_message(f"[GET_DEVICE_PARAMS] Error in get_device_parameters: {str(e)}")
            self.log_message(traceback.format_exc())
            response["status"] = "error"
            response["message"] = str(e)
            response["result"] = {
                "device_name": "Error",
                "parameters": []
            }
    elif command_type == "search_browser_items":
    Parameter Validation

    The _set_device_parameters function clamps parameter values to min/max bounds without validating parameter types, which could cause issues with non-numeric inputs.

    value = float(value)
    original_value = value
    value = max(min(value, param.max), param.min)
    
    if value != original_value:
        self.log_message("Value {0} clamped to {1} for {2}".format(original_value, value, param_name))
    Duplicate Constants

    STATE_MODIFYING_COMMANDS is defined in both files with identical content. This duplication could lead to maintenance issues if one list is updated but not the other.

    STATE_MODIFYING_COMMANDS = [
        "add_notes_to_clip", "create_audio_track", "create_clip", "create_midi_track",
        "fire_clip", "load_browser_item", "set_clip_properties",
        "set_device_parameters", "set_tempo", "set_track_name", "start_playback",
        "stop_clip", "stop_playback"
    ]

    @qodo-code-review
    Copy link
    Copy Markdown

    PR Code Suggestions ✨

    Explore these optional code suggestions:

    CategorySuggestion                                                                                                                                    Impact
    Possible issue
    Fix early return bug

    The function returns a response directly, bypassing the normal command
    processing flow. This will cause the outer try-except block to continue
    executing and potentially overwrite the response. Instead, you should set the
    response values and let the normal flow continue.

    AbletonMCP_Remote_Script/init.py [336-357]

     elif command_type == "get_device_parameters":
                 track_index = params.get("track_index", 0)
                 device_index = params.get("device_index", 0)
                 try:
                     result = self._get_device_parameters(track_index, device_index)
                     self.log_message(f"[GET_DEVICE_PARAMS] Got result from _get_device_parameters: {result}")
    -                # Make sure we return a properly structured response
    -                response = {
    -                    "status": "success",
    -                    "result": result
    -                }
    -                self.log_message(f"[GET_DEVICE_PARAMS] Returning response: {response}")
    -                return response
    +                response["result"] = result
                 except Exception as e:
                     self.log_message(f"[GET_DEVICE_PARAMS] Error in get_device_parameters: {str(e)}")
                     self.log_message(traceback.format_exc())
                     response["status"] = "error"
                     response["message"] = str(e)
                     response["result"] = {
                         "device_name": "Error",
                         "parameters": []
                     }
    • Apply this suggestion
    Suggestion importance[1-10]: 9

    __

    Why: The suggestion correctly identifies a critical bug where the function returns a response directly, bypassing the normal command processing flow. This would cause the outer try-except block to continue executing and potentially overwrite the response, leading to unpredictable behavior.

    High
    • More

    DEFAULT_PORT = 9877
    HOST = "localhost"

    STATE_MODIFYING_COMMANDS = [
    Copy link
    Copy Markdown
    Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Added this list to the top of the remote script and server.py since we want to make sure this is consistent across both files. I was trying to play around with making this a constant but was having some import issues so just stuck to this for now

    clip_index = params.get("clip_index", 0)
    notes = params.get("notes", [])
    result = self._add_notes_to_clip(track_index, clip_index, notes)
    elif command_type == "set_clip_name":
    Copy link
    Copy Markdown
    Author

    @Jakeway Jakeway Apr 8, 2025

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Set clip name is handled by the new set clip properties, so removed it in favor of not having multiple ways to do the same thing

    Copy link
    Copy Markdown

    @coderabbitai coderabbitai Bot left a comment

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    Actionable comments posted: 0

    🧹 Nitpick comments (7)
    MCP_Server/server.py (6)

    15-20: Add a short docstring or comment for discoverability
    While this list is self-explanatory, adding a short docstring or inline comment may help future contributors quickly understand why and when commands are considered state-modifying.


    136-136: Consider returning partial data for recoverable errors
    Currently, if response.get("status") == "error", this raises an exception immediately. If some commands can yield partial data on errors, you might consider returning any available partial result.


    140-140: Make post-response delays configurable
    For large projects, a hardcoded delay can cause performance overhead or insufficient wait times. Exposing this delay as a configurable constant or parameter could be beneficial in different environments.


    733-777: Potential performance improvements for large browser inventories
    When dealing with very large sets of browser items, consider introducing pagination or limiting the search scope early if the user’s environment is known to have thousands of items.


    779-821: Validate property types before setting
    While the code typecasts some properties (e.g., color to int, gain to float), consider explicitly validating all property types to avoid silent failures when invalid data is passed.

    🧰 Tools
    🪛 Ruff (0.8.2)

    810-810: f-string without any placeholders

    Remove extraneous f prefix

    (F541)


    823-858: Log final parameter states
    After setting the device parameters, consider logging a consolidated summary of post-update values to ease debugging if some parameters did not set as expected.

    AbletonMCP_Remote_Script/__init__.py (1)

    1311-1418: Simplify nested condition checks
    Within _search_browser_items, the static analysis hints suggest combining nested conditions such as

    if category_type == "all" or category_type == "instruments":
        if hasattr(app.browser, 'instruments'):
            ...

    into a single condition (e.g., if (category_type in ("all", "instruments")) and hasattr(app.browser, "instruments"):) for clarity.

    🧰 Tools
    🪛 Ruff (0.8.2)

    1344-1345: Use a single if statement instead of nested if statements

    (SIM102)


    1347-1348: Use a single if statement instead of nested if statements

    (SIM102)


    1350-1351: Use a single if statement instead of nested if statements

    (SIM102)


    1353-1354: Use a single if statement instead of nested if statements

    (SIM102)


    1356-1357: Use a single if statement instead of nested if statements

    (SIM102)

    📜 Review details

    Configuration used: CodeRabbit UI
    Review profile: CHILL
    Plan: Pro

    📥 Commits

    Reviewing files that changed from the base of the PR and between bea865e and 0450c82.

    📒 Files selected for processing (2)
    • AbletonMCP_Remote_Script/__init__.py (9 hunks)
    • MCP_Server/server.py (4 hunks)
    🧰 Additional context used
    🪛 Ruff (0.8.2)
    MCP_Server/server.py

    810-810: f-string without any placeholders

    Remove extraneous f prefix

    (F541)

    AbletonMCP_Remote_Script/__init__.py

    1344-1345: Use a single if statement instead of nested if statements

    (SIM102)


    1347-1348: Use a single if statement instead of nested if statements

    (SIM102)


    1350-1351: Use a single if statement instead of nested if statements

    (SIM102)


    1353-1354: Use a single if statement instead of nested if statements

    (SIM102)


    1356-1357: Use a single if statement instead of nested if statements

    (SIM102)

    🔇 Additional comments (14)
    MCP_Server/server.py (4)

    111-111: Good use of consolidated command list
    Checking if command_type is in STATE_MODIFYING_COMMANDS promotes consistency across the code.


    636-706: Verify device indices in upstream logic
    This endpoint delegates invalid track/device checking to Ableton, but you might also want to confirm that calls to get_device_parameters handle negative or out-of-range indices gracefully upstream.


    708-731: Sufficient detail in docstring
    The docstring offers clear instructions on usage. The function effectively retrieves devices, clip slots, and other properties for the master track.


    865-865: Confirm usage of main entry point
    Ensure that calling main() as the entry point is consistent with your deployment or packaging approach. If you distribute the MCP as a library, some users might prefer a different programmatic interface.

    AbletonMCP_Remote_Script/__init__.py (10)

    21-26: Centralized command list
    Defining STATE_MODIFYING_COMMANDS here is consistent with the server changes. This improves maintainability but remember to keep both locations in sync if changes are needed.


    235-236: Great clarity in command dispatch
    Adding "get_master_track_info" as a separate command simplifies code comprehension and ensures alignment with the new _get_master_track_info method.


    238-238: Consistent usage of STATE_MODIFYING_COMMANDS
    Using a unified check for all state-modifying commands helps simplify future expansions and reduces duplication.


    282-291: Efficient approach to setting clip properties
    The new condition for "set_clip_properties" properly defers to a single _set_clip_properties method, improving modularity.


    336-358: Proper structure for “get_device_parameters”
    This handle logic neatly defers to _get_device_parameters, returning a well-formed success or error response.


    359-363: Enhances search capabilities
    The new "search_browser_items" path is consistent with other commands, further unifying your command structure.


    451-498: Detailed master track retrieval
    Offering a specialized _get_master_track_info method clarifies differences from normal tracks (e.g., potential absence of clip slots).


    1115-1172: Comprehensive parameter retrieval
    The _get_device_parameters method logs thorough diagnostic messages and gracefully handles negative or out-of-range indices. Good job including device class info.


    1173-1244: Robust property setting
    Your _set_clip_properties method systematically handles clip attributes, passing back updated values. This is a well-defined approach for multi-attribute updates.


    1245-1310: Cautious clamping of parameter values
    Clamping parameter values with logs is a nice touch, ensuring unexpected out-of-range inputs don’t break the workflow.

    result = self._start_playback()
    elif command_type == "stop_playback":
    result = self._stop_playback()
    elif command_type == "load_instrument_or_effect":
    Copy link
    Copy Markdown
    Author

    Choose a reason for hiding this comment

    The reason will be displayed to describe this comment to others. Learn more.

    this isn't an actual method here, it's a tool from server.py but that will send load_browser_item command

    @Jakeway Jakeway closed this Sep 16, 2025
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Projects

    None yet

    Development

    Successfully merging this pull request may close these issues.

    2 participants