Skip to main content
Glama

CHUK Music MCP Server

A music design system for MCP — shadcn/ui for music composition.

You copy patterns into your project, you own them, you modify them. The library provides correct primitives and well-designed starting points, not a black box.

Vision

Music as a design system, not a DAW. This is a control-plane for composition.

LLMs operate at the intent level — structure, energy, arrangement. The system handles music theory. Composers own their patterns.

Features

  • Pattern System: Copyable, ownable pattern templates (drums, bass, harmony, melody, fx)

  • Style System: Constraint bundles that constrain, suggest, and validate

  • Arrangement Model: Layers × Sections structure with energy curves and harmony

  • Score IR: Versioned intermediate representation — golden-file testable, diffable, round-trippable

  • MIDI Export: Deterministic compilation from YAML to playable MIDI files

  • MCP Integration: Full MCP server with 37+ tools for AI-assisted composition

Quick Start

# Install git clone https://github.com/chuk-ai/chuk-mcp-music cd chuk-mcp-music pip install -e ".[dev]" # Compile an arrangement python examples/compile_arrangement.py # Verify determinism shasum output/demo.mid # e3b0c442... (always the same for the same input)

The Stack

Intent (LLM) ↓ Tokens (semantic constraints) ↓ Structure (sections, energy curves) ↓ Layers (drums, bass, harmony, melody, fx) ↓ Patterns (owned, modifiable recipes) ↓ Score IR (symbolic, inspectable, versioned) ↓ MIDI (deterministic compilation) ↓ Audio (optional, downstream)

What You Own vs What the Library Owns

You Own

Library Owns

Patterns you copy to your project

Schema definitions (pattern/v1, score_ir/v1)

Arrangement YAML files

Compiler pipeline

Style overrides and customizations

Validation rules

Project-specific pattern libraries

Default pattern/style libraries

Output MIDI files

IR specification and canonicalization

The boundary is clear: you own the content, the library owns the machinery.

Key Concepts

Patterns (The shadcn Layer)

Patterns are copyable, ownable, modifiable templates:

# patterns/bass/root-pulse.yaml schema: pattern/v1 name: root-pulse role: bass pitched: true parameters: density: type: enum values: [half, quarter, eighth] default: quarter variants: driving: density: eighth template: events: - degree: chord.root beat: 0 velocity: $velocity_base

Arrangements (Your Composition)

# arrangements/demo.arrangement.yaml schema: arrangement/v1 key: D_minor tempo: 124 harmony: default_progression: [i, VI, III, VII] sections: - name: intro bars: 8 - name: verse bars: 16 layers: bass: role: bass patterns: pulse: ref: bass/root-pulse variant: driving arrangement: intro: null verse: pulse

Styles (Constraint Bundles)

Styles do three things:

  1. Constrain — tempo ranges, forbidden patterns, key preferences

  2. Suggest — pattern shortlists per layer, register hints

  3. Validate — lint errors with actionable fixes

# styles/library/melodic-techno.yaml schema: style/v1 name: melodic-techno description: Driving, melodic electronic music tokens: tempo: range: [120, 128] default: 124 key_preference: minor structure_hints: breakdown_required: true section_multiples: 8 layer_hints: bass: suggested: [bass/rolling-sixteenths, bass/root-pulse] register: low forbidden: patterns: [drums/trap-*]

Validation output:

{ "valid": false, "errors": [ {"message": "Tempo 140 outside style range [120, 128]", "severity": "error"}, {"message": "Pattern drums/trap-hat forbidden by style", "severity": "error"} ], "suggestions": [ {"message": "Consider drums/four-on-floor for drums layer", "severity": "info"} ] }

Score IR (Intermediate Representation)

The Score IR is the stable, inspectable contract between arrangement and MIDI:

Arrangement YAML → Score IR (diffable, versioned) → MIDI

Same arrangement → same Score IR → same MIDI. Always.

Schema Excerpt (score_ir/v1)

{ "schema": "score_ir/v1", "name": "my-track", "key": "D_minor", "tempo": 124, "time_signature": {"numerator": 4, "denominator": 4}, "ticks_per_beat": 480, "total_bars": 24, "notes": [ { "start_ticks": 0, "pitch": 50, "duration_ticks": 480, "velocity": 90, "channel": 1, "source_layer": "bass", "source_pattern": "bass/root-pulse", "source_section": "verse", "bar": 0, "beat": 0.0 } ], "sections": [ {"name": "intro", "start_ticks": 0, "end_ticks": 15360, "bars": 8} ] }

Canonicalization Rules

  • Notes sorted by (start_ticks, channel, pitch)

  • Sections sorted by start_ticks

  • All times in ticks (480 ticks per beat)

  • Source traceability on every note

Usage

# Compile and inspect result = compiler.compile(arrangement) print(result.score_ir.summary()) # {'name': 'my-track', 'total_bars': 32, 'total_notes': 256, # 'layers': {'drums': 128, 'bass': 64, 'harmony': 64}, # 'pitch_range': (36, 72), 'velocity_range': (60, 110)} # Compare two versions diff = old_ir.diff_summary(new_ir) # {'notes_added': 12, 'notes_removed': 8, 'notes_unchanged': 244, # 'tempo_changed': False, 'key_changed': False} # Debug: "Why is this note here?" for note in result.score_ir.notes: if note.pitch == 50 and note.bar == 3: print(f"From {note.source_layer}/{note.source_pattern} in {note.source_section}")

Common Workflows

1. Create Track from Style

# Apply style → suggest patterns → add sections → arrange → compile music_apply_style(arrangement="my-track", style="melodic-techno") music_suggest_patterns(arrangement="my-track", role="bass") # Returns: ["bass/rolling-sixteenths", "bass/root-pulse"] music_add_section(arrangement="my-track", name="intro", bars=8) music_add_section(arrangement="my-track", name="verse", bars=16) music_arrange_layer(arrangement="my-track", layer="bass", section_patterns={"intro": None, "verse": "main"}) music_compile_midi(arrangement="my-track")

2. Iterate on Bassline

# Preview → tweak → diff → compile music_preview_section(arrangement="my-track", section="verse") music_update_pattern_params(arrangement="my-track", layer="bass", params={"density": "eighth"}) music_diff_ir(arrangement="my-track-v1", other_arrangement="my-track-v2") # {'notes_added': 32, 'notes_removed': 16, ...} music_compile_midi(arrangement="my-track")

3. Debug a Bad Result

# Compile to IR → inspect provenance → validate → fix result = music_compile_to_ir(arrangement="my-track") # Inspect which pattern produced the wrong notes # Each note has: source_layer, source_pattern, source_section, bar, beat music_validate(arrangement="my-track") # {"valid": false, "errors": [{"message": "Channel conflict on channel 1"}]} # Fix the issue music_set_layer_level(arrangement="my-track", name="bass", level=0.8)

4. Extract Stems (IR Round-Trip)

# Compile → modify IR → emit separate MIDI files ir = music_compile_to_ir(arrangement="my-track") # Extract just the bass layer bass_ir = music_modify_ir(ir_json=ir["score_ir"], filter_layers=["bass"]) music_emit_midi_from_ir(ir_json=bass_ir["score_ir"], output_name="bass-stem") # Extract drums, reduce velocity drums_ir = music_modify_ir(ir_json=ir["score_ir"], filter_layers=["drums"], velocity_scale=0.8) music_emit_midi_from_ir(ir_json=drums_ir["score_ir"], output_name="drums-stem") # Transpose harmony up an octave harmony_ir = music_modify_ir(ir_json=ir["score_ir"], filter_layers=["harmony"], transpose=12) music_emit_midi_from_ir(ir_json=harmony_ir["score_ir"], output_name="harmony-high")

MCP Tools

The server provides 37+ tools organized by domain:

Arrangement Tools (6):

  • music_create_arrangement - Create a new arrangement

  • music_get_arrangement - Get arrangement details

  • music_list_arrangements - List all arrangements

  • music_save_arrangement - Save to YAML

  • music_delete_arrangement - Delete an arrangement

  • music_duplicate_arrangement - Clone an arrangement

Structure Tools (11):

  • music_add_section, music_remove_section, music_reorder_sections

  • music_set_section_energy, music_add_layer, music_remove_layer

  • music_arrange_layer, music_mute_layer, music_solo_layer

  • music_set_layer_level, music_set_harmony

Pattern Tools (6):

  • music_list_patterns, music_describe_pattern

  • music_add_pattern, music_remove_pattern

  • music_update_pattern_params, music_copy_pattern_to_project

Style Tools (6):

  • music_list_styles, music_describe_style

  • music_suggest_patterns, music_validate_style

  • music_apply_style, music_copy_style_to_project

Compilation Tools (8):

  • music_compile_midi, music_preview_section

  • music_compile_to_ir, music_diff_ir

  • music_modify_ir, music_emit_midi_from_ir

  • music_export_yaml, music_validate

Humanization (Planned)

Determinism doesn't mean robotic. Humanization is seeded and explicit:

# In arrangement humanize: timing_ms: 8 # ±8ms timing drift velocity: 6 # ±6 velocity variation seed: 42 # Reproducible randomness

Same seed → same humanization → still deterministic. Change the seed to explore variations.

Development

# Clone and install git clone https://github.com/chuk-ai/chuk-mcp-music cd chuk-mcp-music pip install -e ".[dev]" # Run full check suite make check # Linting, types, security, tests (532 tests) # Run tests with coverage make test-cov # Currently at 89% coverage # Format code ruff format . ruff check --fix .

Project Structure

src/chuk_mcp_music/ ├── core/ # Music primitives (pitch, rhythm, chord, scale) ├── models/ # Pydantic models (arrangement, pattern, style) ├── arrangement/ # Arrangement management ├── patterns/ # Pattern system and library │ └── library/ # Built-in patterns (copy these!) ├── styles/ # Style system and library │ └── library/ # Built-in styles ├── compiler/ # Compilation pipeline │ ├── arranger.py # Arrangement → Score IR → MIDI │ ├── score_ir.py # Intermediate representation (versioned, diffable) │ └── midi.py # MIDI file generation ├── tools/ # MCP tool implementations └── async_server.py # MCP server entry point

Roadmap

See roadmap.md for the full design document.

Next up:

  • Export profiles (GM, Ableton, Logic drum maps)

  • CC automation lanes (filter sweeps, sidechain ducking)

  • Real-time preview via Web MIDI

  • Pattern learning from MIDI import

License

MIT

-
security - not tested
F
license - not found
-
quality - not tested

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/chrishayuk/chuk-mcp-music'

If you have feedback or need assistance with the MCP directory API, please join our Discord server