MCP server
Let Claude, Codex or any MCP client edit video.
@miraiclip/mcp serves one project file over MCP (stdio). An agent can read the project, apply commands, undo, look at rendered frames, and export. Every successful edit is saved to the file.
npx @miraiclip/mcp --project ./video.json --assets ./mediaThe project file is created if it doesn't exist. Previews and exports run in headless Chrome through @miraiclip/server-export.
Flags
| Flag | Default | What it does |
|---|---|---|
--project <path> | miraiclip-project.json | The project JSON file. Created if missing |
--assets <dir> | the project file's folder | Base folder for relative asset src paths |
--width <px> | 1920 | Composition width, for a new project file only |
--height <px> | 1080 | Composition height, for a new project file only |
--fps <n> | 30 | Frame rate, for a new project file only |
--browser <path> | MIRAICLIP_BROWSER, then installed Chrome | Chrome or Chromium binary for previews and exports |
--help | Print the flags to stderr and exit |
MP4 export needs Google Chrome. WebM works with any Chromium. See The browser.
Connect a client
Claude Desktop
Add the server to claude_desktop_config.json, then restart Claude Desktop. Use absolute paths.
{
"mcpServers": {
"miraiclip": {
"command": "npx",
"args": ["-y", "@miraiclip/mcp", "--project", "/path/to/video.json", "--assets", "/path/to/media"]
}
}
}Claude Code
claude mcp add miraiclip -- npx -y @miraiclip/mcp --project ./video.json --assets ./mediaEverything after -- is the server command.
Other clients
Any client that launches stdio MCP servers works the same way: command npx, arguments -y @miraiclip/mcp --project …. For example, Codex (~/.codex/config.toml):
[mcp_servers.miraiclip]
command = "npx"
args = ["-y", "@miraiclip/mcp", "--project", "/path/to/video.json"]Tools
| Tool | Input | What it does |
|---|---|---|
get_state | include_json? | The describeProject summary: settings, assets, tracks, clips, transitions |
list_commands | none | Every command type with a one-line description |
get_command_schema | type | One command's payload JSON Schema |
dispatch | type, payload | Apply one command |
apply_commands | commands, label? | Apply a batch as one transaction and one undo step |
undo / redo | none | Step the history. Returns the new summary |
preview_frame | timeUs, width?, height? | Render a frame and return it as a PNG image the model can see |
export | out, format, quality?, fps?, width?, height?, rangeStartUs?, rangeEndUs? | Encode to a file |
get_project_json | none | The full project document |
All times are integer microseconds. The tool descriptions tell the model so.
Errors
A failed dispatch or apply_commands changes nothing and returns isError: true. dispatch returns the failure from tryDispatch:
{
"kind": "rejected",
"commandType": "clip/move",
"code": "clip-not-found",
"message": "Command \"clip/move\" rejected (clip-not-found): no clip \"nope\""
}apply_commands returns the applyCommands result, { ok: false, applied: 0, failedIndex, error }, so the model knows which command to fix.
Previews
preview_frame renders through the export pipeline, so the frame matches the exported file. Without width and height, the longest side is capped at 768 px. The first call starts headless Chrome; later calls reuse it.
Export
| Input | Default | Notes |
|---|---|---|
out | required | Relative paths are resolved against the project file's folder |
format | required | "mp4" or "webm" |
quality | "standard" | "draft", "standard" or "high" |
fps, width, height | project settings | |
rangeStartUs, rangeEndUs | whole composition | Give both, or the range is ignored |
The file streams to disk. The tool returns { ok, filePath, bytesWritten }.
A typical session
get_state: read the composition and its ids.apply_commands: make the edit as one batch, for exampleasset/add,track/add,clip/add.preview_frameat the moments that matter, to check the result.undoif it's wrong, orexportwhen it's right.
Notes
| Topic | Detail |
|---|---|
| Saving | Each successful edit writes the project file (via a temporary file and rename) |
| Undo history | Kept in memory while the server runs. Restarting the server clears it; the file stays |
| Asset paths | Relative src values resolve against --assets. Use absolute paths or http(s): URLs otherwise |
| Custom clip kinds | Not rendered: previews and exports support built-in kinds only |
Embed it
The package exports the pieces for your own server. To run it over MCP yourself, also install @modelcontextprotocol/sdk.
import { ProjectSession, callTool, createMcpServer, listTools } from "@miraiclip/mcp";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const session = await ProjectSession.open({ projectPath: "./video.json", assetsDir: "./media" });
// As an MCP server
const server = createMcpServer(session);
await server.connect(new StdioServerTransport());
// Or call the tools directly
listTools(session).map((tool) => tool.name);
const result = await callTool(session, "get_state", {});| Export | What it does |
|---|---|
ProjectSession.open({ projectPath, assetsDir?, defaults?, browser? }) | Loads or creates the project file. session.project is a normal Project |
session.preview({ timeUs, width?, height? }) | A PNG as Uint8Array |
session.export(options) | Same options as the export tool, with range: { startUs, endUs } |
session.close() | Closes the browser |
createMcpServer(session, version?) | An MCP Server with the tools registered |
listTools(session) / callTool(session, name, args) | The tool list and dispatcher without MCP |