MCP server¶
The Model Context Protocol server exposes dvr as typed tools for LLM agents — Claude, Cursor, or any other MCP-compatible client. LLMs call structured tools instead of parsing shell output.
The server uses the MCP Python SDK 2.x protocol models and low-level callback
API while preserving explicit JSON schemas for every dvr tool.
Install¶
The server uses stdio transport by default — clients spawn dvr mcp serve as a subprocess and speak MCP over stdin/stdout.
Configure a client (one command)¶
These commands write the absolute path to dvr into the client's MCP config, preserving any other settings already there. Restart the client and you'll see the dvr tools available.
Useful flags:
| Flag | Effect |
|---|---|
--name <name> |
Register under a name other than dvr (e.g. dvr-prod). |
--enable-eval |
Set DVR_MCP_ENABLE_EVAL=1 in the server's env so the eval tool is callable. |
--no-launch |
Pass --no-launch to dvr mcp serve so it never auto-launches Resolve. |
--dry-run |
Print the resulting config without writing it. |
--force |
Overwrite an existing entry of the same name. |
--config <path> |
Use a custom config file instead of the platform default. |
For any client that uses the standard mcpServers JSON shape, use the generic installer:
If you'd rather edit the config by hand:
Add to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS), %APPDATA%\Claude\claude_desktop_config.json (Windows), or ~/.config/Claude/claude_desktop_config.json (Linux):
Set the MCP server command to dvr mcp serve. Most clients accept the same JSON shape.
Inspect the available tools¶
Tool profiles and tool_search¶
dvr ships more than ninety tools. Listing all of them costs roughly 9,000
tokens of context in every request, most of it schemas for tools a given
session never calls.
By default the server lists a core profile — the operations a session
reaches for first — and leaves the rest to tool_search:
| Profile | Tools listed | Tool-list payload |
|---|---|---|
core (default) |
36 | ~13 KB |
full |
94 | ~38 KB |
Every tool stays callable regardless of profile. Narrowing the listing
narrows what the agent sees up front, never what the server can do. An agent
that needs something unlisted calls tool_search and gets the full schema
back, then calls the tool by name:
Set DVR_MCP_PROFILE=full to restore the pre-1.7 behavior of listing
everything up front.
Available tools¶
Setup and diagnostics (no Resolve required)¶
| Tool | Purpose |
|---|---|
version |
Return dvr / Python / platform version plus bundled MCP branding assets. |
doctor |
Diagnose the dvr → Resolve setup (paths, env, process running) plus bundled MCP branding assets. Pass probe=true to also try a live connection. |
reconnect |
Drop the cached Resolve connection and reconnect. |
schema |
Catalog of valid setting keys, codecs, properties (some topics are static). |
snapshot_list |
List on-disk snapshots, newest first. |
Live ops¶
| Tool | Purpose |
|---|---|
ping |
Verify connection. Returns version. |
inspect |
One-call snapshot of app + project + timeline. |
page_get / page_set |
Read or switch the current page. |
project_list / project_ensure / project_current / project_settings_get / project_save / project_delete |
Project ops. |
timeline_list / timeline_inspect / timeline_ensure / timeline_switch / timeline_rename / timeline_delete / timeline_clear |
Timeline ops. |
timeline_append |
Append media to explicit timeline tracks (track_index, record_frame, source in/out). Non-default tracks require explicit record_frame per item. |
marker_add |
Add a marker at a frame on a timeline. |
clip_where |
Filter timeline items by safe declarative fields (duration, name, track type). |
clip_set_properties / clip_transform / clip_crop / clip_reset / clip_capabilities |
Set documented static timeline-item controls and discover unsupported transition/keyframe capabilities without using eval. |
timeline_add_title |
Insert a (Fusion) title and style its text — string, font, style, size, color (hex/name/[r,g,b]), opacity, tracking, line spacing, position, and alignment. |
clip_set_text |
Re-style Text+ titles selected by safe filters; non-text clips are reported as skipped. |
timeline_create_subtitles |
Generate subtitles from a timeline's audio (Whisper, Studio) with language / chars-per-line / line-break / preset. |
project_generate_speech |
Text-to-speech to an audio clip, including custom voice, speed, variation, pitch, generation ID, filename, and timeline placement (Resolve 21+, Studio). |
media_inspect / media_bins / media_ls / media_import |
Media pool. |
media_scan |
Scan a filesystem folder for importable video/audio files, skipping hidden AppleDouble files by default. |
media_bin_ensure / media_bin_delete / media_move |
Create/delete nested bins and move media-pool clips without breaking timelines. Slash paths like Picture/Plates are accepted consistently. |
timeline_assemble |
Workflow tool: ensure a timeline, import media by path, and append every item in order — a rough cut in one call. |
color_inspect |
Node graph (labels, tools, per-node LUTs), grade versions, and color group for filtered clips. |
color_set_cdl |
Apply slope / offset / power / saturation to a node across a clip selection. |
color_node_lut |
Read or set the LUT on a color node. |
color_export_lut |
Export a clip's grade as a LUT (17 / 33 / 65 / vlt). |
color_versions |
List / add / load / delete / rename grade versions. |
color_copy_grades |
Copy one clip's grade onto the rest of a selection. |
color_reset |
Reset every color node on a selection. |
dctl_list / dctl_read / dctl_write / dctl_delete |
Manage .dctl files in Resolve's LUT directory. Source is validated before it is written. |
lut_list / lut_generate / lut_delete |
Manage LUT files, including generating a .cube from a transform expression. |
tool_search |
Find tools not listed under the current profile and return their schemas. |
render_queue / render_presets / render_formats / render_codecs |
Render config. |
render_submit / render_status / render_stop / render_clear |
Render control. |
render_wait |
Block until a job finishes (or fails / times out) and return its final status — prefer this over polling render_status. |
interchange_export |
Export EDL / AAF / FCPXML / OTIO / etc. |
diff_timelines / diff_to_spec |
Structured diffs. |
apply_spec |
Reconcile live state to a YAML/JSON spec. Supports dry_run, continue_on_error, transactional (snapshot + auto-rollback), and verify (read-back checks). |
spec_export |
Build a spec from live project state — adopt an existing project into spec-managed workflows. |
snapshot_save / snapshot_restore |
Capture/restore project state. |
lint |
Pre-flight validation. |
eval |
Restricted Python eval — no imports, no dunder access. Disabled unless DVR_MCP_ENABLE_EVAL=1. |
eval_unsafe |
Unrestricted Python eval, including host access. Disabled unless DVR_MCP_ENABLE_EVAL_UNSAFE=1. |
Each tool has an explicit JSON schema, so agents see exactly what arguments are accepted before they call.
The MCP surface intentionally exposes reusable editing primitives rather than
show-specific pipeline commands. Agents can combine media_scan,
media_import, media_bin_ensure, media_move, and timeline_append to build
custom ingest or assembly workflows while each step remains inspectable and
recoverable.
Resources — read state, don't guess it¶
Alongside tools, the server exposes live state as MCP resources (JSON):
| URI | Contents |
|---|---|
dvr://inspect |
One-call snapshot of Resolve, current project, current timeline. |
dvr://project/current |
Current project inspect. |
dvr://timeline/current |
Full current-timeline inspect: tracks, items, markers. |
dvr://media/bins |
The current project's bin tree. |
dvr://render/queue |
Jobs in the render queue. |
dvr://doctor |
Static setup diagnostics (no Resolve needed). |
dvr://schema/<topic> |
Static catalogs: settings, clip-properties, color-presets, export-formats. |
Clients that support resources can attach these to context instead of burning tool calls on state reads.
Errors are first-class¶
When a tool fails, the response carries the structured DvrError:
{
"error": {
"type": "TimelineError",
"message": "No timeline is currently loaded.",
"cause": "GetCurrentTimeline returned None.",
"fix": "Switch or create a timeline first.",
"state": {"project": "MyShow"}
}
}
Agents can branch on error.type and recover via the suggested fix. See Errors and diagnostics for the field shapes.
Connection caching¶
The MCP server connects to Resolve lazily on the first tool call that needs it, then reuses that connection for the rest of the session. Tools that don't need Resolve (version, doctor without probe=true, schema for static topics, snapshot_list) never trigger a connection — they're safe to call at startup.
If Resolve was relaunched or external scripting was just enabled, call reconnect to drop the stale handle.
The eval escape hatches¶
Two tiers, gated separately, both off by default.
eval runs a Python expression with r = Resolve(), project, timeline
and dvr bound. Imports and dunder attribute access are blocked, so the
expression cannot reach the filesystem, network, or subprocesses. It still
runs against a live Resolve, so it can change the project — the boundary is
against the host, not against Resolve.
dvr mcp install-claude --enable-eval # sets DVR_MCP_ENABLE_EVAL=1
# or: DVR_MCP_ENABLE_EVAL=1 dvr mcp serve
eval_unsafe runs unrestricted Python, with imports, filesystem, network and
subprocesses all reachable. Use it only when the expression genuinely needs
host access, and never on a shared or unattended machine:
Changed in 1.7.0
Before 1.7.0, eval described itself as allowing "No imports" while in
fact passing a plain dictionary to Python's eval, into which CPython
injects the full builtins — so __import__('subprocess') worked. The
restriction is now real, and the unrestricted behavior moved to the
separately gated eval_unsafe.
Designing prompts that work well¶
- Read before mutating. Have the agent call
inspectbefore deciding what to do. One read replaces a chain of getters. - Use idempotent tools.
project_ensureandtimeline_ensureare safer than create/load pairs in agent code paths. - Use
doctorwhen debugging. It returns instantly and tells you whether the scripting library is found, env vars are set, and Resolve is running — without trying a long connection. - Stream renders.
render_submitreturns a job ID;render_statuspolls it. The client can show progress to the user.