fmod-mcp
by jmperez127
README.md
# fmod-mcp
An MCP server that lets an AI coding agent drive **FMOD Studio**: import audio, create events, wire instruments, place effects on buses, set up sends/returns, manage parameters, and build banks. It does this by speaking to Studio's built-in JavaScript scripting terminal over TCP (port 3663).
**v0.2: 35 tools, 143 tests passing (106 unit + 37 live, plus 1 gated persist test).** See [CLAUDE.md](CLAUDE.md) for the tool catalog, [docs/effects.md](docs/effects.md) for the effect-parameter reference, [docs/troubleshooting.md](docs/troubleshooting.md) when something breaks, and [examples/](examples/) for worked walkthroughs.
Built for use with [Claude Code](https://claude.com/claude-code), but works with any MCP-compatible client.
## Requirements
- FMOD Studio 2.02+ (the scripting terminal has been stable across recent versions)
- Python 3.11+
- A running FMOD Studio instance with the target project open
## Install
```bash
git clone https://github.com/jmperez127/fmod-mcp.git
cd fmod-mcp
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
```
## Register with Claude Code
Run this from inside the cloned `fmod-mcp` directory (so `$(pwd)` resolves correctly):
```bash
claude mcp add fmod -s user -- "$(pwd)/.venv/bin/python" -m fmod_mcp
```
The `--` ends `claude mcp add`'s flag parsing so `-m fmod_mcp` is treated as
arguments to Python. `-s user` registers the server for every Claude Code
session on your machine; drop it for a project-local registration.
Alternatively, if you prefer a globally-callable shim, install via [pipx]:
```bash
pipx install .
claude mcp add fmod -s user fmod-mcp
```
[pipx]: https://pipx.pypa.io/
Verify:
```bash
claude mcp list
```
## Configure host/port (optional)
Defaults to `127.0.0.1:3663`. Override with environment variables:
```bash
export FMOD_MCP_HOST=127.0.0.1
export FMOD_MCP_PORT=3663
```
## Enable TCP scripting in Studio
FMOD Studio's scripting terminal listens on TCP/3663 by default. If it's disabled:
1. Open FMOD Studio
2. `Edit` → `Preferences` (or `FMOD Studio` → `Preferences` on macOS)
3. Look for `Scripting` / `Terminal` settings
4. Enable TCP listener, note the port
## Quick smoke test
With FMOD Studio running and a project open:
```
> ping
{ ok: true, version: { productVersion: 2, majorVersion: 2, minorVersion: 20, ... } }
> list_banks
[ { path: "bank:/Master", guid: "...", name: "Master" }, ... ]
```
## Worked examples
The [examples/](examples/) directory has prompt-style walkthroughs you can paste into a Claude Code session:
| File | Scenario |
|---|---|
| [add_one_sfx.md](examples/add_one_sfx.md) | Minimum pipeline: import → event → sound → save → build |
| [batch_import_sfx.md](examples/batch_import_sfx.md) | Loop the above over a folder of wavs |
| [add_reverb_to_bus.md](examples/add_reverb_to_bus.md) | Reverb return + global send (with parameter reference) |
| [sidechain_compression.md](examples/sidechain_compression.md) | Music-ducks-to-VO routing |
| [parameter_driven_volume.md](examples/parameter_driven_volume.md) | Local game parameter for an event |
| [cleanup_unused_audio.md](examples/cleanup_unused_audio.md) | Find and delete orphan audio in the bin |
## Files it writes
- `~/.cache/fmod-mcp/commands.log`: every JS snippet sent to Studio, timestamped by request ID. Audit trail + replay source.
## Testing
Unit tests run without Studio (mock-based):
```bash
pytest -q
# 106 passed, 38 skipped (live tests)
```
Live tests require FMOD Studio running with any project open:
```bash
FMOD_MCP_LIVE=1 pytest -q
# 143 passed, 1 skipped (persist-gated)
```
The persist-gated save+build verification additionally writes to disk; opt in explicitly:
```bash
FMOD_MCP_LIVE=1 FMOD_MCP_LIVE_PERSIST=1 pytest tests/live/test_save_build_live.py
```
Live tests use a per-test scratch folder under `event:/__mcp_test_scratch__/<hex>/` and clean up via `studio.project.deleteObject` on tear-down. A session-scoped sweep also runs at start AND end so an interrupted run doesn't leave orphans.
## When things break
See [docs/troubleshooting.md](docs/troubleshooting.md) for the common failure modes and their fixes. The short version:
1. Check `~/.cache/fmod-mcp/commands.log` for the exact JS that went out.
2. Paste it into Studio's own Scripting window (`Window` → `Scripting` in the Studio GUI) and iterate.
3. Once working, patch the corresponding JS template in `fmod_mcp/tools/*.py`.
For anything not covered by a named tool, use `run_js`:
```
> run_js return studio.project.model.Event.findInstances().length;
42
```
## Layout
```
fmod_mcp/
├── studio_client.py # TCP/3663 client. Single persistent connection, sentinel-framed IIFE protocol
├── server.py # FastMCP tool registration, stdio transport
├── __main__.py # python -m fmod_mcp
└── tools/
├── discovery.py # ping, list_banks, list_events, list_buses, get_event
├── audio.py # import_audio, delete_audio, move_audio
├── events.py # create_event, add_single_sound, add_multi_sound,
│ # set_event_property, assign_to_bank, assign_to_bus,
│ # delete_event, delete_folder, rename_event, move_event,
│ # add_local_parameter, add_global_parameter
├── effects.py # list_effect_types, add_effect, list_effects, get_effect,
│ # set_effect_param, remove_effect, bypass_effect
├── routing.py # add_return, remove_return, list_returns,
│ # add_send, list_sends, remove_send, set_send_level
├── project.py # save_project, build_banks, deploy_banks
└── escape.py # run_js
tests/
├── (mock-based unit tests)
└── live/ # opt-in via FMOD_MCP_LIVE=1; auto-skip when port 3663 isn't reachable
```
## License
[MIT](LICENSE) © 2026 jmperez127.
TDQS
B3.2/5.0
Scored across 21 tools
Disambiguation4/5
Most tools target distinct resources and actions. However, create_event already includes assignment to bank and bus, creating some overlap with assign_to_bank and assign_to_bus.
Naming Consistency5/5
All tools follow a consistent verb_noun pattern (e.g., add_effect, list_events, set_event_property). Prepositions in assign_to_bank are acceptable and consistent.
Tool Count4/5
21 tools is slightly high but still well-scoped for the FMOD Studio domain. Each tool serves a distinct purpose without excessive redundancy.
Completeness3/5
Core workflows like event creation, effect management, and building are covered. Notable gaps include no delete operations for events or sounds, and no update beyond property setting.