Skip to content

Fix connection handling for WSL2 and improve reliability#78

Open
isaacrowntree wants to merge 1 commit intoahujasid:mainfrom
isaacrowntree:fix/wsl-connection-improvements
Open

Fix connection handling for WSL2 and improve reliability#78
isaacrowntree wants to merge 1 commit intoahujasid:mainfrom
isaacrowntree:fix/wsl-connection-improvements

Conversation

@isaacrowntree
Copy link
Copy Markdown

@isaacrowntree isaacrowntree commented Mar 16, 2026

Summary

Fixes for running the MCP server from WSL2 connecting to Ableton Live on Windows, and general connection reliability improvements.

  • Bind Remote Script to 0.0.0.0 instead of localhost so it accepts connections from WSL2 and other network interfaces
  • Add ABLETON_HOST / ABLETON_PORT env vars for configurable connection target (defaults to localhost:9877) — useful for WSL2, Docker, or remote setups
  • Fix stale connection detection — the existing sendall(b'') check never actually detected dead sockets; replaced with MSG_PEEK-based liveness check
  • Add 5s connect timeout to prevent hanging when the host is unreachable
  • Remove redundant validation command during connection establishment (TCP connect is sufficient)
  • Better error messages — include host:port for easier debugging

Context

When running Claude Code's MCP client in WSL2 on Windows 11, the MCP server could not connect to Ableton's Remote Script because:

  1. The Remote Script only listened on localhost (loopback), unreachable from WSL2's network namespace
  2. The connection liveness check (sendall(b'')) silently succeeded on dead sockets, causing stale connections to never recover

Test plan

  • Tested on WSL2 (Ubuntu) connecting to Ableton Live 12 Suite on Windows 11
  • Verified get_session_info, create_clip, add_notes_to_clip, fire_clip all work
  • Verified stale connection recovery after MCP server restart
  • Should also work unchanged on native macOS/Linux setups (default localhost preserved)

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Server now listens on all network interfaces, enabling remote access from other machines
    • Added environment variable support for configuring connection host and port
  • Improvements

    • Enhanced connection validation to detect and recover from failed connections
    • Improved error logging with detailed host and port context
    • Optimized socket timeout handling for more reliable connection establishment

- Bind Remote Script socket to 0.0.0.0 instead of localhost, allowing
  connections from WSL2 and other network interfaces
- Add ABLETON_HOST and ABLETON_PORT env vars for configurable connection
  target (defaults to localhost:9877)
- Fix stale connection detection: replace sendall(b'') with MSG_PEEK-based
  liveness check that actually detects dead sockets
- Add connect timeout (5s) to prevent hanging on unreachable hosts
- Remove redundant validation command during connection establishment
- Include host:port in error messages for easier debugging

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Fix WSL2 connection handling and improve socket reliability

🐞 Bug fix ✨ Enhancement

Grey Divider

Walkthroughs

Description
• Bind Remote Script to 0.0.0.0 for WSL2 and cross-network connectivity
• Add ABLETON_HOST/ABLETON_PORT environment variables for flexible configuration
• Replace unreliable sendall(b'') with MSG_PEEK-based socket liveness detection
• Add 5-second connection timeout to prevent hanging on unreachable hosts
• Remove redundant validation command during connection establishment
• Improve error messages with host:port information for debugging
Diagram
flowchart LR
  A["Remote Script<br/>Binding"] -->|"Changed to<br/>0.0.0.0"| B["Accept WSL2<br/>Connections"]
  C["Environment<br/>Variables"] -->|"ABLETON_HOST<br/>ABLETON_PORT"| D["Configurable<br/>Connection Target"]
  E["Socket Liveness<br/>Check"] -->|"MSG_PEEK<br/>instead of sendall"| F["Reliable Dead<br/>Socket Detection"]
  G["Connection<br/>Establishment"] -->|"Add 5s<br/>timeout"| H["Prevent Hanging<br/>on Unreachable Host"]
  I["Connection<br/>Validation"] -->|"Remove redundant<br/>command"| J["Faster Connection<br/>Setup"]
Loading

Grey Divider

File Changes

1. AbletonMCP_Remote_Script/__init__.py 🐞 Bug fix +1/-1

Bind Remote Script to all network interfaces

• Changed HOST binding from "localhost" to "0.0.0.0" to accept connections from WSL2 and other
 network interfaces

AbletonMCP_Remote_Script/init.py


2. MCP_Server/server.py 🐞 Bug fix +26/-26

Add env vars, timeout, and fix socket liveness detection

• Added os import and environment variable support for ABLETON_HOST and ABLETON_PORT
 configuration
• Implemented 5-second connection timeout in connect() method to prevent hanging on unreachable
 hosts
• Replaced unreliable sendall(b'') socket liveness check with MSG_PEEK-based detection that
 properly identifies dead sockets
• Removed redundant get_session_info validation command during connection establishment
• Enhanced error messages to include host:port information for easier debugging
• Improved connection retry logic and socket state management

MCP_Server/server.py


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review bot commented Mar 16, 2026

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Env port parse crash 🐞 Bug ⛯ Reliability
Description
MCP_Server/server.py parses ABLETON_PORT with int() at import time; any non-integer value raises
ValueError and prevents the MCP server module from loading. This turns a simple misconfiguration
into a hard startup failure.
Code

MCP_Server/server.py[R11-12]

+ABLETON_HOST = os.environ.get("ABLETON_HOST", "localhost")
+ABLETON_PORT = int(os.environ.get("ABLETON_PORT", "9877"))
Evidence
ABLETON_PORT is converted to int during module import, so an invalid environment value crashes the
process before logging/lifespan startup can run.

MCP_Server/server.py[11-13]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`ABLETON_PORT = int(os.environ.get(...))` runs at import time and can raise `ValueError`, preventing the MCP server from starting when the environment variable is misconfigured.

### Issue Context
This module is used as the package entrypoint (`ableton-mcp = &quot;MCP_Server.server:main&quot;`) and via `python -m MCP_Server.server`, so import-time failures are fatal.

### Fix Focus Areas
- MCP_Server/server.py[11-13]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Unauthenticated remote control 🐞 Bug ⛨ Security
Description
AbletonMCP_Remote_Script now binds the command socket to 0.0.0.0, exposing it on all network
interfaces while still accepting and executing JSON commands without authentication. Any host that
can reach port 9877 can control Ableton Live over the network.
Code

AbletonMCP_Remote_Script/init.py[19]

+HOST = "0.0.0.0"
Evidence
The Remote Script binds to all interfaces and accepts arbitrary client connections, then parses and
executes received JSON commands with no auth/allowlist checks.

AbletonMCP_Remote_Script/init.py[17-21]
AbletonMCP_Remote_Script/init.py[75-82]
AbletonMCP_Remote_Script/init.py[100-105]
AbletonMCP_Remote_Script/init.py[133-167]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
Binding the Ableton Remote Script to `0.0.0.0` exposes the control socket on all interfaces, but the protocol has no authentication, so any reachable host can send commands.

### Issue Context
The script accepts TCP connections and executes JSON commands directly, so network exposure materially changes the threat model.

### Fix Focus Areas
- AbletonMCP_Remote_Script/__init__.py[17-21]
- AbletonMCP_Remote_Script/__init__.py[75-82]
- AbletonMCP_Remote_Script/__init__.py[92-112]
- AbletonMCP_Remote_Script/__init__.py[133-167]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 16, 2026

📝 Walkthrough

Walkthrough

The HOST constant is changed to listen on all network interfaces (0.0.0.0). The Ableton connection logic is refactored to use environment-based host/port configuration, implement liveness checking via socket probing, establish connections with timeout management, and improve error logging with connection context.

Changes

Cohort / File(s) Summary
Network Binding Configuration
AbletonMCP_Remote_Script/__init__.py
Changed HOST constant from "localhost" to "0.0.0.0" to enable listening on all network interfaces.
Connection Management Logic
MCP_Server/server.py
Introduced environment-based ABLETON_HOST and ABLETON_PORT configuration. Enhanced get_ableton_connection to check existing connection liveness via MSG_PEEK socket probe, establish new connections with 5s timeout then removal post-connect, improved error logging with host/port context, and streamlined connection retry logic (max 3 attempts, 1s delay).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 The socket hops to everywhere at once,
With liveness checks and timeouts balanced,
Environment whispers the host and port,
Connections now probe before they're sought,
Better logging makes debugging sport! 🎯

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: fixing connection handling for WSL2 and improving reliability through socket binding changes, environment variables, and connection timeout improvements.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


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

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can disable sequence diagrams in the walkthrough.

Disable the reviews.sequence_diagrams setting to disable sequence diagrams in the walkthrough.

Comment on lines +11 to +12
ABLETON_HOST = os.environ.get("ABLETON_HOST", "localhost")
ABLETON_PORT = int(os.environ.get("ABLETON_PORT", "9877"))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Env port parse crash 🐞 Bug ⛯ Reliability

MCP_Server/server.py parses ABLETON_PORT with int() at import time; any non-integer value raises
ValueError and prevents the MCP server module from loading. This turns a simple misconfiguration
into a hard startup failure.
Agent Prompt
### Issue description
`ABLETON_PORT = int(os.environ.get(...))` runs at import time and can raise `ValueError`, preventing the MCP server from starting when the environment variable is misconfigured.

### Issue Context
This module is used as the package entrypoint (`ableton-mcp = "MCP_Server.server:main"`) and via `python -m MCP_Server.server`, so import-time failures are fatal.

### Fix Focus Areas
- MCP_Server/server.py[11-13]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

# Constants for socket communication
DEFAULT_PORT = 9877
HOST = "localhost"
HOST = "0.0.0.0"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Unauthenticated remote control 🐞 Bug ⛨ Security

AbletonMCP_Remote_Script now binds the command socket to 0.0.0.0, exposing it on all network
interfaces while still accepting and executing JSON commands without authentication. Any host that
can reach port 9877 can control Ableton Live over the network.
Agent Prompt
### Issue description
Binding the Ableton Remote Script to `0.0.0.0` exposes the control socket on all interfaces, but the protocol has no authentication, so any reachable host can send commands.

### Issue Context
The script accepts TCP connections and executes JSON commands directly, so network exposure materially changes the threat model.

### Fix Focus Areas
- AbletonMCP_Remote_Script/__init__.py[17-21]
- AbletonMCP_Remote_Script/__init__.py[75-82]
- AbletonMCP_Remote_Script/__init__.py[92-112]
- AbletonMCP_Remote_Script/__init__.py[133-167]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

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.

🧹 Nitpick comments (2)
MCP_Server/server.py (2)

11-12: Consider handling invalid ABLETON_PORT values gracefully.

If ABLETON_PORT is set to a non-numeric value, int() will raise a ValueError at module load time with an unclear error message. Consider wrapping this in a try-except or validating the port.

💡 Proposed defensive handling
 ABLETON_HOST = os.environ.get("ABLETON_HOST", "localhost")
-ABLETON_PORT = int(os.environ.get("ABLETON_PORT", "9877"))
+try:
+    ABLETON_PORT = int(os.environ.get("ABLETON_PORT", "9877"))
+except ValueError:
+    logger.warning("Invalid ABLETON_PORT value, using default 9877")
+    ABLETON_PORT = 9877

Note: This requires moving the logger initialization before these lines, or using a print statement for the warning.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@MCP_Server/server.py` around lines 11 - 12, ABLETON_PORT is converted with
int(...) at import time which will raise ValueError on non-numeric env values;
update the module to validate/parse ABLETON_PORT with a try-except (or use
str.isdigit() check) and fall back to the default 9877 on failure, emitting a
warning via the module logger (so move logger initialization above these
constants or use print if not possible). Reference the ABLETON_PORT and
ABLETON_HOST symbols when making the change and ensure the code does not raise
at module load but instead logs the invalid value and continues with the default
port.

205-218: MSG_PEEK liveness check is sound, but consider catching OSError for robustness.

The MSG_PEEK approach correctly detects dead connections. However, catching only BlockingIOError may miss some error conditions. On certain platforms or Python versions, non-blocking socket operations can raise OSError with errno.EAGAIN or errno.EWOULDBLOCK.

💡 Broader exception handling for cross-platform safety
             _ableton_connection.sock.setblocking(False)
             try:
                 data = _ableton_connection.sock.recv(1, socket.MSG_PEEK)
                 if data == b'':
                     raise ConnectionError("Remote end closed")
-            except BlockingIOError:
-                pass  # Socket is alive, just no data waiting — this is normal
+            except (BlockingIOError, OSError) as e:
+                # BlockingIOError or EAGAIN/EWOULDBLOCK means socket is alive
+                import errno
+                if isinstance(e, OSError) and e.errno not in (errno.EAGAIN, errno.EWOULDBLOCK):
+                    raise
+                # Socket is alive, just no data waiting — this is normal
             finally:
                 _ableton_connection.sock.setblocking(True)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@MCP_Server/server.py` around lines 205 - 218, The MSG_PEEK liveness check
currently only catches BlockingIOError; update the inner recv exception handling
for _ableton_connection.sock.recv to also catch OSError and treat errno.EAGAIN /
errno.EWOULDBLOCK the same as BlockingIOError (i.e., ignore as "no data yet"),
while re-raising or converting other OSErrors into connection failures;
reference the symbols _ableton_connection, sock, recv, BlockingIOError, OSError,
and errno.EAGAIN/errno.EWOULDBLOCK when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@MCP_Server/server.py`:
- Around line 11-12: ABLETON_PORT is converted with int(...) at import time
which will raise ValueError on non-numeric env values; update the module to
validate/parse ABLETON_PORT with a try-except (or use str.isdigit() check) and
fall back to the default 9877 on failure, emitting a warning via the module
logger (so move logger initialization above these constants or use print if not
possible). Reference the ABLETON_PORT and ABLETON_HOST symbols when making the
change and ensure the code does not raise at module load but instead logs the
invalid value and continues with the default port.
- Around line 205-218: The MSG_PEEK liveness check currently only catches
BlockingIOError; update the inner recv exception handling for
_ableton_connection.sock.recv to also catch OSError and treat errno.EAGAIN /
errno.EWOULDBLOCK the same as BlockingIOError (i.e., ignore as "no data yet"),
while re-raising or converting other OSErrors into connection failures;
reference the symbols _ableton_connection, sock, recv, BlockingIOError, OSError,
and errno.EAGAIN/errno.EWOULDBLOCK when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f64f02b0-fa85-43d6-a256-12f22b74b18d

📥 Commits

Reviewing files that changed from the base of the PR and between e008328 and 85d0f61.

📒 Files selected for processing (2)
  • AbletonMCP_Remote_Script/__init__.py
  • MCP_Server/server.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant