Strudel Live Coding logo

Strudel Live Coding

Community
williamzujkowski

A Model Context Protocol (MCP) server that gives Claude direct control over Strudel.cc for AI-assisted music generation and live coding.

Publisherwilliamzujkowski
Repositorylive-coding-music-mcp
LanguageTypeScript
Forks
33
Stars
238
Available tools
0
Transport typestdio
Categories
LicenseAGPL-3.0
Links
  • Connect tools to AI workflows

    Strudel Live Coding exposes MCP capabilities that can be used by compatible AI clients and agents.

  • 0 available tools

    Browse the callable actions below, including names and descriptions when provided by the server.

  • Ready-to-copy setup

    Use the installation snippets to configure this server in your preferred MCP client.

  • Open source signals

    238 stars and 33 forks from the linked repository.

live-coding-music-mcp

๐ŸŽต MCP server for AI-assisted live-coding music via strudel.cc

Unofficial fan project. Not affiliated with, or endorsed by, the Strudel project. This adapter exists to make live-coding music accessible to beginners who want to try pattern-based music without learning the whole ecosystem first.

Status: Beta | 88% statement coverage | Published to npm | Actively developed

CI npm version Nerq Trust Tools License

A Model Context Protocol (MCP) server that drives Strudel.cc from Claude for AI-assisted live-coding music, pattern generation, and algorithmic composition.

Current State: Beta. The core workflow (init โ†’ compose โ†’ playback โ†’ analyze) works reliably with real audio output. npm test reports ~3060 passing tests, 88.99% statement coverage / 79.99% branch coverage. CI is hardened with OpenSSF Scorecard, SHA-pinned actions, CODEOWNERS, Dependabot, and lint as a blocking gate.

What "Beta" means here:

  • Tool schemas are stable within minor versions; breaking changes require a major bump
  • Multi-session is supported as of v3.0.0 (#108) โ€” named sessions get isolated browser, history, and audio-capture state
  • Upstream @strudel/* dependencies pinned to known-good versions; Dependabot bumps gated on CI
  • Expect hands-on iteration for non-standard patterns โ€” report rough edges, they get fixed

Table of Contents

Features

Music control

  • 28 MCP tools covering pattern editing, playback, audio analysis, generation, history, sessions, MIDI import/export, and Gemini-backed assists. Each tool is enum-parameterized to keep the protocol surface small: pattern_store({ action }), edit_pattern({ mode }), transform({ op }), analyze({ include }), history({ action }), playback({ action }), effect({ action }), shape({ dimension }), audio_capture({ action }), browser_window({ action }), generate_part({ role }), generate_rhythm({ type }), music_theory({ query }), session({ action }), ai_assist({ task }), ... The 58 legacy single-verb aliases that forwarded to these were removed in v4.0.0 (#178).
  • 4 MCP resources for catalog browsing without burning tool calls: strudel://examples, strudel://patterns, strudel://styles, strudel://docs/tools.
  • Real browser automation of Strudel.cc through Playwright.
  • Multi-session support โ€” every browser-touching tool accepts an optional session_id; sessions have isolated browser pages, undo/redo/history stacks, and audio-capture services.
  • Audio analysis via Web Audio API (FFT spectrum, tempo detection, key detection, rhythm complexity).
  • Template-based pattern generation across 8 genres (techno, house, dnb, ambient, trap, jungle, jazz, experimental); optional Gemini-backed ai_assist for feedback, suggestions, and jamming.
  • Music theory helpers: 14 scales, 8 chord progression styles, euclidean and polyrhythm generation.
  • Pattern persistence: JSON-backed save/load with tags + the in-memory edit history (undo/redo/restore/compare).
  • Result envelope on every tools/call: clients branch on { ok, errorCategory, isRetryable } instead of parsing free-text.

Testing & CI status

  • ~3060 passing tests: npm test runs ~3020 unit/integration/example tests in parallel, then 36 browser-validation tests serially against real Chromium. The coverage figure below is from the first tier, which is the one CI also runs โ€” the browser tier adds ~0.2 points and CI does not execute it, so counting it would document coverage nothing verifies.
  • 88.99% statement coverage / 79.99% branch coverage (90.5% functions, 89.75% lines), checked against coverage/coverage-summary.json by a drift guard.
  • Lint blocking in CI: 0 errors, ~195 warnings (mostly any in test mocks).
  • OIDC trusted publishing to npm with SLSA build provenance attestation on every release.

Not Production-Ready: This is experimental software under active development. Use for exploration and experimentation. Expect breaking changes, bugs, and incomplete features. See the Contributing section to help improve it.

Example patterns

7 example patterns ship in patterns/examples/, grouped by genre:

  • Ambient: ambient-pad (70 BPM)
  • Dnb: dnb-classic (174 BPM)
  • House: house-classic (125 BPM)
  • Jazz: jazz-ii-v-i (120 BPM)
  • Jungle: amen-break (165 BPM)
  • Techno: techno-driving (130 BPM)
  • Trap: trap-modern (140 BPM)

Each example is a JSON file with pattern code, BPM, key, and a description, and each sets the tempo it declares (#367). See patterns/examples/README.md for details. Agents can also list these via the strudel://examples MCP resource without making any tool calls.

Migrating from @williamzujkowski/strudel-mcp-server

This project was renamed from @williamzujkowski/strudel-mcp-server to @williamzujkowski/live-coding-music-mcp to make the unofficial fan-project status unambiguous (see #97). The old package is deprecated on npm.

If you were using the old package:

bash
# Remove old
npm uninstall -g @williamzujkowski/strudel-mcp-server

# Install new
npm install -g @williamzujkowski/live-coding-music-mcp

Update your MCP client config. The bin name changed from strudel-mcp to live-coding-music-mcp:

jsonc
// BEFORE
{
  "mcpServers": {
    "strudel": { "command": "strudel-mcp" }
  }
}

// AFTER
{
  "mcpServers": {
    "live-coding-music": { "command": "live-coding-music-mcp" }
  }
}

The MCP server's internal identity string also changed from strudel-mcp-enhanced to live-coding-music-mcp. If you have a client that keys on that string (logs, allowlists, permission policies), update it accordingly.

All tool names and schemas are unchanged.

Installation

Prerequisites

RequirementVersionNotes
Node.js22.x+LTS required
npm10+Comes with Node.js
ChromiumLatestAuto-installed by Playwright
Audio outputAnyRequired for playback (speakers/headphones)

Optional: Docker for containerized deployment.

From npm

bash
npm install -g @williamzujkowski/live-coding-music-mcp

# Install browser (required once)
npx playwright install chromium

From Source

bash
# Clone repository
git clone https://github.com/williamzujkowski/live-coding-music-mcp.git
cd live-coding-music-mcp

# Install dependencies
npm install

# Install Chromium for browser automation
npx playwright install chromium

# Build the project
npm run build

Quick Start

Get from install to your first generated pattern in under two minutes.

1. Install

bash
npm install -g @williamzujkowski/live-coding-music-mcp
npx playwright install chromium   # one-time

Building from source instead? See Installation โ†’ From Source.

2. Configure your MCP client

Claude Desktop

Edit your Claude Desktop config file:

OSConfig path
macOS~/Library/Application Support/Claude/claude_desktop_config.json
Windows%APPDATA%\Claude\claude_desktop_config.json
Linux~/.config/Claude/claude_desktop_config.json

Add the server:

json
{
  "mcpServers": {
    "live-coding-music": {
      "command": "live-coding-music-mcp"
    }
  }
}

Restart Claude Desktop. The server appears under the ๐Ÿ”Œ plug icon.

Claude Code (CLI)

bash
# If installed globally
claude mcp add strudel live-coding-music-mcp

# If built from source
claude mcp add strudel node /path/to/live-coding-music-mcp/dist/index.js

3. Verify the server responds

bash
# From a global install
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | live-coding-music-mcp

# From source
npm run validate

You should see a JSON response listing 28 tools. If you see fewer, the build is out of date โ€” run npm run build.

4. Make your first sound

In Claude, ask:

Initialize Strudel and compose a techno beat.

What you'll see: A Chromium window opens (visibly โ€” this is the live editor, not a hidden process) and lands on strudel.cc. Claude calls init, then compose({ style: "techno" }). A 4-on-the-floor pattern appears in the CodeMirror editor and starts playing through your speakers.

Prefer headless mode (no browser window)? Set "headless": true in config.json before the first init call โ€” see Configuration. Note that tempo detection currently reflects how often the agent polls rather than the audio itself (#322), in headed or headless mode alike.

5. Where to go next

  • Quick Reference below: tool cheat sheet for common operations.
  • Usage Examples: multi-step workflows (composition, audio analysis, AI-assisted jamming).
  • patterns/examples/: 18 ready-to-play patterns across 7 genres plus 4 longform pieces. Agents can browse them via the strudel://examples MCP resource without burning tool calls.

Quick Reference

Common operations as one-line tool calls:

ActionTool call
Initialize browserinit
Create a techno beat in one shotcompose({ style: "techno" })
Play patternplayback({ action: "play" })
Stop playbackplayback({ action: "stop" })
Get current patternget_pattern
Analyze audio (all features)analyze({ include: ["all"] })
Detect tempo onlyanalyze({ include: ["tempo"] })
Save patternpattern_store({ action: "save", name: "my-pattern" })
Undo last edithistory({ action: "undo" })
Edit current patternedit_pattern({ mode: "write", pattern: "..." })
Create an isolated sessionsession({ action: "create", session_id: "live-1" })

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). Use the consolidated tools above.

One-shot workflow:

compose with style: "dnb", key: "Am", tempo: 174, auto_play: true

Available Tools

28 tools across 15 categories:

ToolDescription
initInitialize Strudel in browser
ToolDescription
edit_patternMutate the current session pattern. mode=write replaces the editor contents (default; mirrors the old write tool exactly, including optional pattern validation and auto_play). mode=append concatenates code after the current pattern with a newline. mode=insert places code at the given line position. mode=replace substitutes search with replace โ€” the first occurrence only, unless replace_all is true. The response reports how many occurrences matched, were replaced, and remain. mode=clear empties the editor. Example: edit_pattern({ mode: "write", pattern: "s("bd")", auto_play: true }). For reading the editor without mutating it use get_pattern; for the on-disk pattern catalog use pattern_store.
get_patternGet current pattern code
ToolDescription
playbackControl transport on the current session. action=play starts the editor pattern. action=pause halts the clock where it is, so play resumes mid-cycle. action=stop ends playback. Example: playback({ action: "play" }). For pattern editing use edit_pattern; for tempo use set_tempo.
set_tempoSet BPM. Writes setcpm(bpm/4), assuming one bar of 4/4 per cycle.
ToolDescription
pattern_storePersist patterns to disk and read them back. Use action=save to write the current session pattern under a name; action=load to restore a named pattern into the current session; action=list to enumerate the on-disk catalog (optionally filtered by tag). Example: pattern_store({ action: "save", name: "my-jam", tags: ["techno"] }). For session lifecycle (create/destroy/list active sessions) use the session tool โ€” pattern_store deals with on-disk patterns, not runtime sessions.
import_midiConvert a .mid file into a playable Strudel pattern (Phase 1: literal transcription, #201). Use source="base64" with data= for inline bytes, or source="path" with data= to read from the patterns/midi/ directory (path traversal blocked). Drum tracks (MIDI channel 10) emit one s() lane per sample so simultaneous kicks/hats do not collide. Pitched tracks emit note("...").s("piano") with simultaneous notes merged into [a,b,c] chord tokens. Phase 2+ (structural compression, voice separation, LLM idiomatic pass) tracked in separate issues. Example: import_midi({ source: "path", data: "drumloop.mid", steps_per_cycle: 16 }). For the reverse direction (Strudel โ†’ MIDI) use the export_midi tool.
ToolDescription
historyNavigate or inspect the pattern edit history. action=undo reverts the last edit on the targeted session. action=redo replays a previously-undone edit. action=list returns recent entries with timestamps and previews (limit defaults to 10). action=restore jumps the editor to a specific entry by id (current pattern goes on the undo stack). action=compare diffs two entries by id (or one entry vs current pattern). Example: history({ action: "list", limit: 5 }) โ€” recent edits, newest first. For on-disk saved patterns use pattern_store โ€” history deals with the in-memory edit timeline of the current session.
ToolDescription
composeGenerate, write, and play a complete pattern in one step. Auto-initializes default browser if needed.
generate_partGenerate a single instrumental layer and append it to the current session pattern. role=drums takes style (e.g. "techno"/"house") and optional complexity 0-1. role=bass takes key (e.g. "C") + style. role=melody takes root/scale (e.g. C/minor) and optional length (notes). role=fill takes style and optional bars. Example: generate_part({ role: "drums", style: "techno", complexity: 0.7 }). For full compositions use compose; for rhythmic patterns use generate_rhythm; for music-theory queries use music_theory.
generate_rhythmGenerate a rhythmic pattern and append it to the current session. type=euclidean produces a Euclidean rhythm with hits evenly distributed across steps (optional sound param, default "bd"). type=polyrhythm overlays multiple sound layers with given pattern numbers. Example: generate_rhythm({ type: "euclidean", hits: 3, steps: 8, sound: "hh" }). For complete patterns (drums/bass/melody) use generate_part; for whole compositions use compose.
ToolDescription
music_theoryMusic-theory queries. query=scale returns the notes of a scale (e.g. "C major scale: C, D, E, F, G, A, B"). query=chord_progression returns a chord progression for the key/style AND writes the resulting chord pattern into the current session. Example: music_theory({ query: "scale", root: "C", scale: "major" }). For pattern generation (drums/bass/melody) use generate_part; for rhythmic patterns use generate_rhythm.
ToolDescription
transformApply a single transform op to the current session pattern. op=transpose shifts notes by semitones. op=reverse appends .rev to the pattern. op=stretch slows by factor (>1 slower, <1 faster). op=quantize snaps to the grid (e.g. "1/16"). op=humanize adds rand-nudge timing of amount (0-1). op=swing applies .swing(amount). op=scale applies a root/scale filter to notes. op=vary returns a variation of type (subtle/moderate/extreme/glitch/evolving). Example: transform({ op: "transpose", semitones: 7 }). For effects (add/remove) use effect; for mood/energy/refine use shape; for tempo use set_tempo.
effectAdd or remove a Strudel effect on the current session pattern. action=add appends .<effect>(<params>). action=remove strips EVERY .<effect>(...) call from the pattern, not just the last. Example: effect({ action: "add", effect: "lpf", params: "1000" }). For higher-level effect bundles (mood/energy/refine) use shape; for raw transforms use transform.
shapeShape the current pattern along one of three high-level dimensions. dimension=mood applies a mood profile (dark/euphoric/melancholic/aggressive/dreamy/peaceful/energetic) with optional intensity 0-1. dimension=energy applies an energy level (integer 0-10). dimension=refine applies a directional refinement: faster/slower/louder/quieter/brighter/darker/"more reverb"/drier. All three auto-play by default. Example: shape({ dimension: "mood", target_mood: "dark", intensity: 0.8 }). For raw transform ops use transform; for explicit effects use effect.
ToolDescription
ai_assistGemini-backed pattern assistance. task=feedback returns creative critique on the current pattern (optionally with audio analysis). task=suggest analyzes the currently playing audio and suggests a complementary Strudel pattern as text (not auto-executed). task=jam generates a fresh layer (drums/bass/melody/pad/texture) and merges it into the current pattern, then auto-plays. All three share Gemini auth + rate limiting. Example: ai_assist({ task: "jam", layer: "bass" }). Needs GEMINI_API_KEY, or a logged-in claude/agy/codex CLI on PATH (#252). For non-AI pattern generation use generate_part; for full compositions use compose.
ToolDescription
analyzeAudio analysis on the currently-playing pattern. include=["all"] (default) returns the full spectrum + features object, matching the pre-consolidation behaviour. include=["tempo"] returns only BPM with confidence. include=["key"] returns detected key + scale + confidence. include=["spectrum"] returns FFT features (bass, mid, treble, brightness, etc.). include=["rhythm"] returns rhythm-only analysis (complexity, density, syncopation). Combining values returns an object keyed by category, e.g. analyze({ include: ["tempo", "key"] }) โ†’ { tempo: {...}, key: {...} }. Example: analyze({ include: ["tempo"] }) to cheaply re-check BPM during a session. For static pattern analysis (no browser) use analyze_pattern_local; for runtime validation use validate_pattern_runtime.
validate_pattern_runtimeValidate pattern with runtime error checking (monitors Strudel console for errors)
validate_pattern_localValidate pattern syntax against the local StrudelEngine, which runs in a sandboxed child process (no browser required)
analyze_pattern_localStatic analysis (events/cycle, complexity, optional BPM) without browser playback
query_pattern_eventsEnumerate events the pattern would emit between two cycle indices (max 16 cycles)
transpile_patternTranspile pattern source via StrudelEngine; returns transpiled code or syntax error
ToolDescription
sessionManage isolated Strudel browser sessions (multi-session, #108). action=create starts a new named session (sessions share one browser but isolated contexts). action=destroy closes a named session and releases its resources. action=list returns metadata for all active sessions (id, created, last_activity, is_playing, is_default). action=switch changes the default session that subsequent tool calls route to when no session_id is passed. Example: session({ action: "create", session_id: "live-set-1" }). For the on-disk pattern catalog use pattern_store(action=list) โ€” session(action=list) lists runtime sessions, not saved patterns.
ToolDescription
export_midiExport current pattern to MIDI file. Parses note(), n(), and chord() functions.
browser_windowInteract with the visible Strudel browser window. action=show brings the window to the foreground. action=screenshot captures the current editor view to disk (optional filename). Example: browser_window({ action: "screenshot", filename: "demo.png" }). For diagnostics (status/errors/perf) use diagnostics; for the editor itself use edit_pattern.
ToolDescription
audio_captureRecord audio output from the live Strudel session. action=start begins streaming capture (optional format webm/opus, default webm). action=stop ends the stream and returns base64-encoded audio. action=sample captures a fixed-duration window in one call (100-60000ms, default 5000ms). Example: audio_capture({ action: "sample", duration: 3000 }). Audio must be playing for capture to record meaningful data. For MIDI export use export_midi; for runtime diagnostics use diagnostics. Each session has its own recorder, so captures in different sessions do not interfere (#180).
ToolDescription
diagnosticsInspect server and browser state. level=status returns a quick state snapshot (cache read, <15ms SLA), plus which config.json was read and any warnings from it. level=full returns detailed browser diagnostics including caches, errors, and performance. level=perf returns server-side timing metrics + top bottlenecks. level=memory returns process memory usage. level=errors returns captured console errors and warnings from Strudel. Default level=full preserves the pre-consolidation behaviour. Example: diagnostics({ level: "status" }) โ€” millisecond-cheap. For screenshots use browser_window; for tool listings use the strudel://docs/tools resource.
ToolDescription
export_audioRecord a window of live Strudel audio and write it to a file. Returns a path plus what was actually recorded, not a wall of base64 โ€” prefer this over audio_capture when you want the audio to exist somewhere. format=wav (default) decodes to 16-bit PCM a DAW will open; format=webm writes the raw Opus recording. Reports silent captures instead of writing a silent file and claiming success. Audio must already be playing: call playback({ action: "play" }) first. Example: export_audio({ duration: 4000, filename: "take-01" }).

Auto-generated from source. 28 tools registered.

Usage Examples

Basic Pattern Creation

Create a Simple Beat

You: Initialize Strudel and create a simple techno beat

Claude: [Initializes browser]
        [Generates and writes pattern]
        [Starts playback]

Pattern created:
setcpm(130/4)
stack(
  s("bd*4"),
  s("~ cp ~ cp"),
  s("hh*8").gain(0.4)
).gain(0.8)

Generate a Bassline

You: Add a techno bassline in C

Claude: [Generates bassline]
        [Appends to current pattern]

Added: note("c2 c2 c2 c2").s("sawtooth").cutoff(800)

Advanced Music Generation

Jazz Composition

You: Create a jazz progression in F major with a walking bass

Claude: Generated progression: "Gm7" "C7" "Fmaj7"

Pattern:
stack(
  note("Gm7" "C7" "Fmaj7").struct("1 ~ ~ ~").s("piano"),
  note("g2 c2 f2").s("sine").gain(0.7)
)

Drum & Bass Track

You: Generate a complete drum & bass track at 174 BPM

Claude: [Creates multi-layer pattern with drums, bass, and pads]

// dnb pattern in C at 174 BPM
setcpm(174/4)
stack(
  // Drums
  s("bd ~ ~ [bd bd] ~ ~ bd ~, ~ ~ cp ~ ~ cp ~ ~").fast(2),

  // Bass
  note("c1 ~ ~ c2 ~ c1 ~ ~").s("square").cutoff(400),

  // Pads (atmospheric)
  note("c3 eb3 g3 bb3").s("sawtooth")
    .attack(2).release(4).room(0.8).gain(0.6)
)

Real-Time Manipulation

Live Audio Analysis

You: Analyze what's playing and show me the frequency breakdown

Claude: Audio Analysis Results:
{
  "connected": true,
  "features": {
    "average": 78.5,
    "peak": 180,
    "peakFrequency": 120,
    "bass": 145,
    "mid": 82,
    "treble": 45,
    "isPlaying": true,
    "brightness": "balanced"
  }
}

Strong bass presence detected - typical for techno/house patterns.

Pattern Variations

You: Create a glitchy variation of the current pattern

Claude: [Applies glitch transformation]

Added: .sometimes(x => x.chop(8).rev).rarely(x => x.speed(-1))

Multi-Track Composition

Layered Ambient Track

You: Create an ambient soundscape with multiple layers

Claude: [Generates complex multi-layer pattern]

setcpm(90/4)
stack(
  // Deep bass drone
  note("c1").s("sine").attack(4).release(8).gain(0.5),

  // Texture pad
  note("c3 eb3 g3").s("sawtooth")
    .attack(2).release(6).room(0.9).gain(0.4),

  // Sparse percussion
  s("bd ~ ~ ~, ~ ~ ~ hh:8").room(0.9).gain(0.3),

  // Melodic sparkles
  note("c5 g5 eb5 bb5").struct("~ 1 ~ 1 ~ ~ 1 ~")
    .s("triangle").delay(0.5).room(0.8).gain(0.3)
)

Session Management

Save and Load Patterns

You: Save this pattern as "techno-groove-1" with tags "techno" and "experimental"

Claude: Pattern saved as "techno-groove-1"

---

You: List all my techno patterns

Claude: Saved patterns with tag "techno":
โ€ข techno-groove-1 [techno, experimental] - 2025-10-23T19:30:00.000Z
โ€ข acid-bassline [techno, acid] - 2025-10-23T18:15:00.000Z

---

You: Load "acid-bassline"

Claude: Loaded pattern "acid-bassline"
[Pattern written to editor]

Music Theory Applications

Generate Euclidean Rhythms

You: Create a 5/8 euclidean rhythm with kick drum

Claude: Generated Euclidean rhythm (5/8)

Pattern: s("bd").struct("1 ~ 1 ~ 1 1 ~ 1")

Complex Polyrhythms

You: Generate a polyrhythm with kick (4/16), snare (7/16), and hi-hat (3/16)

Claude: Generated polyrhythm

stack(
  s("bd").euclid(4, 16),
  s("cp").euclid(7, 16),
  s("hh").euclid(3, 16)
)

Integration with LLM Agents

AI-Assisted Composition

You: Analyze the mood of my current pattern and suggest complementary elements

Claude: [Analyzes pattern structure and audio features]

Your pattern has:
- Energetic rhythm (130 BPM)
- Strong bass presence
- Minimal melodic content

Suggestions:
1. Add arpeggiated synth for movement
2. Include occasional breaks/fills
3. Layer atmospheric pad for depth

Would you like me to add any of these?

You: Add an arpeggiated synth in C minor

Claude: [Generates and adds]

note("c4 eb4 g4 bb4 c5").struct("1 1 1 1 1 1 1 1")
  .s("triangle").delay(0.25).room(0.3).gain(0.5)

Testing

Tools are exercised against real Strudel.cc interaction through the browser test suite; those are skipped in CI (require Playwright + audio) but run locally:

bash
# Run integration tests
npm run test:integration

# Run browser tests
node tests/strudel-integration.js

# Run npm test to see current pass/fail/skip counts

Configuration

config.json

json
{
  "headless": false,        // Set true for background operation
  "strudel_url": "https://strudel.cc/",
  "patterns_dir": "./patterns",
  "exports_dir": "./exports",
  "audio_analysis": {
    "fft_size": 2048,       // power of 2 in [32, 32768]; code default is 1024
    "smoothing": 0.8        // number in [0, 1]; default 0.8
  }
}

strudel_url is the REPL the browser is pointed at โ€” set it to run against a self-hosted or pinned Strudel build. Only http/https are accepted; anything else falls back to the default with a logged warning. patterns_dir is where pattern_store reads and writes.

exports_dir is where export_midi({format:"file"}) writes. Exported filenames are reduced to a basename and confined to this directory โ€” a name containing path separators is sanitized rather than honoured, and the response reports the path actually written.

Unknown top-level keys are logged as warnings at startup rather than silently ignored.

audio_analysis.fft_size tunes the FFT bin count on the AnalyserNode attached to Strudel's audio graph โ€” larger = better frequency resolution at higher CPU cost. smoothing is the analyser's smoothingTimeConstant (higher = steadier spectrum, more lag). Invalid values fall back to defaults with a warning; the frequency-band boundaries inside analyze rescale automatically so band Hz coverage stays consistent across FFT sizes.

Architecture

The server is a thin MCP dispatcher (src/server/server.ts, ~520 lines) over twelve per-domain tool modules (src/server/tools/*.ts), ten services (MusicTheory, PatternGenerator, SessionManager, AudioCaptureService, AudioExportService, GeminiService, MIDIExportService, MIDIImportService, StrudelEngine, PatternSandbox) plus a provider-agnostic AI transport layer (src/services/ai/), two controllers (StrudelController for Playwright, AudioAnalyzer for Web Audio API), and a JSON-on-disk PatternStore. Browser automation drives the editor on strudel.cc through window.strudelMirror.editor.dispatch(...) rather than keyboard simulation โ€” about 80% faster.

For the full breakdown โ€” component diagram, per-component responsibilities, directory layout, data flow, and optimization strategies โ€” see ARCHITECTURE.md.

Pattern Examples

Minimal Techno (Verified Working)

javascript
setcpm(130/4)
stack(
  s("bd*4").gain(0.9),
  s("~ cp ~ cp").room(0.2),
  s("hh*16").gain(0.4).pan(sine.range(-0.5, 0.5)),
  note("c2 c2 eb2 c2").s("sawtooth").cutoff(800)
).swing(0.05)

Drum & Bass (Verified Working)

javascript
setcpm(174/4)
stack(
  s("bd ~ ~ [bd bd] ~ ~ bd ~, ~ ~ sn:3 ~ ~ sn:3 ~ ~").fast(2),
  s("hh*16").gain(0.5),
  note("e1 ~ ~ e2 ~ e1 ~ ~").s("sine:2").lpf(200)
)

Generated Jazz Progression

javascript
// Jazz ii-V-I in F
stack(
  note("Gm7" "C7" "Fmaj7").struct("1 ~ ~ ~").s("piano"),
  note("g2 c2 f2").s("sine").gain(0.7)
)

Docker Support

bash
# Build image
docker build -t live-coding-music-mcp .

# Run container
docker run -it --rm live-coding-music-mcp

# Or use docker-compose
docker-compose up

Development

For development setup, scripts, debugging, adding new tools, and the release process, see DEVELOPMENT.md.

Quick links:

The Configuration section above covers runtime config (config.json). ARCHITECTURE.md covers the system design.

Performance

Measured against the current StrudelController cache + Strudel.cc on a developer machine:

OperationLatency
Browser initialization1.5โ€“2 s (with resource blocking)
Pattern write50โ€“80 ms (cached CodeMirror editor access)
Pattern read (cached)10โ€“15 ms
Play / pause / stop100โ€“150 ms
Audio analysis (single FFT)10โ€“15 ms
Tempo detection<100 ms (onset-based; reports poll cadence, not tempo โ€” #322)
Key detection<100 ms (Krumhansl-Schmuckler, Pearson; bass-register accuracy depends on fft_size)
Pattern generation<100 ms (template-based)
Process resident memory~120โ€“150 MB

Advanced Usage

Custom Music Theory

Create Custom Scales

typescript
// The MusicTheory service supports 15+ built-in scales
// You can request any combination:

You: Generate a lydian scale starting on D

Claude: D lydian scale: D, E, F#, G#, A, B, C#

Complex Chord Progressions

typescript
You: Create a jazz ii-V-I progression in Bb with extended voicings

Claude: Generated jazz progression in Bb: "Cm7" "F7" "Bbmaj7"

Pattern:
note("Cm7" "F7" "Bbmaj7")
  .struct("1 ~ ~ ~")
  .s("sawtooth")
  .add(note("7"))  // Add 7th extensions
  .room(0.4)

Advanced Rhythm Techniques

Euclidean Patterns

You: Create a complex polyrhythm using euclidean patterns:
     - Bass drum: 7 hits in 16 steps
     - Snare: 5 hits in 16 steps
     - Hi-hat: 11 hits in 16 steps

Claude: [Generates mathematically perfect polyrhythm]

stack(
  s("bd").euclid(7, 16),
  s("cp").euclid(5, 16),
  s("hh").euclid(11, 16)
)

Generative Variations

You: Create 3 variations of my current pattern: subtle, moderate, and glitchy

Claude: [Generates three variants]

Version 1 (subtle):
original.sometimes(x => x.fast(2))

Version 2 (moderate):
original.every(4, x => x.rev).sometimes(x => x.fast(2))

Version 3 (glitchy):
original.sometimes(x => x.chop(8).rev).rarely(x => x.speed(-1))

Performance Optimization Tips

Pattern Caching

// The server caches patterns intelligently:
// - Generated patterns stored before browser init
// - Editor content cached for 100ms
// - Audio analysis cached for 50ms

// For fastest performance:
1. Generate patterns before initializing browser
2. Initialize once and reuse the session
3. Batch multiple changes together

Headless Mode

json
// In config.json:
{
  "headless": true,  // Faster, no GUI
  "strudel_url": "https://strudel.cc/",
  "patterns_dir": "./patterns"
}

Integration Patterns

Workflow Automation

You: Create a complete track:
     1. Start with techno drums at 130 BPM
     2. Add acid bassline in E
     3. Layer ambient pads
     4. Save as "track-001" with tags "techno", "acid", "experimental"

Claude: [Executes full workflow]
        [Initializes browser]
        [Generates drum pattern]
        [Adds bassline]
        [Layers pads]
        [Saves with metadata]

Complete! Pattern saved as "track-001"

Audio-Reactive Composition

You: Monitor the audio and suggest adjustments based on frequency balance

Claude: [Analyzes current playback]

Analysis shows:
- Bass: 180 (strong)
- Mid: 45 (weak)
- Treble: 30 (weak)

Recommendations:
1. Reduce bass gain by 20%
2. Add mid-range melodic content
3. Include hi-hat pattern for treble

Apply changes? [Yes/No]

Advanced Pattern Manipulation

Dynamic Transformations

You: Apply these transformations in sequence:
     1. Transpose up 3 semitones
     2. Add 10% humanization
     3. Apply reverb with 0.6 room size
     4. Every 4th cycle, reverse the pattern

Claude: [Applies transformations]

Final pattern:
[original]
  .transpose(3)
  .nudge(rand.range(-0.1, 0.1))
  .room(0.6)
  .every(4, x => x.rev)

Conditional Logic

You: Create a pattern that evolves:
     - Bars 1-4: Simple kick pattern
     - Bars 5-8: Add snare
     - Bars 9-16: Add full drum kit
     - Then loop with variations

Claude: [Generates evolving pattern]

s("bd*4")
  .struct("1 ~ ~ ~")
  .mask("<1 1 1 1 0 0 0 0>/4")  // First 4 bars
  .stack(
    s("~ cp ~ cp")
      .mask("<0 0 0 0 1 1 1 1>/8")  // Bars 5-8
  )
  // Full complexity from bar 9
  .every(8, x => x.sometimes(y => y.fast(2)))

Security

Pattern Validation

All patterns are validated before execution:

  • Dangerous gain levels (>2.0) are flagged
  • Eval blocks are rejected
  • Path traversal attacks are blocked in PatternStore

Browser Isolation

Be precise about what this does and does not give you:

  • The OS-level Chrome sandbox is disabled. Chromium is launched with --no-sandbox and --disable-setuid-sandbox (src/StrudelController.ts) for headless and container compatibility, where the sandbox otherwise needs CAP_SYS_ADMIN or user namespaces. This is a deliberate trade-off, not a security feature.
  • The browser context is a fresh, non-persistent Playwright context โ€” no profile on disk, no cookies or storage carried over between runs, and no file:// navigation is ever performed.
  • Resource blocking is a performance optimization, not a security boundary. Images, fonts, and media are aborted to speed up page load; scripts, stylesheets, and network requests from strudel.cc are allowed through as normal.
  • Patterns are validated before execution (see above), which is the actual pre-execution control.

If you need real isolation, run the whole MCP server inside a locked-down container or VM. The server does not sandbox itself.

Known Limitations

  • No authentication: The MCP server trusts all incoming requests
  • Local only: Designed for local development, not network deployment
  • Pattern execution: Patterns execute in browser context with audio access

Reporting Security Issues

Found a vulnerability? Please open a security issue or email the maintainer directly. Do not disclose publicly until patched.

Troubleshooting

Common Issues

Browser doesn't open

Symptom: Error: Browser not initialized or Chromium launch fails

Solutions:

bash
# Install Chromium for Playwright
npx playwright install chromium

# If that fails, try installing all browsers
npx playwright install

# Check Playwright installation
npx playwright --version

# For Linux, install dependencies
sudo npx playwright install-deps chromium

Audio analysis returns "not connected"

Symptom: Audio analysis shows connected: false

Solutions:

  1. Ensure pattern is playing first:

    You: Play the pattern, wait 2 seconds, then analyze
  2. Reinitialize the browser:

    You: Stop, close the browser, reinitialize, and try again
  3. Check audio context activation:

    • Audio contexts require user interaction on some systems
    • The browser window must be visible (not headless) for first run

Pattern syntax errors

Symptom: Pattern doesn't play or shows errors in console

Solutions:

Common issues:
1. Missing quotes: s(bd*4) โ†’ s("bd*4")
2. Unmatched parentheses: stack(s("bd")) โ†’ stack(s("bd")))
3. Invalid note names: note("h2") โ†’ note("c2")

Test with minimal pattern first:
s("bd*4")

Then build complexity gradually.

MCP connection issues

Symptom: Claude can't find the server or tools

Solutions:

bash
# Verify server is built
npm run build

# Check if server responds
echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node dist/index.js

# Should return JSON with 28 tools

# Reinstall MCP server in Claude
claude mcp remove strudel
claude mcp add strudel node $(pwd)/dist/index.js

# Restart Claude
claude chat

Performance issues / Slow response

Symptom: Operations take longer than expected

Solutions:

  1. Enable caching (default, but verify):

    • Editor caching: 100ms TTL
    • Audio analysis: 50ms TTL
  2. Use headless mode for faster operation:

    json
    // config.json
    { "headless": true }
  3. Batch operations:

    Instead of:
    - Add drums
    - Add bass
    - Add melody
    
    Do:
    - Generate complete pattern with drums, bass, and melody
  4. Reduce browser overhead:

    • Close other browser instances
    • Disable browser DevTools
    • Use resource blocking (enabled by default)

Patterns not saving

Symptom: pattern_store({ action: "save" }) fails or patterns don't persist

Solutions:

bash
# Check patterns directory exists
ls -la ./patterns

# Create manually if needed
mkdir -p ./patterns

# Verify write permissions
touch ./patterns/test.json
rm ./patterns/test.json

# Check for invalid pattern names
# Valid: "techno-beat-1", "my_pattern", "track001"
# Invalid: "pattern/with/slashes", "name:with:colons"

Platform-Specific Issues

macOS

bash
# Keyboard shortcut uses Meta (Cmd) key
# Already handled by ControlOrMeta

# If Chromium crashes on M1/M2:
npx playwright install chromium --with-deps

Linux

bash
# Install system dependencies
sudo npx playwright install-deps chromium

# If running in Docker/headless environment:
# Ensure config.json has headless: true

Windows

bash
# Use PowerShell or Git Bash
# Paths should use forward slashes in config.json

# If Chromium doesn't launch:
npx playwright install chromium

Debugging Tips

Enable Verbose Logging

bash
# Set environment variable
DEBUG=* node dist/index.js

# Or in Claude:
You: Enable detailed logging for the next operation

Check Browser State

You: Show me the current browser initialization state
     and any cached patterns

Claude: Browser state:
- Initialized: true
- Cached patterns: 2
- Undo stack depth: 5
- Last operation: edit_pattern (2.5s ago)

Validate Pattern Syntax

You: Before playing, validate this pattern syntax:
     s("bd*4, ~ cp ~ cp")

Claude: [Checks syntax]
Valid Strudel pattern.
Ready to play.

Getting Help

If you encounter issues not covered here:

  1. Check existing issues: GitHub Issues
  2. Run integration tests: npm run test:integration
  3. Enable debug mode: DEBUG=* npm start
  4. Consult Strudel docs: Strudel.cc documentation
  5. Create new issue: Include error messages, OS, Node version, and steps to reproduce

Contributing

PRs welcome โ€” bug fixes, docs, tests, new tools. See CONTRIBUTING.md for the contribution guide and DEVELOPMENT.md for setup and workflow.

For non-trivial work, open a GitHub issue first (issue policy in CLAUDE.md). For typos and small fixes, a PR directly is fine.

GitHub Discussions are open for use-case questions and design conversations.

License

AGPL-3.0-or-later โ€” see LICENSE for the full text.

This project depends on @strudel/core, @strudel/mini, @strudel/tonal, and @strudel/transpiler, which are AGPL-3.0 licensed by the upstream Strudel project. Since we import from those packages and redistribute the combined work via npm, this project must be distributed under the same copyleft terms. If you fork or redistribute, you must keep the AGPL license and provide source access to any network-accessible users (AGPL ยง13).

Earlier versions of this package (including @williamzujkowski/strudel-mcp-server prior to deprecation) shipped with an MIT declaration โ€” that was incorrect given the AGPL dependencies. v2.0.0 of @williamzujkowski/live-coding-music-mcp corrects the license to AGPL-3.0-or-later. v1.0.0 of this package is deprecated; install v2.0.0 or later (current: v4.0.0).

Acknowledgments

  • Strudel.cc โ€” pattern-based live coding environment (this project is a fan adapter, not affiliated)
  • TidalCycles โ€” original pattern language Strudel descends from
  • Anthropic โ€” Claude and the MCP protocol
  • Playwright โ€” browser automation

v4.0.0 โ€” Open source, AGPL-3.0-or-later, experimental | Report issues | Contribute

This project is under active development. Core features work, but expect bugs and breaking changes. Not recommended for production use.

Installation

TypingMind
Prerequisites:

Node.js 18+

{
  "mcpServers": {
    "strudel-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "@williamzujkowski/strudel-mcp-server"
      ]
    }
  }
}

Use Strudel Live Coding MCP with multiple AI models

TypingMind connects MCP tools at the workspace level, so once Strudel Live Coding is connected, you can use it with different AI models in TypingMind instead of setting it up separately for each model. This MCP runs locally through the TypingMind MCP connector on your device.

Setup guide to use the local connector

Use this when the MCP server needs access to local files, apps, or private resources on your computer.

1

Open the MCP settings

In TypingMind, go to Settings, Advanced Settings, then Model Context Protocol and choose Setup Connector.

  1. Open TypingMind in your browser.
  2. Click the Settings icon.
  3. Go to Advanced Settings.
  4. Open the Model Context Protocol section.
  5. Click Setup Connector and choose This Device.
TypingMind MCP connector setup screen with This Device selected
2

Run the connector command

Choose This Device, copy the command from TypingMind, and run it in Terminal. Keep the process running while you use MCP.

  1. Copy the setup command shown by TypingMind.
  2. Open Terminal on macOS or Windows Terminal on Windows.
  3. Paste and run the command.
  4. Approve the package install if Terminal asks you to proceed.
  5. Keep the Terminal window running while using MCP tools.
3

Add Strudel Live Coding as a server

When the connector status is Ready, click Edit Servers and paste the MCP server configuration.

  1. Wait until the connector status shows Ready.
  2. Click Edit Servers.
  3. Paste the Strudel Live Coding MCP server configuration.
  4. Save the server list.
  5. Refresh if you want to confirm the connector is still ready.
TypingMind MCP settings showing active server and Edit Servers button
{
  "mcpServers": {
    "strudel-live-coding": {
      "command": "npx",
      "args": [
        "-y",
        "@williamzujkowski/strudel-mcp-server"
      ]
    }
  }
}
4

Use it across models

Save the server list, open Plugins, enable the Strudel Live Coding MCP tools, then select any supported AI model in TypingMind and use the tools in chat or assign them to an AI agent.

  1. Open the Plugins page in TypingMind.
  2. Enable the Strudel Live Coding MCP tools.
  3. Start a chat and choose the AI model you want to use.
  4. Use the MCP tools in chat or assign them to an AI agent.
  5. Switch to another AI model whenever needed without reconnecting MCP.
TypingMind chat using enabled MCP tools with a selected AI model
Can you use Strudel Live Coding to help me with this task?
Strudel Live Coding
Sure. I read it.
Here is what I found using Strudel Live Coding.

Frequently asked questions

What is the Strudel Live Coding MCP server used for?

Strudel Live Coding is an MCP server that lets compatible AI clients connect to external tools and context. In TypingMind, you can add this MCP server once and make its tools available in your AI workspace.

Can I use Strudel Live Coding MCP with multiple AI models in TypingMind?

Yes. TypingMind connects MCP tools at the workspace level, so you can use Strudel Live Coding with different AI models such as Claude, ChatGPT, Gemini, or other models you have configured in TypingMind without setting up the MCP server separately for each model.

Why use Strudel Live Coding MCP with TypingMind?

TypingMind is one of the best frontends for LLM chat because it brings multiple AI models, prompts, plugins, AI agents, API keys, and MCP tools into one workspace. With Strudel Live Coding connected, you can use its MCP tools across your preferred models while keeping your chat workflow organized in TypingMind.

How do I connect Strudel Live Coding MCP to TypingMind?

Strudel Live Coding runs through the TypingMind local MCP connector. This is best when the MCP server needs access to local files, desktop apps, command-line tools, or private resources on your computer.

What tools does Strudel Live Coding MCP provide in TypingMind?

Strudel Live Coding exposes MCP capabilities that can be enabled from the TypingMind Plugins page and used in chat or assigned to AI agents.

Do I need to share my API keys with TypingMind to use Strudel Live Coding MCP?

No. TypingMind is local-first and lets you keep your model providers, API keys, prompts, and MCP configuration under your control. If Strudel Live Coding requires authentication, add the required headers, OAuth settings, or local configuration for that MCP server when you create the connection.

Related MCP Servers

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter ๐Ÿ‘‡