Mirelo MCP Server
# Mirelo MCP Server
A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server that wraps the Mirelo v2 HTTP API, exposing Mirelo's sound-effects generation, audio extension, inpainting, and account tooling to any MCP-compatible client (Claude Desktop, Kiro, and others).
The server speaks MCP over stdio and advertises 21 tools covering six generation families plus job polling, asset uploads, and account lookup.
## Features
- Six generation families, each available as a **sync generate**, **async submit**, and **preflight** tool:
- Text → SFX
- Video → SFX
- Extend audio (audio-only)
- Extend audio (with video)
- Inpaint audio (audio-only)
- Inpaint audio (with video)
- Async job polling (`mirelo_job_status`) for long-running submissions.
- Asset upload slots with optional direct upload of a local file to the presigned S3 URL.
- Account/quota lookup (`mirelo_get_account`).
- Eager config validation at startup, with a clear error and non-zero exit on invalid configuration.
- Starts and advertises every tool even without an API key — credential-requiring calls return a structured missing-credential error instead of failing at startup.
## Requirements
- Node.js `>=20`
## Installation
```bash
npm install
npm run build
```
`npm run build` compiles TypeScript to `dist/` and produces the executable entrypoint `dist/index.js` (with a `#!/usr/bin/env node` shebang, exposed as the `mirelo-mcp-server` bin).
## Configuration
The server reads three environment variables at startup:
| Variable | Required | Default | Notes |
| --- | --- | --- | --- |
| `MIRELO_API_KEY` | No | — | Mirelo API key (bearer token). If absent, the server still starts and advertises all tools; calls that need credentials return a missing-credential error. |
| `MIRELO_BASE_URL` | No | `https://api.mirelo.ai` | Must be an absolute `http`/`https` URL. Normalized (trailing slash stripped). An invalid value aborts startup. |
| `MIRELO_TIMEOUT_MS` | No | `60000` | Request timeout in ms. Must be an integer in `[1000, 600000]`. An out-of-range value aborts startup. |
Invalid `MIRELO_BASE_URL` or `MIRELO_TIMEOUT_MS` values cause a `ConfigError`, printed to stderr with a non-zero exit code.
## MCP client configuration
Add the server to your MCP client's config (e.g. `mcp.json`).
**Recommended — run via `npx`** (no clone or build; npx fetches the published package on demand):
```json
{
"mcpServers": {
"mirelo": {
"command": "npx",
"args": ["-y", "mirelo-mcp-server"],
"env": {
"MIRELO_API_KEY": "your-mirelo-api-key",
"MIRELO_BASE_URL": "https://api.mirelo.ai",
"MIRELO_TIMEOUT_MS": "60000"
}
}
}
}
```
**Global install** (installs the `mirelo-mcp-server` bin on `PATH`):
```bash
npm install -g mirelo-mcp-server
```
```json
{
"mcpServers": {
"mirelo": {
"command": "mirelo-mcp-server",
"env": {
"MIRELO_API_KEY": "your-mirelo-api-key"
}
}
}
}
```
**From source** (clone + build, then point at the built entrypoint):
```json
{
"mcpServers": {
"mirelo": {
"command": "node",
"args": ["/absolute/path/to/mirelo-mcp/dist/index.js"],
"env": {
"MIRELO_API_KEY": "your-mirelo-api-key"
}
}
}
}
```
## Tools
The server advertises 21 tools.
### Generation families
Each family provides three tools:
- `_generate` — synchronous generation; returns `result_urls` when the job completes inline.
- `_submit` — asynchronous submission; returns a `job_id` / `job_url` to poll.
- `_preflight` — validates inputs and reports what would be sent without calling the generation endpoint.
| Family | Tools | Notes |
| --- | --- | --- |
| Text → SFX | `mirelo_text_to_sfx_generate`, `mirelo_text_to_sfx_submit`, `mirelo_text_to_sfx_preflight` | Model versions v1.5 / v1.6. |
| Video → SFX | `mirelo_video_to_sfx_generate`, `mirelo_video_to_sfx_submit`, `mirelo_video_to_sfx_preflight` | Requires a `video` Input_Source and `duration_ms`. |
| Extend audio (audio-only) | `mirelo_extend_audio_generate`, `mirelo_extend_audio_submit`, `mirelo_extend_audio_preflight` | v1.6. |
| Extend audio (with video) | `mirelo_extend_audio_with_video_generate`, `mirelo_extend_audio_with_video_submit`, `mirelo_extend_audio_with_video_preflight` | v1.6. |
| Inpaint audio (audio-only) | `mirelo_inpaint_audio_generate`, `mirelo_inpaint_audio_submit`, `mirelo_inpaint_audio_preflight` | v1.6. |
| Inpaint audio (with video) | `mirelo_inpaint_audio_with_video_generate`, `mirelo_inpaint_audio_with_video_submit`, `mirelo_inpaint_audio_with_video_preflight` | v1.6. |
### Management tools
| Tool | Purpose |
| --- | --- |
| `mirelo_job_status` | Poll an async job by `job_id` (`GET /v2/jobs/{job_id}`). Reports `processing` → `succeeded` / `errored`. |
| `mirelo_create_asset_upload_slot` | Create an asset upload slot (`POST /v2/assets`) and optionally upload a local file to the returned presigned S3 URL. |
| `mirelo_get_account` | Fetch account details (`GET /v2/me`): `id`, `email`, `credits_available`, `overage_enabled`. |
### Input sources
Tools that take audio or video accept an `Input_Source` in one of two shapes:
```json
{ "type": "url", "audio_url": "https://..." }
```
```json
{ "type": "asset", "asset_id": "asset_..." }
```
(Use `video_url` for video sources.) To reference an uploaded file, create an asset with `mirelo_create_asset_upload_slot`, then pass `{ "type": "asset", "asset_id": "..." }`.
## Example prompts to test
These are natural-language prompts you can give an MCP client (Claude Desktop, Kiro, etc.) once the server is connected and `MIRELO_API_KEY` is set. Each is phrased so the client maps it to the right tool and parameters. Durations are in milliseconds and stay within each tool's valid range.
### Account and connectivity
- "Check my Mirelo account — what's my email and how many credits do I have left?"
- "Is overage enabled on my Mirelo account?"
### Text → SFX
- "Generate a 5-second sound effect of heavy rain on a tin roof." (sync generate, `duration_ms: 5000`)
- "Create the sound of a sword being drawn from a metal scabbard, about 2 seconds long, and give me 3 variations." (`duration_ms: 2000`, `num_samples: 3`)
- "Make a seamless 10-second looping campfire crackle." (sets `loop: true`, `duration_ms: 10000`)
- "Generate a distant thunder rumble using model v1.5." (`model_version: "v1.5"`)
- "Estimate the cost and time to generate a 30-second forest ambience before actually making it." (preflight)
- "Submit a long 45-second cinematic whoosh as a background job and give me the job id." (async submit)
### Video → SFX
- "Generate sound effects for this video and match them to its first 8 seconds: https://example.com/clip.mp4." (`video` url source, `duration_ms: 8000`)
- "Add foley to my video asset asset_123 starting at the 2-second mark, and return the result as a muxed video." (`start_offset_ms: 2000`, `output: "video"`)
- "Preflight the cost of scoring a 12-second video clip." (preflight, `duration_ms: 12000`)
- "Submit a video-to-SFX job for https://example.com/scene.mp4 covering 20 seconds and give me the job id to poll." (async submit)
### Extend audio (audio-only)
- "Take this audio https://example.com/loop.wav and extend it by 8 more seconds." (`append_duration_ms: 8000`)
- "Extend my audio asset asset_456 by 15 seconds and guide the new part with the prompt 'building to a crescendo'." (`append_duration_ms: 15000`, `prompt`)
- "Loop-extend this 3-second clip so it plays seamlessly, adding 5 seconds." (`loop: true`, `append_duration_ms: 5000`)
### Extend audio (with video)
- "I have a video at https://example.com/ad.mp4 and its audio at https://example.com/ad.wav — extend the audio by 6 seconds to match the video tail." (`append_duration_ms: 6000`)
- "Extend the soundtrack of my video asset by 30 seconds starting 4 seconds in." (`append_duration_ms: 30000`, `start_offset_ms: 4000`)
### Inpaint audio (audio-only)
- "In this audio https://example.com/take.wav, replace the section from 2s to 5s with a clean guitar strum." (`segment: { start_ms: 2000, end_ms: 5000 }`, `prompt`)
- "Fix my audio asset asset_789 by regenerating the 10000-14000ms region." (`segment: { start_ms: 10000, end_ms: 14000 }`)
- "Preflight replacing a 3-second segment (from 6s to 9s) of a clip." (preflight)
### Inpaint audio (with video)
- "For my video asset (video asset_v1, audio asset_a1), replace the audio between 3s and 7s to fix a glitch." (`segment: { start_ms: 3000, end_ms: 7000 }`)
### Assets
- "Create an upload slot for a WAV file and upload /Users/me/sfx/input.wav to it." (`content_type: "audio/wav"`, `file_path`)
- "Create an asset upload slot for content type video/mp4 and give me the presigned URL." (slot only, no local upload)
### Async job polling
- "Check the status of Mirelo job job_abc123." (`mirelo_job_status`)
- "Poll job job_abc123 and, once it's succeeded, give me the result URLs."
> Tip: the `_preflight` tools never generate audio or spend credits, so they're the safest way to smoke-test connectivity and your API key.
## Async job flow
1. Call a family's `_submit` tool to get a `job_id`.
2. Poll `mirelo_job_status` with that `job_id`.
3. When the status is `succeeded`, read the `result_urls` from the response.
Synchronous `_generate` tools skip this loop and return `result_urls` directly when the job finishes inline.
## Architecture
- Transport: stdio via the `@modelcontextprotocol/sdk` server. The SDK owns the handshake, version negotiation, and request dispatch.
- Startup sequence: load and validate config → read the API key once → build a single configured HTTP client → bind each registry tool's handler → register `ListTools` + `CallTool` and connect the stdio transport.
- The entrypoint (`src/index.ts`) exports `createServer`, `connectStdio`, and `main`, and is import-safe (importing it has no side effects) so it can be exercised by tests without spawning stdio.
- Tool definitions live in `src/tools/*` and are aggregated by `src/registry.ts`, which enforces the 21-tool invariant at load time.
## Development
```bash
npm test # run the test suite once (vitest)
npm run test:watch # watch mode
npm run build # type-check and emit dist/
```
The suite includes property-based tests using [fast-check](https://github.com/dubzzz/fast-check) alongside unit and integration tests.
## License
MIT
TDQS
Scored across 21 tools
Each tool targets a distinct operation: generation, preflight estimation, or asynchronous submission for specific audio tasks (extend, inpaint, text-to-sfx, video-to-sfx), with clear separation between audio-only and with-video variants. Additional tools for account, asset upload, and job polling are unique and non-overlapping.
All tools follow a consistent snake_case pattern with the 'mirelo_' prefix, using verb_noun combinations like 'extend_audio_generate' and 'job_status'. The naming is predictable and uniform across synchronous, preflight, and submit variants.
21 tools is slightly above the typical range, but each tool is justified for a comprehensive audio generation and editing service covering synchronous and asynchronous modes, preflight cost estimation, and support for multiple input types. The count is well-scoped and not excessive.
The tool set covers core generation and editing workflows (extend, inpaint, text-to-sfx, video-to-sfx), including cost estimates and async submission. Missing features include job cancellation, listing assets, and asset deletion, but these are minor gaps for the primary audio generation purpose.