fix: keep Ableton tools available across MCP clients - #13
fix: keep Ableton tools available across MCP clients#13HelloThisIsFlo wants to merge 17 commits into
Conversation
|
Warning Review limit reached
Next review available in: 23 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThe MCP server now coordinates a single Ableton backend owner across processes, manages ownership-aware startup and shutdown, tracks controlled operations, supports cancellable backend work, exposes ownership status and release tools, and updates dashboard lifecycle handling, tests, instructions, and architecture documentation. ChangesOwnership-backed MCP server
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant MCPClient
participant ToolHandler
participant OwnershipManager
participant ControlBackend
participant AbletonConnection
MCPClient->>ToolHandler: invoke Ableton tool
ToolHandler->>OwnershipManager: ensure_control()
OwnershipManager->>ControlBackend: start backend when ownership is acquired
ToolHandler->>OwnershipManager: begin_operation()
ToolHandler->>AbletonConnection: send_command()
AbletonConnection-->>ToolHandler: command result
ToolHandler->>OwnershipManager: end_operation()
ToolHandler-->>MCPClient: result or structured ownership status
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
PR Summary by QodoKeep Ableton tools available across MCP clients via ownership port coordination
AI Description
Diagram
High-Level Assessment
Files changed (19)
|
Code Review by Qodo
1.
|
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (1)
MCP_Server/tools/_base.py (1)
113-118: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider logging swallowed background exceptions.
_consume_background_resultsilently discards any exception (including_ControlReleasedErroror a real backend error) from a task that outlived its timeout. Since the client already received a timeout response, this is the only place such failures could ever surface for debugging.♻️ Suggested tweak
def _consume_background_result(task: asyncio.Task) -> None: """Retrieve a timed-out task's result so late exceptions are not leaked.""" try: - task.exception() + exc = task.exception() + if exc is not None: + logger.warning("Timed-out tool task finished with error: %s", exc) except (asyncio.CancelledError, Exception): pass🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@MCP_Server/tools/_base.py` around lines 113 - 118, Update _consume_background_result to log exceptions retrieved from the timed-out task instead of silently swallowing them, while continuing to ignore normal cancellation and preserving the existing late-result consumption behavior. Use the module’s established logging mechanism and include the exception details for debugging.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@MCP_Server/dashboard/server.py`:
- Around line 199-204: Update the dashboard shutdown logic around
state.dashboard_server and state.dashboard_thread so references are cleared only
after the thread has exited. After thread.join(timeout=3.0), check
thread.is_alive(); if it remains alive, report the incomplete shutdown and
retain both state references, otherwise perform the existing cleanup.
In `@MCP_Server/ownership.py`:
- Around line 151-158: Update the claim startup flow around the lock-protected
transition that sets self._phase to "owner" and the corresponding cleanup in
shutdown() so a forced shutdown cannot race an in-progress start_backend().
Serialize startup and shutdown lifecycle transitions, or revalidate ownership
and listener state after start_backend() before publishing "owner"; if startup
was cancelled, clean up the backend resources and return a failed ClaimResult.
Apply the same protection to the related transition at the noted second
location.
- Around line 184-195: Update MCP_Server/ownership.py lines 184-195 so ownership
is released only after every owner-only cleanup succeeds; on cleanup failure,
return ReleaseResult with released=False and retain local ownership. In
MCP_Server/server.py lines 150-151, make the live cache scan cooperatively
cancellable or wait for it to finish. In MCP_Server/server.py lines 218-223,
retain live thread references and report shutdown timeouts. In
MCP_Server/dashboard/server.py lines 199-204, clear dashboard references only
after confirmed thread termination.
- Around line 306-316: Update the payload validation in the status-reading flow
before any payload.get calls to require that payload is a JSON
object/dictionary. Return self._standby_status("occupied_unknown") for valid
non-object JSON values such as lists, while preserving the existing validation
for service, protocol, and owner.
In `@MCP_Server/server.py`:
- Around line 150-151: Update the live browser scan flow around
populate_browser_cache() to observe stop_event after the scan begins, including
during its socket waits and sleeps. Propagate the shutdown signal through the
cache-population logic so owner-only work stops promptly after handoff, while
preserving normal scan behavior when stop_event remains unset.
In `@MCP_Server/tools/_base.py`:
- Around line 91-93: Update the _ControlReleasedError handler to obtain
ownership status through asyncio.to_thread rather than calling
ownership.get_status() directly, preserving the existing tool_error response and
status payload while preventing blocking remote-owner probing on the event loop.
- Around line 47-77: Update the tool wrapper so claim-free operations do not
acquire the Ableton semaphore, allowing get_server_capabilities and
release_ableton_control to remain usable during ownership recovery. Also bound
the ownership.ensure_control call with _TOOL_TIMEOUT_SECONDS and handle timeout
consistently, while preserving existing claim error responses for completed
attempts.
In `@tests/test_ownership.py`:
- Around line 176-190: Move the cleanup scope in the ownership test to begin
immediately after creating the owner via _configured_manager, enclosing the
ensure_control assertion and all subsequent setup. Ensure owner.shutdown()
remains in the corresponding finally block so it runs when the assertion fails
or raises, while preserving the existing test behavior.
---
Nitpick comments:
In `@MCP_Server/tools/_base.py`:
- Around line 113-118: Update _consume_background_result to log exceptions
retrieved from the timed-out task instead of silently swallowing them, while
continuing to ignore normal cancellation and preserving the existing late-result
consumption behavior. Use the module’s established logging mechanism and include
the exception details for debugging.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d834b24f-dfd1-42d0-b819-e6f81276fb8e
📒 Files selected for processing (11)
MCP_Server/dashboard/server.pyMCP_Server/instructions.pyMCP_Server/ownership.pyMCP_Server/server.pyMCP_Server/state.pyMCP_Server/tools/_base.pyMCP_Server/tools/session.pyREADME.mddocs/ARCHITECTURE.mdtests/test_ownership.pytests/test_tool_handler.py
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@MCP_Server/connections/ableton.py`:
- Around line 143-148: Update the CommandCancelled raises in the socket timeout
shutdown path and the retry-delay cancellation path to explicitly suppress
exception chaining with a direct-from-None raise. Preserve the existing
cancellation message and stop_event behavior in both locations.
In `@MCP_Server/ownership.py`:
- Around line 208-220: Update shutdown cleanup after _stop_backend_once()
succeeds to recheck _active_operations under _lock before calling
_close_local_ownership(). If timed-out _run_sync_tool workers remain active,
keep ownership and the listener, set _phase to "cleanup_failed", and return a
failed ReleaseResult with the existing retry-style status; only release local
ownership once no active operations remain.
In `@MCP_Server/tools/_base.py`:
- Around line 57-94: The controlled-tool flow around invoke and
_ableton_semaphore currently releases the semaphore when the caller times out or
is cancelled, even though shielded claim_task or task continues running. Keep
the semaphore leased until the underlying work completes by moving semaphore
release responsibility to a completion callback for timeout and
caller-cancellation paths, while returning or propagating the
timeout/cancellation immediately; apply this consistently to both claim and tool
execution tasks.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: bdf6bc73-226a-4d0e-b5f5-7ddfe0974f81
📒 Files selected for processing (12)
MCP_Server/cache/browser.pyMCP_Server/connections/ableton.pyMCP_Server/dashboard/server.pyMCP_Server/ownership.pyMCP_Server/server.pyMCP_Server/tools/_base.pyREADME.mddocs/ARCHITECTURE.mdtests/test_browser_cache.pytests/test_connections.pytests/test_ownership.pytests/test_tool_handler.py
🚧 Files skipped from review as they are similar to previous changes (3)
- MCP_Server/dashboard/server.py
- docs/ARCHITECTURE.md
- README.md
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
This was very fun to watch btw. Seeing your agents work giving feedback to my agents. 😂 @hidingwill The last commit didn't trigger a new review, I tried triggering it manually with Either way. Thanks for this amazing project! |
|
Quick heads-up: testing uncovered an existing issue where I’m investigating a fix. If it’s small, I’ll include it in this PR; otherwise I’ll open a follow-up. |
|
Caution AI-authored update This update was written by Codex 5.6 Sol Ultra. I directed the intended behaviour, discussed the architecture and engineering trade-offs, and tested the result end to end. I have not personally reviewed the generated code line by line. Update: the status fix and review hardening are includedMy previous comment said I was investigating the misleading Since the previously published head ( What each commit does
Scope boundariesThe update does not add a public claim tool, idle timeout, force-steal behaviour, HTTP transport, Codex configuration change, Remote Script change, or wire-protocol change. Failed Live activation still rolls back ownership and returns a structured tool error. Validation
All 15 inline review threads are now resolved. CodeRabbit passed a fresh review on final head |
Code Review by Qodo
1.
|
|
/review |
|
Code review by qodo was updated up to the latest commit 209ce67 |
Caution
AI implementation disclosure
This PR's code was written by Codex (GPT-5.6, SOL, extra-high). I designed the intended behaviour, iterated on it, and discussed the architecture and engineering trade-offs with the model. I tested the implementation end to end and it works in my environment. However, I have not personally reviewed the generated code, so please treat it as tested but not human-code-reviewed.
Summary
Root cause
Codex and similar clients can start a private AbletonBridge stdio process per task. The previous machine-wide singleton exited every later process when port 9881 was occupied, so those tasks completed without Ableton tools even though Live and the first bridge were healthy.
The original standby status also treated the absence of a local backend as proof that Ableton was disconnected. The status contract now separates ownership, local backend state, and remote health that this process cannot observe.
Design
get_server_capabilitiesandrelease_ableton_controlclaim-freenullwithnot_started,owned_elsewhere, orunknownUser impact
Multiple MCP tasks can see Ableton tools simultaneously. One task controls Live, while standby tasks remain healthy, accurately describe what they can observe, and can take over after an explicit release.
Failed Live activation leaves MCP healthy, rolls back ownership, and returns a structured tool error.
Validation
uv run --frozen pytest -qgit diff --checkSummary by CodeRabbit
release_ableton_controlto enable intentional control handoffs.