MCP setup and a tool call

ferraus mcp serve exposes every command as a Model Context Protocol tool, so an AI client (Claude Code, Claude Desktop, Cursor, or any MCP client) can edit audio without a shell. This page sets it up and shows a complete session as it ran against ferraus 0.1.0. Background: MCP concepts.

Set it up

Claude Code:

claude mcp add ferraus -- ferraus mcp serve --allow ~/music

Any client that reads an mcpServers config (Claude Desktop's claude_desktop_config.json, Cursor's mcp.json, ...):

{"mcpServers": {"ferraus": {"command": "ferraus", "args": ["mcp", "serve", "--allow", "/Users/me/music"]}}}

--allow is the sandbox: every input and output must be inside an allowed directory (repeat the flag for more; the default is the current directory). Use an absolute path in JSON configs, since ~ is not expanded there.

A session

The server speaks JSON-RPC 2.0 over stdio, one message per line. This is the exchange a client performs, trimmed:

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"docs-test","version":"0"}}}
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18",
 "capabilities":{"tools":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"prompts":{"listChanged":false},"logging":{}},
 "serverInfo":{"name":"ferraus","title":"ferraus audio toolkit","version":"0.1.0"},
 "instructions":"ferraus edits, repairs, separates, tunes, transcribes, and masters audio. Every tool returns the ferraus JSON envelope ..."}}

tools/list returns 175 tools: one per command (ferraus_<command>, spaces become underscores) plus ferraus_job_start, ferraus_job_status, ferraus_job_result, and ferraus_job_cancel. Each tool's inputSchema is the command's argument schema (ferraus schema <command>):

{"name": "ferraus_fx", "title": "ferraus fx",
 "description": "Apply a chain of effects in order in one pass\n\n...\n\nCLI equivalent: ferraus fx take.wav -o clean.wav --fx \"denoise:auto\" ...",
 "inputSchema": {"type": "object", "required": ["inputs"], "additionalProperties": false,
                 "properties": {"inputs": {"type": "array", "items": {"type": "string"}, "minItems": 1, ...},
                                "output": {"type": "string", ...}, "fx": {...}, ...}}}

A tool call: arguments use the flag names with _ for -, inputs go in inputs, the output path in output.

{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"ferraus_normalize",
 "arguments":{"inputs":["episodes/ep02.wav"],"lufs":"-16","output":"ep02.mcp.wav"}}}
{"jsonrpc":"2.0","id":4,"result":{
 "content":[{"type":"text","text":"{\"ok\":true,\"command\":\"normalize\", ...}"}],
 "structuredContent":{"ok":true,"command":"normalize",
   "inputs":[{"path":"/Users/me/music/episodes/ep02.wav","duration_s":13.5,"sample_rate":48000,"channels":1}],
   "outputs":[{"path":"/Users/me/music/ep02.mcp.wav","duration_s":13.5}],
   "params":{"lufs":"-16","ceiling":"-1dBTP","true_peak_limit":"on","mode":"lufs","target_lufs":-16.0, ...},
   "metrics":{"before":{"integrated_lufs":-22.43,"true_peak_dbtp":-7.88},"gain_db":6.43,
              "after":{"integrated_lufs":-16.0,"true_peak_dbtp":-1.45}}}}}

structuredContent is the same envelope --json prints on the command line; content[0].text carries it as text for clients that do not read structured content. Relative paths resolve against the first --allow directory. When a call names no output, results go to ferraus-out/ inside it.

List values can be JSON arrays: {"inputs": ["vox.wav", "beat.wav"], "gains": ["0dB", "-6dB"]} is the same as --gains 0dB,-6dB.

A path outside the sandbox is refused, and the error is a normal envelope with "isError": true:

{"jsonrpc":"2.0","id":5,"result":{"isError":true,"structuredContent":{"ok":false,"command":"gain",
 "error":{"code":"path_not_allowed","message":"/etc/hosts is outside the allowed directories",
          "hint":"allowed: /Users/me/music","exit_code":2}}, ...}}

Long jobs

Separation, mastering, and batches can take minutes. Start them as jobs:

{"jsonrpc":"2.0","id":8,"method":"tools/call","params":{"name":"ferraus_job_start",
 "arguments":{"tool":"ferraus_separate","arguments":{"inputs":["song.wav"],"tier":"fast"}}}}

The result's structuredContent is {"job_id": "job-1", "state": "running"}; poll ferraus_job_status with {"job_id": "job-1"}, then fetch ferraus_job_result, or stop it with ferraus_job_cancel. Jobs live as long as the server process. Calls that carry an MCP progress token also receive notifications/progress.

Resources and prompts

resources/list returns ferraus://catalog, ferraus://presets, ferraus://models, ferraus://plugins, and ferraus://sounds; the templates ferraus://docs/{command}, ferraus://schema/{command}, ferraus://presets/{effect}, and ferraus://jobs/{id} read one item. prompts/list returns clean-and-tune-vocal, extract-stems-for-remix, hum-to-instrument, and master-song, which walk a model through the recipes in this cookbook.

HTTP instead of stdio

ferraus mcp serve --http 127.0.0.1:8765 --allow ~/music

Clients POST JSON-RPC to http://127.0.0.1:8765/mcp. Requests with a browser Origin other than localhost are refused.