livepilot 1.4.5 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +187 -144
- package/README.md +136 -61
- package/m4l_device/BUILD_GUIDE.md +161 -0
- package/m4l_device/LivePilot_Analyzer.amxd +0 -0
- package/m4l_device/LivePilot_Analyzer.maxpat +680 -0
- package/m4l_device/livepilot_bridge.js +942 -0
- package/mcp_server/__init__.py +1 -1
- package/mcp_server/connection.py +22 -16
- package/mcp_server/curves.py +741 -0
- package/mcp_server/m4l_bridge.py +285 -0
- package/mcp_server/server.py +29 -3
- package/mcp_server/tools/analyzer.py +508 -0
- package/mcp_server/tools/automation.py +431 -0
- package/mcp_server/tools/clips.py +16 -12
- package/mcp_server/tools/devices.py +2 -2
- package/mcp_server/tools/mixing.py +50 -14
- package/mcp_server/tools/tracks.py +2 -2
- package/package.json +2 -3
- package/plugin/agents/livepilot-producer/AGENT.md +32 -2
- package/plugin/plugin.json +2 -2
- package/plugin/skills/livepilot-core/SKILL.md +76 -11
- package/plugin/skills/livepilot-core/references/automation-atlas.md +272 -0
- package/plugin/skills/livepilot-core/references/overview.md +68 -5
- package/plugin/skills/livepilot-release/SKILL.md +101 -0
- package/remote_script/LivePilot/__init__.py +3 -2
- package/remote_script/LivePilot/clip_automation.py +220 -0
- package/remote_script/LivePilot/mixing.py +90 -1
- package/remote_script/LivePilot/server.py +3 -0
package/mcp_server/__init__.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""LivePilot MCP Server — bridges MCP protocol to Ableton Live."""
|
|
2
|
-
__version__ = "1.
|
|
2
|
+
__version__ = "1.6.1"
|
package/mcp_server/connection.py
CHANGED
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
7
|
import socket
|
|
8
|
+
import threading
|
|
8
9
|
import time
|
|
9
10
|
import uuid
|
|
10
11
|
from collections import deque
|
|
@@ -54,6 +55,7 @@ class AbletonConnection:
|
|
|
54
55
|
self._socket: Optional[socket.socket] = None
|
|
55
56
|
self._recv_buf: bytes = b""
|
|
56
57
|
self._command_log: deque[dict] = deque(maxlen=self.MAX_LOG_ENTRIES)
|
|
58
|
+
self._lock = threading.Lock() # Serialize all TCP send/receive cycles
|
|
57
59
|
|
|
58
60
|
# ------------------------------------------------------------------
|
|
59
61
|
# Connection lifecycle
|
|
@@ -101,7 +103,8 @@ class AbletonConnection:
|
|
|
101
103
|
def ping(self) -> bool:
|
|
102
104
|
"""Send a ping and return True if a pong is received."""
|
|
103
105
|
try:
|
|
104
|
-
|
|
106
|
+
with self._lock:
|
|
107
|
+
resp = self._send_raw({"type": "ping"})
|
|
105
108
|
return resp.get("result", {}).get("pong") is True
|
|
106
109
|
except Exception:
|
|
107
110
|
return False
|
|
@@ -109,25 +112,28 @@ class AbletonConnection:
|
|
|
109
112
|
def send_command(self, command_type: str, params: Optional[dict] = None) -> dict:
|
|
110
113
|
"""Send a command to Ableton and return the result dict.
|
|
111
114
|
|
|
115
|
+
Thread-safe: a lock serializes all TCP send/receive cycles to
|
|
116
|
+
prevent socket corruption when multiple MCP tools fire concurrently.
|
|
112
117
|
Retries once on socket errors with a fresh connection.
|
|
113
118
|
"""
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
self.
|
|
119
|
+
with self._lock:
|
|
120
|
+
# Ensure we have a connection
|
|
121
|
+
if not self.is_connected():
|
|
122
|
+
self.connect()
|
|
117
123
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
124
|
+
command: dict = {"type": command_type}
|
|
125
|
+
if params:
|
|
126
|
+
command["params"] = params
|
|
121
127
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
# Log the
|
|
128
|
+
try:
|
|
129
|
+
response = self._send_raw(command)
|
|
130
|
+
except (OSError, AbletonConnectionError):
|
|
131
|
+
# Retry once with a fresh connection
|
|
132
|
+
self.disconnect()
|
|
133
|
+
self.connect()
|
|
134
|
+
response = self._send_raw(command)
|
|
135
|
+
|
|
136
|
+
# Log and error handling outside the lock (no socket access needed)
|
|
131
137
|
log_entry = {
|
|
132
138
|
"command": command_type,
|
|
133
139
|
"params": params,
|