nobu
<p align="center">
<img src="assets/images/nobu.png" alt="nobu — chiptune / retro MIDI MCP" width="720">
</p>
# nobu
**Game-agnostic MCP toolkit for composing chiptune / retro MIDI and rendering game audio.**
[](LICENSE)
[](https://www.python.org/downloads/)
[](CHANGELOG.md)
[](CHANGELOG.md)
Plug nobu into **Cursor**, **Claude Desktop**, or **Kilo Code**. Ask an agent to compose a stage theme — get a real multi-track `.mid`, then render to `.ogg` / `.wav` for your engine.
nobu ships **no game-specific data**. Your project supplies tonic, mood, and biome params.
```
agent / examples ──► nobu MCP ──► assets/midi/{project}.mid
│
assets/soundfonts/*.sf2 ──┐ ▼
└─► render MCP / scripts ──► output/audio/{project}/wav|ogg/
```
---
## Agent-ready?
**Cursor / Claude Code / Kilo (opened on this repo):** yes — after clone the agent should read `AGENTS.md` / `CLAUDE.md`, run `python scripts/bootstrap.py --no-prompt`, reload MCP, and be ready. Humans can run `python scripts/bootstrap.py` without flags for an interactive optional-upgrade menu. Rules/skills ship in-repo (`.cursor/rules`, `.claude/skills`); MCP/Kilo configs are **generated locally** by bootstrap (templates: `.mcp.example.json`, `.kilo/kilo.example.jsonc`).
**Cursor caveat:** new project MCP servers start **disabled** (Cursor security). After bootstrap + reload, toggle **nobu** on once under Settings → MCP (or Customize → MCP). Stays on for that workspace afterward — we cannot force-enable from the repo.
**Claude Desktop:** almost — same bootstrap, but the agent must also merge the MCP snippet into the **global** `claude_desktop_config.json` (Desktop does not auto-load project `.mcp.json`).
**Not magic:** `python` must be on PATH; open the cloned folder; reload after bootstrap; on Cursor, enable `nobu` once if it appears disabled.
## Paste this to your agent
```text
Clone https://github.com/francescojr/nobu.git (sibling folder or into this workspace).
Read AGENTS.md (and CLAUDE.md if present) and run: python scripts/bootstrap.py --no-prompt
If this is a game project, also run: python scripts/bootstrap.py --integrate <this_project_root> --no-prompt
Reload MCP. In Cursor, enable server "nobu" if it is disabled (Settings → MCP).
Confirm tools: compose (start_project … export_midi) + Mega Drive (export_megadrive) + render (render_project / render_all_modes).
Then follow .claude/skills/game-music-producer/ — deliver MIDI, audio, and/or VGM when I ask.
```
Integrating into an existing game:
```text
Set up https://github.com/francescojr/nobu as our game-music MCP.
Clone it next to this repo, run python scripts/bootstrap.py --integrate <absolute path to this project> --no-prompt,
reload MCP, and confirm tools work. Do not hardcode our game data into nobu.
```
Agents follow [AGENTS.md](AGENTS.md). Bootstrap rewrites project-local `.cursor/mcp.json` + `.mcp.json` + `.kilo/kilo.jsonc` to the local venv (never global Cursor MCP).
---
## Fastest path to sound
**Human (interactive menu for optional upgrades):**
```bash
git clone https://github.com/francescojr/nobu.git
cd nobu
python scripts/bootstrap.py
# → optional menu for tinysoundfont / FluidSynth / ffmpeg hints
# → reload Cursor/Kilo → enable nobu once → ask your agent for music
```
**Agent / CI (no prompts):**
```bash
python scripts/bootstrap.py --no-prompt
```
**Prove audio works (no MCP):**
```bash
python scripts/bootstrap.py --no-prompt --smoke
```
**Optional upgrades later:**
```bash
python scripts/bootstrap.py --with-render --no-prompt # hybrid drums (pip)
python scripts/bootstrap.py --doctor # re-check deps
# SF2: copy your .sf2 → assets/soundfonts/default.sf2 (see assets/soundfonts/README.md)
# Kilo: Settings → MCP → nobu → Network Timeout → 5 min
```
### Works out of the box vs optional
| After bootstrap | Works? |
|---|---|
| Compose + render **chip** via MCP | Yes (use `render_chip`; ~sub-second render) |
| Render **hybrid** | Needs `tinysoundfont` + your `.sf2` |
| Render **sf2** | Needs FluidSynth CLI + your `.sf2` |
| OGG on Windows | `ffmpeg` recommended (WAV always works) |
---
## Quick start (manual)
```bash
git clone https://github.com/francescojr/nobu.git
cd nobu
python scripts/bootstrap.py
# creates .venv, installs deps, writes .cursor/mcp.json, optional upgrade menu
```
Then reload MCP. In **Cursor**: Settings → MCP → enable **nobu** once if disabled. Full notes: [SETUP.md](.claude/skills/game-music-producer/SETUP.md).
### Demo without MCP
```bash
python examples/demo_biome_ost.py
python scripts/render_midi.py --mode chip # always works, no SF2 needed
# → assets/midi/*.mid and output/audio/{project}/wav|ogg/
```
### Render modes (chip / hybrid / full SF2)
| Mode | What you get | Requirements |
|---|---|---|
| `chip` | Pure chiptune | None (default path without SF2) |
| `hybrid` | SF2 drums + chiptune melodic | `.sf2` + `tinysoundfont` (else → chip) |
| `sf2` | Full SoundFont (all instruments) | `.sf2` + FluidSynth CLI (else → chip) |
| `auto` | Best available | Never fails — degrades to chip |
Full **SF2** always does FluidSynth → **WAV**, then converts to **OGG** (prefer `ffmpeg` on Windows). FluidSynth is not asked to write OGG directly.
```bash
python scripts/render_midi.py --mode chip
python scripts/render_midi.py --mode sf2 --soundfont assets/soundfonts/default.sf2
python scripts/render_track.py assets/midi/biome1_calm.mid --mode hybrid
```
---
## Folder layout
| Path | Purpose |
|---|---|
| `nobu_mcp.py` | MCP server entrypoint (`FastMCP("nobu")`) |
| `AGENTS.md` | Checklist agents run after clone |
| `scripts/bootstrap.py` | One-shot setup (venv, deps, Cursor `.mcp.json` + Kilo `.kilo/kilo.jsonc`) |
| `.kilo/` | Kilo Code v7+ config (`kilo.example.jsonc`, `rules/nobu.md`) |
| `assets/midi/` | Generated / authored `.mid` files |
| `assets/soundfonts/` | Your `.sf2` files (not vendored — see README there) |
| `output/audio/` | Rendered audio root — `{project}/wav/` and `{project}/ogg/` per track |
| `nobu_render.py` | Shared render library (CLI + MCP) |
| `scripts/render_midi.py` | Batch MIDI → audio (`chip` / `hybrid` / `sf2` / `auto`) |
| `scripts/render_track.py` | Single-file render (same modes; SF2 → WAV → OGG) |
| `examples/demo_biome_ost.py` | Generic 4-biome × calm/combat demo |
| `.claude/skills/game-music-producer/` | Agent skill + theory references |
**Env overrides:** `NOBU_MIDI_DIR`, `NOBU_OUTPUT_DIR`, `NOBU_SF2` (also `FLUID_SYNTH_SF2`).
---
## MCP tools
| Tool | Role |
|---|---|
| `start_project` | Create an empty multi-layer project |
| `suggest_scale_for_mood` | Map scene mood → scale + pitch palette |
| `generate_scale` | Build a scale from tonic + type |
| `add_layer` | Add melody / harmony / bass / drums (string drum names OK) |
| `set_tempo_change` | Schedule BPM changes (horizontal resequencing) |
| `list_layers` | Inspect layers before export |
| `export_midi` | Write `assets/midi/{project}.mid` + loop metadata |
| `get_megadrive_capabilities` | Builtin FM patches / optional BYO PCM readiness |
| `export_megadrive` | MIDI → Mega Drive `.vgm` for SGDK |
| `render_project` | Render one mode: chip / hybrid / sf2 / auto |
| `render_chip` | Pure chiptune render shortcut |
| `render_hybrid` | SF2 drums + chip melodic shortcut |
| `render_sf2` | Full SoundFont render shortcut |
| `render_all_modes` | chip + hybrid + sf2 in one call |
| `get_render_capabilities` | FluidSynth / SF2 / ffmpeg health check |
| `list_soundfonts` | Discover `.sf2` files |
Audio output: `output/audio/{project}/wav/` and `.../ogg/`. Mega Drive: `.../vgm/`.
Mood keys: `safe_exploration`, `heroic_exploration`, `nostalgic_town`, `melancholic_dungeon`, `hostile_exotic_biome`, `magical_dreamlike`, `combat`, `victory`, `classic_retro`.
---
## SoundFonts
Place **your own** `.sf2` at:
```
assets/soundfonts/default.sf2
```
Or set `NOBU_SF2=/path/to/yours.sf2`. Re-check: `python scripts/bootstrap.py --doctor` or MCP `get_render_capabilities`.
Without a soundfont, chip render **always works** via the built-in NES-style synth. Hybrid/sf2 modes fall back to chip until you add a soundfont.
---
## Mega Drive / VGM (SGDK)
Export a console-ready VGM after compose:
```bash
python scripts/export_megadrive.py assets/midi/my_track.mid --json
# → output/audio/my_track/vgm/my_track.vgm
```
MCP: `get_megadrive_capabilities` → `export_megadrive("my_track")`.
Builtin FM patches + **PSG noise drums** always work. Optional BYO patches/PCM live under `assets/megadrive/` (not shipped — same policy as SoundFonts). See [assets/megadrive/README.md](assets/megadrive/README.md). Point your SGDK `.res` at the VGM (`XGM name "file.vgm"`); rescomp runs xgmtool in the game project.
---
## Agent skill
Copy or open this repo so Cursor / Claude can load:
`.claude/skills/game-music-producer/`
When the **nobu** MCP is connected, the skill instructs the agent to call the tools and deliver real MIDI **and audio** — not text-only sketches.
---
## Versioning
nobu follows [Semantic Versioning](https://semver.org/) (`MAJOR.MINOR.PATCH`).
See [CHANGELOG.md](CHANGELOG.md) ([Keep a Changelog](https://keepachangelog.com/) categories).
There is **no `[Unreleased]` section** — Cursor session hooks cut a new version
whenever a conversation ends with meaningful changes.
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).
## License
[MIT](LICENSE) — Copyright (c) 2026 nobu contributors.
---
---
# nobu (Português)
**Toolkit MCP game-agnostic para compor MIDI chiptune/retrô e renderizar áudio de jogo.**
## Cole isso no seu agente
```text
Clone https://github.com/francescojr/nobu.git.
Leia nobu/AGENTS.md e rode: python scripts/bootstrap.py --no-prompt
Se for um projeto de jogo, rode também: python scripts/bootstrap.py --integrate <raiz_deste_projeto> --no-prompt
Recarregue o MCP, confirme o server "nobu" (16 tools: compose + Mega Drive + render).
Siga .claude/skills/game-music-producer/ — compose, render e/ou VGM quando eu pedir.
```
## Início rápido (manual)
```bash
git clone https://github.com/francescojr/nobu.git
cd nobu
python scripts/bootstrap.py # humano: menu interativo de optionals
python scripts/bootstrap.py --no-prompt --smoke # agente: prova chip sem MCP
python examples/demo_biome_ost.py
python scripts/render_midi.py --mode chip # funciona sem SF2
```
Modos: `chip` (puro) · `hybrid` (SF2 drums + chip) · `sf2` (SoundFont completo).
Sem SF2/FluidSynth → fallback automático para chiptune (não quebra).
SF2: FluidSynth → WAV → OGG (`ffmpeg` recomendado no Windows).
Detalhes: [AGENTS.md](AGENTS.md) · [SETUP.md](.claude/skills/game-music-producer/SETUP.md).
## Pastas
| Path | Uso |
|---|---|
| `assets/midi/` | Arquivos `.mid` |
| `assets/soundfonts/` | Seus `.sf2` (não distribuídos no repo) |
| `assets/megadrive/` | BYO patches/PCM opcionais (não distribuídos) |
| `output/audio/` | `{project}/wav/`, `ogg/`, e `vgm/` |
## Tools MCP (inglês)
Compose: `start_project` → `suggest_scale_for_mood` → `add_layer` → `list_layers` → `export_midi`
Mega Drive: `get_megadrive_capabilities` → `export_megadrive`
Render: `render_project` / `render_chip` / `render_hybrid` / `render_sf2` / `render_all_modes`
## Licença
[MIT](LICENSE).
TDQS
Scored across 7 tools
The tools have distinct purposes, but suggest_scale_for_mood and generate_scale both deal with musical scales and could be confused if descriptions are not read carefully. However, the descriptions clarify their different use cases (mood-based vs. arbitrary scale generation).
All tool names follow a consistent verb_noun pattern in snake_case (e.g., list_layers, start_project, generate_scale). No deviations or mixing of styles.
With 7 tools, the server covers the core workflow of creating a game track: starting a project, selecting scales, adding layers, adjusting tempo, listing layers, and exporting. The count feels well-scoped for the domain.
The tool set covers the essential operations for composing a multi-layer MIDI track. Minor gaps exist, such as no tool to modify or delete a layer after addition, and no explicit tool to set initial tempo (only tempo changes). These are not critical for basic usage.