A community-driven registry for Claude, Cursor, Windsurf, Cline & more. Not affiliated with Anthropic.
Are you the author? Sign in to claim
A Model Context Protocol (MCP) server that gives Claude direct control over Strudel.cc for AI-assisted music generation
🎵 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 | 86% statement coverage | Published to npm | Actively developed
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. 1709 tests pass, 86.32% statement coverage / 75.93% branch coverage. CI is hardened with OpenSSF Scorecard, SHA-pinned actions, CODEOWNERS, Dependabot, and lint as a blocking gate.
What "Beta" means here:
@strudel/* dependencies pinned to known-good versions; Dependabot bumps gated on CIpattern_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).strudel://examples, strudel://patterns, strudel://styles, strudel://docs/tools.session_id; sessions have isolated browser pages, undo/redo/history stacks, and audio-capture services.ai_assist for feedback, suggestions, and jamming.tools/call: clients branch on { ok, errorCategory, isRetryable } instead of parsing free-text.any in test mocks).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.
18 example patterns ship in patterns/examples/, grouped by genre:
Each example is a JSON file with pattern code, BPM, key, and a description. See patterns/examples/README.md for details. Agents can also list these via the strudel://examples MCP resource without making any tool calls.
@williamzujkowski/strudel-mcp-serverThis 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:
# 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:
// 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.
| Requirement | Version | Notes |
|---|---|---|
| Node.js | 22.x+ | LTS required |
| npm | 10+ | Comes with Node.js |
| Chromium | Latest | Auto-installed by Playwright |
| Audio output | Any | Required for playback (speakers/headphones) |
Optional: Docker for containerized deployment.
npm install -g @williamzujkowski/live-coding-music-mcp
# Install browser (required once)
npx playwright install chromium
# 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
Get from install to your first generated pattern in under two minutes.
npm install -g @williamzujkowski/live-coding-music-mcp
npx playwright install chromium # one-time
Building from source instead? See Installation → From Source.
Edit your Claude Desktop config file:
| OS | Config 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:
{
"mcpServers": {
"live-coding-music": {
"command": "live-coding-music-mcp"
}
}
}
Restart Claude Desktop. The server appears under the 🔌 plug icon.
# 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
# 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 26 tools. If you see fewer, the build is out of date — run npm run build.
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 audio analysis (tempo / key detection) is more reliable in headed mode; headless audio sampling is best-effort.
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.Common operations as one-line tool calls:
| Action | Tool call |
|---|---|
| Initialize browser | init |
| Create a techno beat in one shot | compose({ style: "techno" }) |
| Play pattern | playback({ action: "play" }) |
| Stop playback | playback({ action: "stop" }) |
| Get current pattern | get_pattern |
| Analyze audio (all features) | analyze({ include: ["all"] }) |
| Detect tempo only | analyze({ include: ["tempo"] }) |
| Save pattern | pattern_store({ action: "save", name: "my-pattern" }) |
| Undo last edit | history({ action: "undo" }) |
| Edit current pattern | edit_pattern({ mode: "write", pattern: "..." }) |
| Create an isolated session | session({ 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
27 tools across 14 categories:
| Tool | Description |
|---|---|
init | Initialize Strudel in browser |
| Tool | Description |
|---|---|
edit_pattern | Mutate the current session pattern. |
get_pattern | Get current pattern code |
| Tool | Description |
|---|---|
playback | Control transport on the current session. |
set_tempo | Set BPM |
| Tool | Description |
|---|---|
pattern_store | Persist patterns to disk and read them back. |
import_midi | Convert a .mid file into a playable Strudel pattern (Phase 1: literal transcription, #201). |
| Tool | Description |
|---|---|
history | Navigate or inspect the pattern edit history. |
| Tool | Description |
|---|---|
compose | Generate, write, and play a complete pattern in one step. Auto-initializes default browser if needed. |
generate_part | Generate a single instrumental layer and append it to the current session pattern. |
generate_rhythm | Generate a rhythmic pattern and append it to the current session. |
| Tool | Description |
|---|---|
music_theory | Music-theory queries. |
| Tool | Description |
|---|---|
transform | Apply a single transform op to the current session pattern. |
effect | Add or remove a Strudel effect on the current session pattern. |
shape | Shape the current pattern along one of three high-level dimensions. |
| Tool | Description |
|---|---|
ai_assist | Gemini-backed pattern assistance. |
| Tool | Description |
|---|---|
analyze | Audio analysis on the currently-playing pattern. |
validate_pattern_runtime | Validate pattern with runtime error checking (monitors Strudel console for errors) |
validate_pattern_local | Validate pattern syntax against the in-process StrudelEngine (no browser required) |
analyze_pattern_local | Static analysis (events/cycle, complexity, optional BPM) without browser playback |
query_pattern_events | Enumerate events the pattern would emit between two cycle indices (max 16 cycles) |
transpile_pattern | Transpile pattern source via StrudelEngine; returns transpiled code or syntax error |
| Tool | Description |
|---|---|
session | Manage isolated Strudel browser sessions (multi-session, #108). |
| Tool | Description |
|---|---|
export_midi | Export current pattern to MIDI file. Parses note(), n(), and chord() functions. |
browser_window | Interact with the visible Strudel browser window. |
| Tool | Description |
|---|---|
audio_capture | Record audio output from the live Strudel session. |
| Tool | Description |
|---|---|
diagnostics | Inspect server and browser state. |
Auto-generated from source. 27 tools registered.
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)
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)
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)
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)
)
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))
Layered Ambient Track
You: Create an ambient soundscape with multiple layers
Claude: [Generates complex multi-layer pattern]
setcpm(90)
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)
)
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]
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)
)
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)
Tools are exercised against real Strudel.cc interaction through the browser test suite; those are skipped in CI (require Playwright + audio) but run locally:
# 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
{
"headless": false, // Set true for background operation
"strudel_url": "https://strudel.cc/",
"patterns_dir": "./patterns",
"audio_analysis": {
"fft_size": 1024, // power of 2 in [32, 32768]; default 1024
"smoothing": 0.8 // number in [0, 1]; default 0.8
}
}
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.
The server is a thin MCP dispatcher (src/server/server.ts, ~510 lines) over twelve per-domain tool modules (src/server/tools/*.ts), six services (MusicTheory, PatternGenerator, SessionManager, AudioCaptureService, GeminiService, MIDIExportService, StrudelEngine), two controllers (StrudelController for Playwright, AudioAnalyzer for Web Audio API), and a JSON-on-disk PatternStore. Browser automation hits the CodeMirror editor on strudel.cc directly via editor.__view.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.
setcpm(130)
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)
setcpm(174)
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)
)
// Jazz ii-V-I in F
stack(
note("Gm7" "C7" "Fmaj7").struct("1 ~ ~ ~").s("piano"),
note("g2 c2 f2").s("sine").gain(0.7)
)
# 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
For development setup, scripts, debugging, adding new tools, and the release process, see DEVELOPMENT.md.
Quick links:
git clone to npm testgh release create triggers the publish workflowThe Configuration section above covers runtime config (config.json). ARCHITECTURE.md covers the system design.
Measured against the current StrudelController cache + Strudel.cc on a developer machine:
| Operation | Latency |
|---|---|
| Browser initialization | 1.5–2 s (with resource blocking) |
| Pattern write | 50–80 ms (cached CodeMirror editor access) |
| Pattern read (cached) | 10–15 ms |
| Play / pause / stop | 100–150 ms |
| Audio analysis (single FFT) | 10–15 ms |
| Tempo detection | <100 ms (onset-based; degraded under headless audio) |
| Key detection | <100 ms (Krumhansl-Schmuckler) |
| Pattern generation | <100 ms (template-based) |
| Process resident memory | ~120–150 MB |
Create Custom Scales
// 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
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)
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))
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
// In config.json:
{
"headless": true, // Faster, no GUI
"strudel_url": "https://strudel.cc/",
"patterns_dir": "./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]
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)))
All patterns are validated before execution:
Found a vulnerability? Please open a security issue or email the maintainer directly. Do not disclose publicly until patched.
Symptom: Error: Browser not initialized or Chromium launch fails
Solutions:
# 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
Symptom: Audio analysis shows connected: false
Solutions:
Ensure pattern is playing first:
You: Play the pattern, wait 2 seconds, then analyze
Reinitialize the browser:
You: Stop, close the browser, reinitialize, and try again
Check audio context activation:
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.
Symptom: Claude can't find the server or tools
Solutions:
# 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 26 tools
# Reinstall MCP server in Claude
claude mcp remove strudel
claude mcp add strudel node $(pwd)/dist/index.js
# Restart Claude
claude chat
Symptom: Operations take longer than expected
Solutions:
Enable caching (default, but verify):
Use headless mode for faster operation:
// config.json
{ "headless": true }
Batch operations:
Instead of:
- Add drums
- Add bass
- Add melody
Do:
- Generate complete pattern with drums, bass, and melody
Reduce browser overhead:
Symptom: pattern_store({ action: "save" }) fails or patterns don't persist
Solutions:
# 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"
# Keyboard shortcut uses Meta (Cmd) key
# Already handled by ControlOrMeta
# If Chromium crashes on M1/M2:
npx playwright install chromium --with-deps
# Install system dependencies
sudo npx playwright install-deps chromium
# If running in Docker/headless environment:
# Ensure config.json has headless: true
# Use PowerShell or Git Bash
# Paths should use forward slashes in config.json
# If Chromium doesn't launch:
npx playwright install chromium
Enable Verbose Logging
# 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.
If you encounter issues not covered here:
npm run test:integrationDEBUG=* npm startPRs 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.
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).
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.
A Jetbrains IDE IntelliJ plugin aimed to provide coding agents the ability to leverage intelliJ's indexing of the codeba
Run Claude Code as an MCP server so any agent can delegate coding tasks to it
Browser automation using accessibility snapshots instead of screenshots
via CLI