@williamzujkowski/live-coding-music-mcp 3.0.0 → 4.0.0
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/README.md +37 -102
- package/dist/server/server.js +8 -8
- package/dist/server/tools/ai.js +5 -61
- package/dist/server/tools/analysis.js +2 -32
- package/dist/server/tools/capture.js +3 -41
- package/dist/server/tools/compose.js +2 -16
- package/dist/server/tools/diagnostics.js +3 -50
- package/dist/server/tools/editor.js +3 -66
- package/dist/server/tools/generate.js +7 -152
- package/dist/server/tools/history.js +5 -58
- package/dist/server/tools/playback.js +5 -21
- package/dist/server/tools/session.js +3 -41
- package/dist/server/tools/storage.js +1 -49
- package/dist/server/tools/transform.js +9 -156
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
[](https://github.com/williamzujkowski/live-coding-music-mcp/actions)
|
|
14
14
|
[](https://www.npmjs.com/package/@williamzujkowski/live-coding-music-mcp)
|
|
15
15
|
[](https://nerq.ai/kya/live-coding-music-mcp)
|
|
16
|
-
[]()
|
|
17
17
|
[](LICENSE)
|
|
18
18
|
|
|
19
19
|
A Model Context Protocol (MCP) server that drives [Strudel.cc](https://strudel.cc/) from Claude for AI-assisted live-coding music, pattern generation, and algorithmic composition.
|
|
@@ -169,7 +169,7 @@ Common commands for immediate use:
|
|
|
169
169
|
| Edit current pattern | `edit_pattern({ mode: "write", pattern: "..." })` |
|
|
170
170
|
| Create an isolated session | `session({ action: "create", session_id: "live-1" })` |
|
|
171
171
|
|
|
172
|
-
The legacy single-verb tools (`play`, `stop`, `save`, `undo`, `write`, `generate_pattern`, ...)
|
|
172
|
+
The legacy single-verb tools (`play`, `stop`, `save`, `undo`, `write`, `generate_pattern`, ...) were deprecated aliases during the `3.0.x` line and were removed in v4.0.0 ([#178](https://github.com/williamzujkowski/live-coding-music-mcp/issues/178)). Use the consolidated tools above.
|
|
173
173
|
|
|
174
174
|
**One-shot workflow:**
|
|
175
175
|
```
|
|
@@ -201,7 +201,7 @@ Then ask Claude:
|
|
|
201
201
|
|
|
202
202
|
<!-- TOOLS:START -->
|
|
203
203
|
|
|
204
|
-
**
|
|
204
|
+
**26 tools** across 14 categories:
|
|
205
205
|
|
|
206
206
|
<details><summary><strong>Setup</strong> (1)</summary>
|
|
207
207
|
|
|
@@ -211,127 +211,94 @@ Then ask Claude:
|
|
|
211
211
|
|
|
212
212
|
</details>
|
|
213
213
|
|
|
214
|
-
<details><summary><strong>Pattern Editing</strong> (
|
|
214
|
+
<details><summary><strong>Pattern Editing</strong> (2)</summary>
|
|
215
215
|
|
|
216
216
|
| Tool | Description |
|
|
217
217
|
|------|-------------|
|
|
218
|
+
| `edit_pattern` | Mutate the current session pattern. |
|
|
218
219
|
| `get_pattern` | Get current pattern code |
|
|
219
|
-
| `write` | [DEPRECATED — use edit_pattern({ mode: "write" }) instead] Write pattern to editor with optional auto-play and validation |
|
|
220
|
-
| `append` | [DEPRECATED — use edit_pattern({ mode: "append" }) instead] Append code to current pattern |
|
|
221
|
-
| `insert` | [DEPRECATED — use edit_pattern({ mode: "insert" }) instead] Insert code at specific line |
|
|
222
|
-
| `replace` | [DEPRECATED — use edit_pattern({ mode: "replace" }) instead] Replace pattern section |
|
|
223
220
|
|
|
224
221
|
</details>
|
|
225
222
|
|
|
226
|
-
<details><summary><strong>Playback</strong> (
|
|
223
|
+
<details><summary><strong>Playback</strong> (2)</summary>
|
|
227
224
|
|
|
228
225
|
| Tool | Description |
|
|
229
226
|
|------|-------------|
|
|
230
|
-
| `
|
|
231
|
-
| `clear` | [DEPRECATED — use edit_pattern({ mode: "clear" }) instead] Clear the editor |
|
|
232
|
-
| `play` | [DEPRECATED — use playback({ action: "play" }) instead] Start playing pattern |
|
|
233
|
-
| `pause` | [DEPRECATED — use playback({ action: "pause" }) instead] Pause playback |
|
|
234
|
-
| `stop` | [DEPRECATED — use playback({ action: "stop" }) instead] Stop playback |
|
|
227
|
+
| `playback` | Control transport on the current session. |
|
|
235
228
|
| `set_tempo` | Set BPM |
|
|
236
229
|
|
|
237
230
|
</details>
|
|
238
231
|
|
|
239
|
-
<details><summary><strong>Storage</strong> (
|
|
232
|
+
<details><summary><strong>Storage</strong> (1)</summary>
|
|
240
233
|
|
|
241
234
|
| Tool | Description |
|
|
242
235
|
|------|-------------|
|
|
243
|
-
| `
|
|
244
|
-
| `load` | [DEPRECATED — use pattern_store({ action: "load" }) instead] Load saved pattern |
|
|
245
|
-
| `list` | [DEPRECATED — use pattern_store({ action: "list" }) instead] List saved patterns |
|
|
236
|
+
| `pattern_store` | Persist patterns to disk and read them back. |
|
|
246
237
|
|
|
247
238
|
</details>
|
|
248
239
|
|
|
249
|
-
<details><summary><strong>History</strong> (
|
|
240
|
+
<details><summary><strong>History</strong> (1)</summary>
|
|
250
241
|
|
|
251
242
|
| Tool | Description |
|
|
252
243
|
|------|-------------|
|
|
253
|
-
| `
|
|
254
|
-
| `redo` | [DEPRECATED — use history({ action: "redo" }) instead] Redo action |
|
|
255
|
-
| `list_history` | [DEPRECATED — use history({ action: "list" }) instead] List recent pattern history with timestamps and previews |
|
|
256
|
-
| `restore_history` | [DEPRECATED — use history({ action: "restore" }) instead] Restore a previous pattern from history by ID |
|
|
244
|
+
| `history` | Navigate or inspect the pattern edit history. |
|
|
257
245
|
|
|
258
246
|
</details>
|
|
259
247
|
|
|
260
|
-
<details><summary><strong>Generation</strong> (
|
|
248
|
+
<details><summary><strong>Generation</strong> (3)</summary>
|
|
261
249
|
|
|
262
250
|
| Tool | Description |
|
|
263
251
|
|------|-------------|
|
|
264
252
|
| `compose` | Generate, write, and play a complete pattern in one step. Auto-initializes default browser if needed. |
|
|
265
|
-
| `
|
|
266
|
-
| `
|
|
267
|
-
| `generate_bassline` | [DEPRECATED — use generate_part({ role: "bass" }) instead] Generate bassline |
|
|
268
|
-
| `generate_melody` | [DEPRECATED — use generate_part({ role: "melody" }) instead] Generate melody from scale |
|
|
269
|
-
| `generate_polyrhythm` | [DEPRECATED — use generate_rhythm({ type: "polyrhythm" }) instead] Generate polyrhythm |
|
|
270
|
-
| `generate_fill` | [DEPRECATED — use generate_part({ role: "fill" }) instead] Generate drum fill |
|
|
271
|
-
| `generate_variation` | [DEPRECATED — use transform({ op: "vary" }) instead] Create pattern variations (mis-named today; it transforms, not generates) |
|
|
253
|
+
| `generate_part` | Generate a single instrumental layer and append it to the current session pattern. |
|
|
254
|
+
| `generate_rhythm` | Generate a rhythmic pattern and append it to the current session. |
|
|
272
255
|
|
|
273
256
|
</details>
|
|
274
257
|
|
|
275
|
-
<details><summary><strong>Music Theory</strong> (
|
|
258
|
+
<details><summary><strong>Music Theory</strong> (1)</summary>
|
|
276
259
|
|
|
277
260
|
| Tool | Description |
|
|
278
261
|
|------|-------------|
|
|
279
|
-
| `
|
|
280
|
-
| `generate_chord_progression` | [DEPRECATED — use music_theory({ query: "chord_progression" }) instead] Generate chord progression |
|
|
281
|
-
| `generate_euclidean` | [DEPRECATED — use generate_rhythm({ type: "euclidean" }) instead] Generate Euclidean rhythm |
|
|
282
|
-
| `apply_scale` | [DEPRECATED — use transform({ op: "scale" }) instead] Apply scale to notes |
|
|
262
|
+
| `music_theory` | Music-theory queries. |
|
|
283
263
|
|
|
284
264
|
</details>
|
|
285
265
|
|
|
286
|
-
<details><summary><strong>Transform</strong> (
|
|
266
|
+
<details><summary><strong>Transform</strong> (3)</summary>
|
|
287
267
|
|
|
288
268
|
| Tool | Description |
|
|
289
269
|
|------|-------------|
|
|
290
|
-
| `
|
|
291
|
-
| `
|
|
292
|
-
| `
|
|
293
|
-
| `quantize` | [DEPRECATED — use transform({ op: "quantize" }) instead] Quantize to grid |
|
|
294
|
-
| `humanize` | [DEPRECATED — use transform({ op: "humanize" }) instead] Add human timing variation |
|
|
295
|
-
| `add_effect` | [DEPRECATED — use effect({ action: "add" }) instead] Add effect to pattern |
|
|
296
|
-
| `remove_effect` | [DEPRECATED — use effect({ action: "remove" }) instead] Remove effect |
|
|
297
|
-
| `add_swing` | [DEPRECATED — use transform({ op: "swing" }) instead] Add swing to pattern |
|
|
298
|
-
| `set_energy` | [DEPRECATED — use shape({ dimension: "energy" }) instead] Adjust energy level 0-10. Auto-plays after applying. |
|
|
270
|
+
| `transform` | Apply a single transform op to the current session pattern. |
|
|
271
|
+
| `effect` | Add or remove a Strudel effect on the current session pattern. |
|
|
272
|
+
| `shape` | Shape the current pattern along one of three high-level dimensions. |
|
|
299
273
|
|
|
300
274
|
</details>
|
|
301
275
|
|
|
302
|
-
<details><summary><strong>AI</strong> (
|
|
276
|
+
<details><summary><strong>AI</strong> (1)</summary>
|
|
303
277
|
|
|
304
278
|
| Tool | Description |
|
|
305
279
|
|------|-------------|
|
|
306
|
-
| `
|
|
307
|
-
| `jam_with` | [DEPRECATED — use ai_assist({ task: "jam" }) instead] AI generates a complementary layer to jam with your pattern. |
|
|
308
|
-
| `shift_mood` | [DEPRECATED — use shape({ dimension: "mood" }) instead] Transform current pattern to match a different emotional mood. Moods: dark, euphoric, melancholic, aggressive, dreamy, peaceful, energetic. |
|
|
309
|
-
| `refine` | [DEPRECATED — use shape({ dimension: "refine" }) instead] Incrementally refine current pattern: faster/slower/louder/quieter/brighter/darker/"more reverb"/drier. |
|
|
280
|
+
| `ai_assist` | Gemini-backed pattern assistance. |
|
|
310
281
|
|
|
311
282
|
</details>
|
|
312
283
|
|
|
313
|
-
<details><summary><strong>Analysis</strong> (
|
|
284
|
+
<details><summary><strong>Analysis</strong> (6)</summary>
|
|
314
285
|
|
|
315
286
|
| Tool | Description |
|
|
316
287
|
|------|-------------|
|
|
317
288
|
| `analyze` | Audio analysis on the currently-playing pattern. |
|
|
318
|
-
| `analyze_spectrum` | [DEPRECATED — use analyze({ include: ["spectrum"] }) instead] FFT spectrum analysis |
|
|
319
|
-
| `analyze_rhythm` | [DEPRECATED — use analyze({ include: ["rhythm"] }) instead] Rhythm analysis |
|
|
320
|
-
| `detect_tempo` | [DEPRECATED — use analyze({ include: ["tempo"] }) instead] BPM detection |
|
|
321
|
-
| `detect_key` | [DEPRECATED — use analyze({ include: ["key"] }) instead] Key detection |
|
|
322
289
|
| `validate_pattern_runtime` | Validate pattern with runtime error checking (monitors Strudel console for errors) |
|
|
323
|
-
| `
|
|
290
|
+
| `validate_pattern_local` | Validate pattern syntax against the in-process StrudelEngine (no browser required) |
|
|
291
|
+
| `analyze_pattern_local` | Static analysis (events/cycle, complexity, optional BPM) without browser playback |
|
|
292
|
+
| `query_pattern_events` | Enumerate events the pattern would emit between two cycle indices (max 16 cycles) |
|
|
293
|
+
| `transpile_pattern` | Transpile pattern source via StrudelEngine; returns transpiled code or syntax error |
|
|
324
294
|
|
|
325
295
|
</details>
|
|
326
296
|
|
|
327
|
-
<details><summary><strong>Session</strong> (
|
|
297
|
+
<details><summary><strong>Session</strong> (1)</summary>
|
|
328
298
|
|
|
329
299
|
| Tool | Description |
|
|
330
300
|
|------|-------------|
|
|
331
|
-
| `
|
|
332
|
-
| `destroy_session` | [DEPRECATED — use session({ action: "destroy" }) instead] Close and destroy a Strudel session. |
|
|
333
|
-
| `list_sessions` | [DEPRECATED — use session({ action: "list" }) instead] List all active Strudel sessions. |
|
|
334
|
-
| `switch_session` | [DEPRECATED — use session({ action: "switch" }) instead] Change the default session. |
|
|
301
|
+
| `session` | Manage isolated Strudel browser sessions (multi-session, #108). |
|
|
335
302
|
|
|
336
303
|
</details>
|
|
337
304
|
|
|
@@ -340,59 +307,27 @@ Then ask Claude:
|
|
|
340
307
|
| Tool | Description |
|
|
341
308
|
|------|-------------|
|
|
342
309
|
| `export_midi` | Export current pattern to MIDI file. Parses note(), n(), and chord() functions. |
|
|
343
|
-
| `
|
|
310
|
+
| `browser_window` | Interact with the visible Strudel browser window. |
|
|
344
311
|
|
|
345
312
|
</details>
|
|
346
313
|
|
|
347
|
-
<details><summary><strong>Audio</strong> (
|
|
314
|
+
<details><summary><strong>Audio</strong> (1)</summary>
|
|
348
315
|
|
|
349
316
|
| Tool | Description |
|
|
350
317
|
|------|-------------|
|
|
351
|
-
| `
|
|
352
|
-
| `stop_audio_capture` | [DEPRECATED — use audio_capture({ action: "stop" }) instead] Stop audio capture and return base64-encoded audio. |
|
|
353
|
-
| `capture_audio_sample` | [DEPRECATED — use audio_capture({ action: "sample" }) instead] Capture a fixed-duration audio sample. |
|
|
318
|
+
| `audio_capture` | Record audio output from the live Strudel session. |
|
|
354
319
|
|
|
355
320
|
</details>
|
|
356
321
|
|
|
357
|
-
<details><summary><strong>Debug</strong> (
|
|
322
|
+
<details><summary><strong>Debug</strong> (1)</summary>
|
|
358
323
|
|
|
359
324
|
| Tool | Description |
|
|
360
325
|
|------|-------------|
|
|
361
|
-
| `show_browser` | [DEPRECATED — use browser_window({ action: "show" }) instead] Bring browser window to foreground |
|
|
362
326
|
| `diagnostics` | Inspect server and browser state. |
|
|
363
|
-
| `show_errors` | [DEPRECATED — use diagnostics({ level: "errors" }) instead] Display captured console errors and warnings from Strudel |
|
|
364
|
-
| `performance_report` | [DEPRECATED — use diagnostics({ level: "perf" }) instead] Get performance metrics and bottlenecks |
|
|
365
|
-
| `memory_usage` | [DEPRECATED — use diagnostics({ level: "memory" }) instead] Get current memory usage statistics |
|
|
366
|
-
|
|
367
|
-
</details>
|
|
368
|
-
|
|
369
|
-
<details><summary><strong>Other</strong> (19)</summary>
|
|
370
|
-
|
|
371
|
-
| Tool | Description |
|
|
372
|
-
|------|-------------|
|
|
373
|
-
| `ai_assist` | Gemini-backed pattern assistance. |
|
|
374
|
-
| `suggest_pattern_from_audio` | [DEPRECATED — use ai_assist({ task: "suggest" }) instead] Analyze playing audio and suggest a complementary Strudel pattern. |
|
|
375
|
-
| `validate_pattern_local` | Validate pattern syntax against the in-process StrudelEngine (no browser required) |
|
|
376
|
-
| `analyze_pattern_local` | Static analysis (events/cycle, complexity, optional BPM) without browser playback |
|
|
377
|
-
| `query_pattern_events` | Enumerate events the pattern would emit between two cycle indices (max 16 cycles) |
|
|
378
|
-
| `transpile_pattern` | Transpile pattern source via StrudelEngine; returns transpiled code or syntax error |
|
|
379
|
-
| `audio_capture` | Record audio output from the live Strudel session. |
|
|
380
|
-
| `browser_window` | Interact with the visible Strudel browser window. |
|
|
381
|
-
| `edit_pattern` | Mutate the current session pattern. |
|
|
382
|
-
| `generate_part` | Generate a single instrumental layer and append it to the current session pattern. |
|
|
383
|
-
| `music_theory` | Music-theory queries. |
|
|
384
|
-
| `generate_rhythm` | Generate a rhythmic pattern and append it to the current session. |
|
|
385
|
-
| `history` | Navigate or inspect the pattern edit history. |
|
|
386
|
-
| `playback` | Control transport on the current session. |
|
|
387
|
-
| `session` | Manage isolated Strudel browser sessions (multi-session, #108). |
|
|
388
|
-
| `pattern_store` | Persist patterns to disk and read them back. |
|
|
389
|
-
| `transform` | Apply a single transform op to the current session pattern. |
|
|
390
|
-
| `effect` | Add or remove a Strudel effect on the current session pattern. |
|
|
391
|
-
| `shape` | Shape the current pattern along one of three high-level dimensions. |
|
|
392
327
|
|
|
393
328
|
</details>
|
|
394
329
|
|
|
395
|
-
_Auto-generated from source.
|
|
330
|
+
_Auto-generated from source. 26 tools registered._
|
|
396
331
|
|
|
397
332
|
<!-- TOOLS:END -->
|
|
398
333
|
|
|
@@ -682,7 +617,7 @@ The Strudel MCP Server is built with a modular architecture that separates conce
|
|
|
682
617
|
#### 1. **StrudelMCPServer** (`src/server/server.ts`, ~510 lines)
|
|
683
618
|
|
|
684
619
|
Thin MCP dispatcher that:
|
|
685
|
-
- Aggregates
|
|
620
|
+
- Aggregates 26 tool definitions from the per-domain modules in `src/server/tools/`
|
|
686
621
|
- Routes `tools/call` requests to the right module's `execute()`
|
|
687
622
|
- Wraps every response in the discriminated result envelope (`{ ok, errorCategory, isRetryable, ... }`)
|
|
688
623
|
- Tracks initialization, per-session history bundles, per-session audio capture services
|
|
@@ -1546,7 +1481,7 @@ npm run build
|
|
|
1546
1481
|
# Check if server responds
|
|
1547
1482
|
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node dist/index.js
|
|
1548
1483
|
|
|
1549
|
-
# Should return JSON with
|
|
1484
|
+
# Should return JSON with 26 tools
|
|
1550
1485
|
|
|
1551
1486
|
# Reinstall MCP server in Claude
|
|
1552
1487
|
claude mcp remove strudel
|
|
@@ -1725,6 +1660,6 @@ Earlier versions of this package (including `@williamzujkowski/strudel-mcp-serve
|
|
|
1725
1660
|
|
|
1726
1661
|
---
|
|
1727
1662
|
|
|
1728
|
-
**
|
|
1663
|
+
**v4.0.0** — Open source, AGPL-3.0-or-later, experimental | [Report issues](https://github.com/williamzujkowski/live-coding-music-mcp/issues) | [Contribute](https://github.com/williamzujkowski/live-coding-music-mcp/pulls)
|
|
1729
1664
|
|
|
1730
1665
|
*This project is under active development. Core features work, but expect bugs and breaking changes. Not recommended for production use.*
|
package/dist/server/server.js
CHANGED
|
@@ -67,7 +67,7 @@ export class StrudelMCPServer {
|
|
|
67
67
|
constructor() {
|
|
68
68
|
this.server = new Server({
|
|
69
69
|
name: 'live-coding-music-mcp',
|
|
70
|
-
version: '
|
|
70
|
+
version: '4.0.0',
|
|
71
71
|
}, {
|
|
72
72
|
capabilities: {
|
|
73
73
|
tools: {},
|
|
@@ -188,10 +188,10 @@ export class StrudelMCPServer {
|
|
|
188
188
|
async getCurrentPatternSafe(sessionId) {
|
|
189
189
|
if (sessionId) {
|
|
190
190
|
// Explicit session — strict lookup, no pre-init stash. Named sessions
|
|
191
|
-
// must be created via `
|
|
191
|
+
// must be created via `session({ action: "create" })` before they're useful.
|
|
192
192
|
const sessionController = this.sessionManager.getSession(sessionId);
|
|
193
193
|
if (!sessionController) {
|
|
194
|
-
throw new Error(`Session '${sessionId}' not found. Create it first with
|
|
194
|
+
throw new Error(`Session '${sessionId}' not found. Create it first with session({ action: "create" }).`);
|
|
195
195
|
}
|
|
196
196
|
try {
|
|
197
197
|
return await sessionController.getCurrentPattern();
|
|
@@ -216,7 +216,7 @@ export class StrudelMCPServer {
|
|
|
216
216
|
if (sessionId) {
|
|
217
217
|
const sessionController = this.sessionManager.getSession(sessionId);
|
|
218
218
|
if (!sessionController) {
|
|
219
|
-
throw new Error(`Session '${sessionId}' not found. Create it first with
|
|
219
|
+
throw new Error(`Session '${sessionId}' not found. Create it first with session({ action: "create" }).`);
|
|
220
220
|
}
|
|
221
221
|
return await sessionController.writePattern(pattern);
|
|
222
222
|
}
|
|
@@ -240,7 +240,7 @@ export class StrudelMCPServer {
|
|
|
240
240
|
if (sessionId) {
|
|
241
241
|
const controller = this.sessionManager.getSession(sessionId);
|
|
242
242
|
if (!controller) {
|
|
243
|
-
throw new Error(`Session '${sessionId}' not found. Create it first with
|
|
243
|
+
throw new Error(`Session '${sessionId}' not found. Create it first with session({ action: "create" }).`);
|
|
244
244
|
}
|
|
245
245
|
return controller;
|
|
246
246
|
}
|
|
@@ -274,7 +274,7 @@ export class StrudelMCPServer {
|
|
|
274
274
|
// Save current state for undo and history (#41) before any edit
|
|
275
275
|
// (#179): route to the session-specific bundle. Default session is
|
|
276
276
|
// 'default'; named sessions get their own isolated stacks.
|
|
277
|
-
if (
|
|
277
|
+
if (name === 'edit_pattern') {
|
|
278
278
|
const sid = args?.session_id;
|
|
279
279
|
const canRead = sid !== undefined || this.isInitialized;
|
|
280
280
|
if (canRead) {
|
|
@@ -288,7 +288,7 @@ export class StrudelMCPServer {
|
|
|
288
288
|
id: this.historyIdCounter,
|
|
289
289
|
pattern: current,
|
|
290
290
|
timestamp: new Date(),
|
|
291
|
-
action:
|
|
291
|
+
action: args?.mode ?? 'write',
|
|
292
292
|
});
|
|
293
293
|
if (bundle.undoStack.length > this.MAX_HISTORY)
|
|
294
294
|
bundle.undoStack.shift();
|
|
@@ -421,7 +421,7 @@ export class StrudelMCPServer {
|
|
|
421
421
|
if (sessionId) {
|
|
422
422
|
const sessionController = this.sessionManager.getSession(sessionId);
|
|
423
423
|
if (!sessionController) {
|
|
424
|
-
throw new Error(`Session '${sessionId}' not found. Create it first with
|
|
424
|
+
throw new Error(`Session '${sessionId}' not found. Create it first with session({ action: "create" }).`);
|
|
425
425
|
}
|
|
426
426
|
if (!sessionController.page) {
|
|
427
427
|
throw new Error(`Session '${sessionId}' has no active page yet.`);
|
package/dist/server/tools/ai.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ai domain — AI-powered feedback, pattern suggestion, and jamming.
|
|
3
3
|
*
|
|
4
|
-
* Owns (
|
|
5
|
-
*
|
|
4
|
+
* Owns one consolidated tool (`ai_assist(task=feedback|suggest|jam)`) —
|
|
5
|
+
* all three tasks share Gemini's rate limiting and auth.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* tool — they all share Gemini's rate limiting and auth, so the merge
|
|
9
|
-
* is natural. Deferred until external contracts stabilize.
|
|
10
|
-
*
|
|
11
|
-
* `jam_with` carries six private helpers that analyze the current
|
|
7
|
+
* The `jam` task carries six private helpers that analyze the current
|
|
12
8
|
* pattern (tempo/key/style detection, layer detection, merge logic)
|
|
13
|
-
* and that are used only
|
|
9
|
+
* and that are used only here. They live in this module.
|
|
14
10
|
*/
|
|
15
11
|
import { Logger } from '../../utils/Logger.js';
|
|
16
12
|
const logger = new Logger();
|
|
@@ -56,52 +52,6 @@ export const tools = [
|
|
|
56
52
|
required: ['task'],
|
|
57
53
|
},
|
|
58
54
|
},
|
|
59
|
-
{
|
|
60
|
-
name: 'get_pattern_feedback',
|
|
61
|
-
description: '[DEPRECATED — use ai_assist({ task: "feedback" }) instead] Get AI-powered creative feedback on the current pattern using Google Gemini.',
|
|
62
|
-
inputSchema: {
|
|
63
|
-
type: 'object',
|
|
64
|
-
properties: {
|
|
65
|
-
includeAudio: { type: 'boolean', description: 'Include audio analysis (plays pattern briefly). Default: false' },
|
|
66
|
-
style: { type: 'string', description: 'Optional style hint for context (e.g., "techno", "ambient")' },
|
|
67
|
-
...SESSION_ID_PROP,
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
name: 'suggest_pattern_from_audio',
|
|
73
|
-
description: '[DEPRECATED — use ai_assist({ task: "suggest" }) instead] Analyze playing audio and suggest a complementary Strudel pattern.',
|
|
74
|
-
inputSchema: {
|
|
75
|
-
type: 'object',
|
|
76
|
-
properties: {
|
|
77
|
-
style: { type: 'string', description: 'Optional style hint (e.g., "ambient", "techno", "jazz")' },
|
|
78
|
-
role: {
|
|
79
|
-
type: 'string',
|
|
80
|
-
enum: ['complement', 'bassline', 'melody', 'percussion'],
|
|
81
|
-
description: 'What role the suggested pattern should fill. Default: complement',
|
|
82
|
-
},
|
|
83
|
-
...SESSION_ID_PROP,
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
name: 'jam_with',
|
|
89
|
-
description: '[DEPRECATED — use ai_assist({ task: "jam" }) instead] AI generates a complementary layer to jam with your pattern.',
|
|
90
|
-
inputSchema: {
|
|
91
|
-
type: 'object',
|
|
92
|
-
properties: {
|
|
93
|
-
layer: {
|
|
94
|
-
type: 'string',
|
|
95
|
-
enum: ['drums', 'bass', 'melody', 'pad', 'texture'],
|
|
96
|
-
description: 'Type of layer to add: drums, bass, melody, pad, or texture',
|
|
97
|
-
},
|
|
98
|
-
style_hint: { type: 'string', description: 'Optional style guidance (e.g., "funky", "minimal", "atmospheric")' },
|
|
99
|
-
auto_play: { type: 'boolean', description: 'Start playback after adding layer (default: true)' },
|
|
100
|
-
...SESSION_ID_PROP,
|
|
101
|
-
},
|
|
102
|
-
required: ['layer'],
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
55
|
];
|
|
106
56
|
export const toolNames = new Set(tools.map(t => t.name));
|
|
107
57
|
export async function execute(name, args, ctx) {
|
|
@@ -117,12 +67,6 @@ export async function execute(name, args, ctx) {
|
|
|
117
67
|
throw new Error(`Invalid task: ${t}. Must be one of: feedback, suggest, jam`);
|
|
118
68
|
}
|
|
119
69
|
}
|
|
120
|
-
case 'get_pattern_feedback':
|
|
121
|
-
return await getPatternFeedback(args?.includeAudio || false, args?.style, ctx, sid);
|
|
122
|
-
case 'suggest_pattern_from_audio':
|
|
123
|
-
return await suggestPatternFromAudio(args?.style, args?.role || 'complement', ctx, sid);
|
|
124
|
-
case 'jam_with':
|
|
125
|
-
return await jamWith(args.layer, args.style_hint, args.auto_play, ctx, sid);
|
|
126
70
|
default:
|
|
127
71
|
throw new Error(`ai module does not handle tool: ${name}`);
|
|
128
72
|
}
|
|
@@ -307,7 +251,7 @@ Example patterns:
|
|
|
307
251
|
style: style || 'auto',
|
|
308
252
|
valid: validation.valid,
|
|
309
253
|
validation_errors: validation.valid ? [] : validation.errors,
|
|
310
|
-
usage: 'Use write
|
|
254
|
+
usage: 'Use edit_pattern({ mode: "write" }) to load this pattern, then playback({ action: "play" }) to hear it.',
|
|
311
255
|
};
|
|
312
256
|
}
|
|
313
257
|
catch (error) {
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* analysis domain — audio analysis + pattern validation (browser + local).
|
|
3
3
|
*
|
|
4
|
-
* Owns (
|
|
5
|
-
* browser-required: analyze
|
|
6
|
-
* detect_tempo, detect_key, validate_pattern_runtime
|
|
4
|
+
* Owns (6 tools):
|
|
5
|
+
* browser-required: analyze (include[] filter), validate_pattern_runtime
|
|
7
6
|
* browser-free: validate_pattern_local, analyze_pattern_local,
|
|
8
7
|
* query_pattern_events, transpile_pattern
|
|
9
8
|
*
|
|
@@ -11,10 +10,6 @@
|
|
|
11
10
|
* pairs naturally with validate_pattern_runtime (which monitors the
|
|
12
11
|
* actual Strudel console). They were orphan handlers — implementations
|
|
13
12
|
* present but not registered in tools/list — until #124.
|
|
14
|
-
*
|
|
15
|
-
* Post-consolidation (per #110 audit): a single `analyze` tool with
|
|
16
|
-
* an `include[]` filter absorbs the 4 audio detection tools.
|
|
17
|
-
* Local-engine tools stay distinct — different subsystem.
|
|
18
13
|
*/
|
|
19
14
|
import { InputValidator } from '../../utils/InputValidator.js';
|
|
20
15
|
const SESSION_ID_PROP = {
|
|
@@ -47,26 +42,6 @@ export const tools = [
|
|
|
47
42
|
},
|
|
48
43
|
},
|
|
49
44
|
},
|
|
50
|
-
{
|
|
51
|
-
name: 'analyze_spectrum',
|
|
52
|
-
description: '[DEPRECATED — use analyze({ include: ["spectrum"] }) instead] FFT spectrum analysis',
|
|
53
|
-
inputSchema: { type: 'object', properties: { ...SESSION_ID_PROP } },
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
name: 'analyze_rhythm',
|
|
57
|
-
description: '[DEPRECATED — use analyze({ include: ["rhythm"] }) instead] Rhythm analysis',
|
|
58
|
-
inputSchema: { type: 'object', properties: { ...SESSION_ID_PROP } },
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
name: 'detect_tempo',
|
|
62
|
-
description: '[DEPRECATED — use analyze({ include: ["tempo"] }) instead] BPM detection',
|
|
63
|
-
inputSchema: { type: 'object', properties: { ...SESSION_ID_PROP } },
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
name: 'detect_key',
|
|
67
|
-
description: '[DEPRECATED — use analyze({ include: ["key"] }) instead] Key detection',
|
|
68
|
-
inputSchema: { type: 'object', properties: { ...SESSION_ID_PROP } },
|
|
69
|
-
},
|
|
70
45
|
{
|
|
71
46
|
name: 'validate_pattern_runtime',
|
|
72
47
|
description: 'Validate pattern with runtime error checking (monitors Strudel console for errors)',
|
|
@@ -232,11 +207,6 @@ export async function execute(name, args, ctx) {
|
|
|
232
207
|
switch (name) {
|
|
233
208
|
case 'analyze':
|
|
234
209
|
return await doAnalyze(args, controller);
|
|
235
|
-
// Deprecated aliases — forward to consolidated handler.
|
|
236
|
-
case 'analyze_spectrum': return await getSpectrum(controller);
|
|
237
|
-
case 'analyze_rhythm': return await getRhythm(controller);
|
|
238
|
-
case 'detect_tempo': return await getTempo(controller);
|
|
239
|
-
case 'detect_key': return await getKey(controller);
|
|
240
210
|
case 'validate_pattern_runtime': {
|
|
241
211
|
InputValidator.validateStringLength(args.pattern, 'pattern', 10000, false);
|
|
242
212
|
const validation = await controller.validatePatternRuntime(args.pattern, args.waitMs || 500);
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* capture domain — audio recording and MIDI export.
|
|
3
3
|
*
|
|
4
|
-
* Owns
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Per the #110 audit, the three audio_capture tools collapse into a
|
|
8
|
-
* single `audio_capture(action=start|stop|sample)` post-split.
|
|
9
|
-
* `export_midi` stays separate — MIDI is symbolic, audio is waveform.
|
|
4
|
+
* Owns two tools: `audio_capture(action=start|stop|sample)` and
|
|
5
|
+
* `export_midi` (kept separate — MIDI is symbolic, audio is waveform).
|
|
10
6
|
*
|
|
11
7
|
* The AudioCaptureService is lazy-instantiated module-side: we need
|
|
12
8
|
* the current Playwright page for `injectRecorder()`, and that only
|
|
@@ -53,34 +49,6 @@ export const tools = [
|
|
|
53
49
|
required: ['action'],
|
|
54
50
|
},
|
|
55
51
|
},
|
|
56
|
-
{
|
|
57
|
-
name: 'start_audio_capture',
|
|
58
|
-
description: '[DEPRECATED — use audio_capture({ action: "start" }) instead] Start capturing audio. Audio must be playing.',
|
|
59
|
-
inputSchema: {
|
|
60
|
-
type: 'object',
|
|
61
|
-
properties: {
|
|
62
|
-
format: { type: 'string', enum: ['webm', 'opus'], description: 'Audio format (default: webm)' },
|
|
63
|
-
maxDuration: { type: 'number', description: 'Maximum capture duration in milliseconds' },
|
|
64
|
-
...SESSION_ID_PROP,
|
|
65
|
-
},
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
name: 'stop_audio_capture',
|
|
70
|
-
description: '[DEPRECATED — use audio_capture({ action: "stop" }) instead] Stop audio capture and return base64-encoded audio.',
|
|
71
|
-
inputSchema: { type: 'object', properties: { ...SESSION_ID_PROP } },
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
name: 'capture_audio_sample',
|
|
75
|
-
description: '[DEPRECATED — use audio_capture({ action: "sample" }) instead] Capture a fixed-duration audio sample.',
|
|
76
|
-
inputSchema: {
|
|
77
|
-
type: 'object',
|
|
78
|
-
properties: {
|
|
79
|
-
duration: { type: 'number', description: 'Duration in milliseconds (default: 5000)' },
|
|
80
|
-
...SESSION_ID_PROP,
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
52
|
{
|
|
85
53
|
name: 'export_midi',
|
|
86
54
|
description: 'Export current pattern to MIDI file. Parses note(), n(), and chord() functions.',
|
|
@@ -113,12 +81,6 @@ export async function execute(name, args, ctx) {
|
|
|
113
81
|
throw new Error(`Invalid action: ${a}. Must be one of: start, stop, sample`);
|
|
114
82
|
}
|
|
115
83
|
}
|
|
116
|
-
case 'start_audio_capture':
|
|
117
|
-
return await startAudioCapture(args?.format, args?.maxDuration, ctx, sid);
|
|
118
|
-
case 'stop_audio_capture':
|
|
119
|
-
return await stopAudioCapture(ctx, sid);
|
|
120
|
-
case 'capture_audio_sample':
|
|
121
|
-
return await captureAudioSample(args?.duration, ctx, sid);
|
|
122
84
|
case 'export_midi':
|
|
123
85
|
return await exportMidi(args?.filename, args?.duration, args?.bpm, args?.format, ctx, sid);
|
|
124
86
|
default:
|
|
@@ -134,7 +96,7 @@ async function startAudioCapture(format, _maxDuration, ctx, sid) {
|
|
|
134
96
|
await service.startCapture(ctx.getController(sid).page, { format });
|
|
135
97
|
return {
|
|
136
98
|
success: true,
|
|
137
|
-
message: 'Audio capture started. Use
|
|
99
|
+
message: 'Audio capture started. Use audio_capture({ action: "stop" }) to get the recorded audio.',
|
|
138
100
|
format: format || 'webm',
|
|
139
101
|
};
|
|
140
102
|
}
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* compose domain — high-level UX tools that aren't a thin verb on a
|
|
3
|
-
* subsystem: `compose` (one-shot generate+write+play) and
|
|
4
|
-
* (
|
|
5
|
-
*
|
|
6
|
-
* Post-consolidation (per #110 audit):
|
|
7
|
-
* - `compose` absorbs `generate_pattern` after the alias period
|
|
8
|
-
* - `show_browser` merges with `screenshot` into `browser_window`
|
|
9
|
-
* These targets live in different epics (#120). Keeping the two tools
|
|
10
|
-
* together here matches today's "UX Tools" grouping; the file is small
|
|
11
|
-
* enough to split later without disruption.
|
|
3
|
+
* subsystem: `compose` (one-shot generate+write+play) and
|
|
4
|
+
* `browser_window` (show or screenshot the Strudel window).
|
|
12
5
|
*/
|
|
13
6
|
import { InputValidator } from '../../utils/InputValidator.js';
|
|
14
7
|
const SESSION_ID_PROP = {
|
|
@@ -35,11 +28,6 @@ export const tools = [
|
|
|
35
28
|
required: ['action'],
|
|
36
29
|
},
|
|
37
30
|
},
|
|
38
|
-
{
|
|
39
|
-
name: 'show_browser',
|
|
40
|
-
description: '[DEPRECATED — use browser_window({ action: "show" }) instead] Bring browser window to foreground',
|
|
41
|
-
inputSchema: { type: 'object', properties: { ...SESSION_ID_PROP } },
|
|
42
|
-
},
|
|
43
31
|
{
|
|
44
32
|
name: 'compose',
|
|
45
33
|
description: 'Generate, write, and play a complete pattern in one step. Auto-initializes default browser if needed.',
|
|
@@ -101,8 +89,6 @@ export async function execute(name, args, ctx) {
|
|
|
101
89
|
}
|
|
102
90
|
return a === 'show' ? await doShow(ctx, sid) : await doScreenshot(args, ctx, sid);
|
|
103
91
|
}
|
|
104
|
-
case 'show_browser':
|
|
105
|
-
return await doShow(ctx, sid);
|
|
106
92
|
case 'compose': {
|
|
107
93
|
InputValidator.validateStringLength(args.style, 'style', 100, false);
|
|
108
94
|
if (args.key) {
|