davinci-resolve-mcp 2.99.3 → 2.101.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/CHANGELOG.md +127 -0
- package/README.md +5 -4
- package/README.zh-CN.md +5 -5
- package/docs/SKILL.md +27 -1
- package/docs/contributing.md +1 -1
- package/docs/guides/color-decision-guide.md +30 -0
- package/docs/install.md +1 -1
- package/docs/kernels/color-grade-kernel.md +22 -0
- package/docs/reference/api-coverage.md +2 -2
- package/install.py +1 -1
- package/package.json +1 -1
- package/src/granular/common.py +1 -1
- package/src/server.py +172 -4
- package/src/utils/cube_lut.py +270 -0
- package/src/utils/grade_loop.py +356 -0
- package/src/utils/knowledge.py +594 -0
package/AGENTS.md
CHANGED
|
@@ -66,6 +66,10 @@ semantics.
|
|
|
66
66
|
- Public overview, current stats, and docs map: `README.md`
|
|
67
67
|
- Historical release notes: `CHANGELOG.md`
|
|
68
68
|
- AI assistant operating reference: `docs/SKILL.md`
|
|
69
|
+
- Craft guidance for non-Claude-Code clients: the `knowledge` tool serves
|
|
70
|
+
`.claude/skills/`, `docs/guides/`, and `docs/kernels/` as resolved prose over
|
|
71
|
+
MCP. When you add a skill, guide, or kernel it is indexed automatically — a
|
|
72
|
+
drift guard fails the suite if anything in those directories is unreachable
|
|
69
73
|
- Release checklist and validation rules: `docs/process/release-process.md`
|
|
70
74
|
- Kernel workflow support maps: `docs/kernels/`
|
|
71
75
|
- API coverage and live-test status: `docs/reference/api-coverage.md`
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,128 @@
|
|
|
2
2
|
|
|
3
3
|
Release history for the DaVinci Resolve MCP Server. The latest release is summarized in the root README; older entries live here to keep the README focused.
|
|
4
4
|
|
|
5
|
+
## What's New in v2.101.0
|
|
6
|
+
|
|
7
|
+
**A grade can now reject itself.** `assess_grade` has measured grade damage since
|
|
8
|
+
v2.68.0 — banding in a sky, highlight levels collapsing, shadow grain amplified into
|
|
9
|
+
noise — and every flag it raises carries a remedy. Nothing consumed that report. The
|
|
10
|
+
measurement existed; the loop did not, so the remedy "reduce the strength" was advice an
|
|
11
|
+
agent had no way to act on.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **`media_analysis grade_loop`** — the retry ladder. Applies a look LUT, measures the
|
|
16
|
+
real decoded frame, and on any flag retries with the same look attenuated toward
|
|
17
|
+
identity (strength x 0.8 per rung, floored at 0.5, three tries by default). The first
|
|
18
|
+
strength that clears every sampled frame wins.
|
|
19
|
+
- **A flagged result is never reported acceptable.** An exhausted ladder returns
|
|
20
|
+
`needs_human` with the best attempt and its remaining flags — never a quiet success
|
|
21
|
+
at a strength that still bands.
|
|
22
|
+
- **Every sampled frame must pass.** `times=[...]` samples several timestamps and the
|
|
23
|
+
report names the one that failed; a grade clean on the frame you happened to check
|
|
24
|
+
is not a grade that passed.
|
|
25
|
+
- **The best attempt is the gentlest.** When nothing converges, attempts rank by flag
|
|
26
|
+
count with ties broken by the smallest colour shift — equal damage means taking the
|
|
27
|
+
one a human has less to undo.
|
|
28
|
+
- **It does not touch the project.** The result is an apply manifest with
|
|
29
|
+
`safe_to_apply`, and a flagged result carries the reason it is blocked.
|
|
30
|
+
- `dry_run` defaults to true and reports the ffmpeg decode budget before anyone
|
|
31
|
+
commits to it. `cost_tier` defaults to `numeric`, because escalating every rung to
|
|
32
|
+
vision would spend host turns on attempts that exist to be rejected.
|
|
33
|
+
- **`media_analysis grade_loop_capabilities`** — dependency state, ladder constants, and
|
|
34
|
+
an explicit statement of which modes exist.
|
|
35
|
+
- **`src/utils/cube_lut.py`** — read, write, and attenuate 3D `.cube` LUTs. Attenuation
|
|
36
|
+
is a blend toward identity, the same operation a LUT mix control performs. Exact at
|
|
37
|
+
both endpoints: strength 1.0 returns the table unchanged and 0.0 returns true
|
|
38
|
+
identity. 1D LUTs are refused by name, and attenuation on a non-unit
|
|
39
|
+
`DOMAIN_MIN`/`DOMAIN_MAX` is refused because identity is only identity on 0..1.
|
|
40
|
+
|
|
41
|
+
### Not built, and said so
|
|
42
|
+
|
|
43
|
+
The in-loop **live** mode — apply in Resolve, render a frame, assess, repeat — is not
|
|
44
|
+
implemented. It needs a single-frame render per rung, and shipping it unvalidated would
|
|
45
|
+
put a "verified live" claim behind something no runnable command has produced.
|
|
46
|
+
`grade_loop_capabilities()` says this in the response rather than only in the docs. The
|
|
47
|
+
offline LUT ladder is complete and validated.
|
|
48
|
+
|
|
49
|
+
### Documentation
|
|
50
|
+
|
|
51
|
+
- `docs/guides/color-decision-guide.md` — a new "Rejecting Your Own Grade" section on
|
|
52
|
+
when measurement beats eyeballing a compressed preview.
|
|
53
|
+
- `docs/kernels/color-grade-kernel.md` — the numeric grade-QC actions and their
|
|
54
|
+
display-referred-only contract.
|
|
55
|
+
|
|
56
|
+
### Validation
|
|
57
|
+
|
|
58
|
+
- Offline suite: 2889 passed, 1 skipped, 711 subtests, 0 failures.
|
|
59
|
+
- End-to-end through real ffmpeg on generated media: a look that converges only after
|
|
60
|
+
backing off, and one that never converges and says so.
|
|
61
|
+
- Two deliberate mutations — `acceptable` hard-coded true, and a rung passing on its
|
|
62
|
+
first clean frame — were each caught by the new tests.
|
|
63
|
+
- No Resolve behavior changed; live test not required. A test asserts no Resolve
|
|
64
|
+
connection is attempted.
|
|
65
|
+
|
|
66
|
+
## What's New in v2.100.0
|
|
67
|
+
|
|
68
|
+
**The craft guidance is now readable by any MCP client.** This repository carries a
|
|
69
|
+
real body of editorial, colour, and audio guidance — how to tighten a take without
|
|
70
|
+
cutting the breath out of it, what frames to look at before applying a grade, which
|
|
71
|
+
API calls silently lie. It lived in `.claude/skills/`, `docs/guides/`, and
|
|
72
|
+
`docs/kernels/`, and it was reachable only by an agent with this checkout on disk.
|
|
73
|
+
|
|
74
|
+
Over MCP there is no checkout. A skill that says "open
|
|
75
|
+
`docs/guides/color-decision-guide.md`" is a dead end on Codex, Cursor, or a bare SDK
|
|
76
|
+
loop: the pointer resolves to nothing, and the agent operates the tools without ever
|
|
77
|
+
seeing the reasoning that makes the operation correct.
|
|
78
|
+
|
|
79
|
+
### Added
|
|
80
|
+
|
|
81
|
+
- **`knowledge` tool** (36th compound tool) — the corpus served as prose, with no
|
|
82
|
+
Resolve connection involved:
|
|
83
|
+
- `topics(category?)` — the index: id, summary, size, sections, related topics.
|
|
84
|
+
35 topics across `workflow`, `guide`, `kernel`, `reference`, and `repo`.
|
|
85
|
+
- `get(topic, section?, inline?)` — resolved prose. Natural aliases (`"tighten"`,
|
|
86
|
+
`"dead air"`, `"grading"`) resolve to real topics, and referenced guides and
|
|
87
|
+
kernels arrive **inlined**, so what comes back is the manual rather than a path
|
|
88
|
+
to it. `section` returns one heading's subtree.
|
|
89
|
+
- `search(query, limit?)` — ranked topics with excerpts.
|
|
90
|
+
- `capabilities()` — topic counts by category and the corpus directories.
|
|
91
|
+
- **`knowledge://topics` MCP resource** — the same index, so hosts that consume
|
|
92
|
+
resources can see what guidance exists without spending a turn on it.
|
|
93
|
+
- `setup(action="schema")` now names the guidance, because an agent's orientation
|
|
94
|
+
call is where it will actually be noticed.
|
|
95
|
+
|
|
96
|
+
### Design notes
|
|
97
|
+
|
|
98
|
+
- **Inlining stops at one level.** Following references transitively would turn a
|
|
99
|
+
150-line answer into the whole `docs/` tree.
|
|
100
|
+
- **Oversized references are summarised, not truncated.** Over the inline budget an
|
|
101
|
+
agent gets the title, summary, section list, and the topic id to fetch — a
|
|
102
|
+
truncated prefix is the first N lines, which is rarely the part that answers the
|
|
103
|
+
question.
|
|
104
|
+
- **`reference` topics are terminal.** The 2250-line operating reference and the
|
|
105
|
+
generated API ledgers cross-link each other freely; inlining from them doubles a
|
|
106
|
+
document that was already complete.
|
|
107
|
+
- **An unknown section is an error that lists the real ones**, never a quiet return
|
|
108
|
+
of the whole document.
|
|
109
|
+
- **Search matches whole words.** Substring counting ranked `resolve-audio` top for
|
|
110
|
+
"dead air", because "air" is inside "F-air-light". Body hits are also normalised by
|
|
111
|
+
document length, so the longest document cannot win on mass alone.
|
|
112
|
+
|
|
113
|
+
### Guarded against drift
|
|
114
|
+
|
|
115
|
+
A test asserts every skill, guide, and kernel in the corpus reaches the index, and
|
|
116
|
+
that every alias points at a topic that exists. Knowledge added to this repository
|
|
117
|
+
later cannot go silently unserved — the failure mode a hand-kept list has every time.
|
|
118
|
+
|
|
119
|
+
### Validation
|
|
120
|
+
|
|
121
|
+
- Offline suite: 2849 passed, 1 skipped, 711 subtests, 0 failures.
|
|
122
|
+
- Three deliberate mutations (substring search, inlining disabled, unknown section
|
|
123
|
+
returning the whole document) were each caught by the new tests.
|
|
124
|
+
- No Resolve behavior changed; live test not required. A test asserts the tool never
|
|
125
|
+
reaches for a Resolve connection.
|
|
126
|
+
|
|
5
127
|
## What's New in v2.99.3
|
|
6
128
|
|
|
7
129
|
**Fusion authoring now works on the free edition.** v2.99.2 documented that
|
|
@@ -58,6 +180,11 @@ render -> PSNR 23.32 dB vs baseline — RENDERED
|
|
|
58
180
|
|
|
59
181
|
## What's New in v2.99.2
|
|
60
182
|
|
|
183
|
+
> **Cause corrected in v2.99.3.** The "`fusion_comp add_tool` cannot run on the
|
|
184
|
+
> free edition" note below has the wrong cause: `GetAttrs`/`SetAttrs` are present
|
|
185
|
+
> on a Fusion tool and work when called — the bridge client reported them absent
|
|
186
|
+
> because `dir()` on a Fusion Tool omits them. `add_tool` needed no change.
|
|
187
|
+
|
|
61
188
|
**The Fusion comp-lock question is closed on Resolve 21.** The v2.98.5–v2.98.8
|
|
62
189
|
work isolated the bug on Studio 19.1.3.7 only, and the open caveat was whether
|
|
63
190
|
the "renders on 19.1.3.7, ignored on 21.0.4.5" split reported in
|
package/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
English | [简体中文](README.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
[](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
|
|
6
6
|
[](https://www.npmjs.com/package/davinci-resolve-mcp)
|
|
7
7
|
[](docs/reference/api-coverage.md)
|
|
8
|
-
[-blue.svg)](#server-modes)
|
|
9
9
|
[-18%20tools-blueviolet.svg)](#server-modes)
|
|
10
10
|
[](docs/reference/api-coverage.md#test-results)
|
|
11
11
|
[](https://www.blackmagicdesign.com/products/davinciresolve)
|
|
@@ -109,7 +109,7 @@ The command starts a loopback-only server and opens the control panel in your br
|
|
|
109
109
|
|
|
110
110
|
| Mode | Entry point | Tools | Best for |
|
|
111
111
|
|------|-------------|-------|----------|
|
|
112
|
-
| Compound | `src/server.py` |
|
|
112
|
+
| Compound | `src/server.py` | 36 | Default mode for most assistants. Related Resolve operations are grouped behind action parameters to keep context usage low. |
|
|
113
113
|
| Full / granular | `src/server.py --full` or `src/resolve_mcp_server.py` | 353 | Power users who want one MCP tool per Resolve API method. |
|
|
114
114
|
|
|
115
115
|
The compound server is recommended unless you specifically need the granular one-tool-per-method surface.
|
|
@@ -218,6 +218,7 @@ The open-source servers are complete and fully functional on their own.
|
|
|
218
218
|
| Audio and Fairlight | Track/item probes, source mapping, guarded audio property writes, voice isolation, auto-sync planning, transcription/subtitle probes |
|
|
219
219
|
| Render and deliver | Format/codec matrix probing, render settings validation, queued job lifecycle checks, guarded Quick Export |
|
|
220
220
|
| Extension authoring | Fuse, DCTL, ACES DCTL, and Resolve-page Lua/Python script lifecycle helpers with safe MCP-marked install/remove |
|
|
221
|
+
| Craft guidance | The bundled editorial, colour, audio, and workflow guidance served as prose over MCP — indexed, searchable, and readable by any client, not just ones with this repository on disk |
|
|
221
222
|
|
|
222
223
|
## Optional Extras
|
|
223
224
|
|
|
@@ -272,7 +273,7 @@ The default server is a local stdio process launched by your MCP client; it does
|
|
|
272
273
|
|
|
273
274
|
| Metric | Value |
|
|
274
275
|
|--------|-------|
|
|
275
|
-
| MCP Tools | **
|
|
276
|
+
| MCP Tools | **36** compound / **353** granular (live server) |
|
|
276
277
|
| Advanced (offline) tools | **18** — .drp/.drt/.drx + DB authoring, no Resolve running |
|
|
277
278
|
| Kernel Actions | **136** guarded workflow actions across 9 compound tools |
|
|
278
279
|
| API Methods Covered | **361/361** (100%) |
|
package/README.zh-CN.md
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 简体中文
|
|
4
4
|
|
|
5
|
-
[](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
|
|
6
6
|
[](https://www.npmjs.com/package/davinci-resolve-mcp)
|
|
7
7
|
[](docs/reference/api-coverage.md)
|
|
8
|
-
[-blue.svg)](#服务器模式)
|
|
9
9
|
[-18%20tools-blueviolet.svg)](#服务器模式)
|
|
10
10
|
[](docs/reference/api-coverage.md#test-results)
|
|
11
11
|
[](https://www.blackmagicdesign.com/products/davinciresolve)
|
|
12
12
|
[](https://www.python.org/downloads/)
|
|
13
13
|
[](https://opensource.org/licenses/MIT)
|
|
14
14
|
|
|
15
|
-
> 本翻译对应 v2.
|
|
15
|
+
> 本翻译对应 v2.101.0 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
|
|
16
16
|
|
|
17
17
|
一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
|
|
18
18
|
|
|
@@ -81,7 +81,7 @@ venv/bin/python -m src.control_panel
|
|
|
81
81
|
|
|
82
82
|
| 模式 | 入口 | 工具数 | 适合谁 |
|
|
83
83
|
|------|------|--------|--------|
|
|
84
|
-
| Compound(复合) | `src/server.py` |
|
|
84
|
+
| Compound(复合) | `src/server.py` | 36 | 大多数助手的默认模式。相关的 Resolve 操作按 action 参数分组,压低上下文占用。 |
|
|
85
85
|
| Full / granular(细粒度) | `src/server.py --full` 或 `src/resolve_mcp_server.py` | 353 | 想要"一个 Resolve API 方法 = 一个 MCP 工具"的重度用户。 |
|
|
86
86
|
|
|
87
87
|
除非你明确需要一方法一工具的细粒度界面,否则推荐复合模式。
|
|
@@ -196,7 +196,7 @@ DRX 调色写入**针对 Resolve Studio 做过实机校准**:调色参数默
|
|
|
196
196
|
|
|
197
197
|
| 指标 | 数值 |
|
|
198
198
|
|------|------|
|
|
199
|
-
| MCP 工具 | **
|
|
199
|
+
| MCP 工具 | **36** 复合 / **353** 细粒度(实时服务器) |
|
|
200
200
|
| Advanced(离线)工具 | **18**——.drp/.drt/.drx + 数据库创作,无需 Resolve 运行 |
|
|
201
201
|
| 内核 action | 9 个复合工具下 **136** 个带护栏的工作流 action |
|
|
202
202
|
| API 方法覆盖 | **361/361**(100%) |
|
package/docs/SKILL.md
CHANGED
|
@@ -162,7 +162,7 @@ before mutating Resolve state.
|
|
|
162
162
|
|
|
163
163
|
| Mode | Entry point | Tool count | Use when |
|
|
164
164
|
|---|---|---|---|
|
|
165
|
-
| Compound (default) | `src/server.py` |
|
|
165
|
+
| Compound (default) | `src/server.py` | 36 tools | Most workflows — keeps context lean |
|
|
166
166
|
| Granular (full) | `src/server.py --full` | 353 tools | Power users needing one tool per API method |
|
|
167
167
|
|
|
168
168
|
This skill document covers the **compound server** (the default). Each compound
|
|
@@ -494,6 +494,32 @@ you are on the correct page first.
|
|
|
494
494
|
|
|
495
495
|
## Tool Map
|
|
496
496
|
|
|
497
|
+
### Craft Guidance
|
|
498
|
+
|
|
499
|
+
**`knowledge`** — The editorial, colour, audio, and workflow guidance bundled with
|
|
500
|
+
this server, served as prose. No Resolve connection required.
|
|
501
|
+
|
|
502
|
+
Read a topic **before** a creative or destructive operation, not after. The tools will
|
|
503
|
+
happily execute an editorially wrong decision; this is where the reasoning lives —
|
|
504
|
+
measured numbers, known traps, and what each move costs to undo.
|
|
505
|
+
|
|
506
|
+
Key actions:
|
|
507
|
+
- `topics(category?)` — the index: topic id, one-line summary, size, sections, and
|
|
508
|
+
related topics. Categories: `workflow` (task playbooks: tighten a recording, build a
|
|
509
|
+
rough cut, match a grade), `guide`, `kernel` (per-surface tool maps), `reference`
|
|
510
|
+
(exhaustive ledgers including this document), `repo` (contributing here)
|
|
511
|
+
- `get(topic, section?, inline?)` — the resolved prose. Natural aliases work
|
|
512
|
+
(`"tighten"`, `"dead air"`, `"grading"`, `"conform"`). Referenced guides and kernels
|
|
513
|
+
arrive inlined, so a client with no checkout of this repository still gets the
|
|
514
|
+
manual, not a path to it. `section` returns one heading's subtree
|
|
515
|
+
- `search(query, limit?)` — ranked topics with excerpts
|
|
516
|
+
- `capabilities()` — topic count by category, and the corpus directories
|
|
517
|
+
|
|
518
|
+
The same index is published as the `knowledge://topics` MCP resource, so hosts that
|
|
519
|
+
consume resources can see what guidance exists without spending a turn.
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
497
523
|
### App Control
|
|
498
524
|
|
|
499
525
|
**`resolve_control`** — App-level operations.
|
package/docs/contributing.md
CHANGED
|
@@ -63,7 +63,7 @@ This MCP server controls DaVinci Resolve via its Scripting API. Some tools perfo
|
|
|
63
63
|
davinci-resolve-mcp/
|
|
64
64
|
├── install.py # Universal installer (macOS/Windows/Linux)
|
|
65
65
|
├── src/
|
|
66
|
-
│ ├── server.py # Compound MCP server —
|
|
66
|
+
│ ├── server.py # Compound MCP server — 36 tools (default)
|
|
67
67
|
│ ├── resolve_mcp_server.py # Thin full-server entrypoint — 353 tools
|
|
68
68
|
│ ├── granular/ # Modular full-server implementation
|
|
69
69
|
│ └── utils/ # Platform detection, Resolve connection helpers
|
|
@@ -211,6 +211,36 @@ If untreated/current/after comparison is not available through the API in the
|
|
|
211
211
|
moment, say which part is unavailable and whether the user wants a blind/global
|
|
212
212
|
pass. Do not imply that a grade was reviewed if no rendered frames were checked.
|
|
213
213
|
|
|
214
|
+
## Rejecting Your Own Grade
|
|
215
|
+
|
|
216
|
+
Looking at frames tells you whether a grade reads right. It does not reliably tell you
|
|
217
|
+
whether it *damaged* the picture — banding in a sky, highlight levels collapsing, shadow
|
|
218
|
+
grain amplified into visible noise. Those are measurable, and measuring them is cheaper
|
|
219
|
+
and steadier than eyeballing a compressed preview.
|
|
220
|
+
|
|
221
|
+
`media_analysis assess_grade` measures one graded frame against its source and returns
|
|
222
|
+
flags with remedies. `media_analysis grade_loop` closes the loop: it applies a look LUT,
|
|
223
|
+
measures the real decoded result, and on any flag retries with the same look attenuated
|
|
224
|
+
toward identity (strength × 0.8 per rung, floored at 0.5, three tries by default). The
|
|
225
|
+
first strength that clears every sampled frame wins.
|
|
226
|
+
|
|
227
|
+
Three properties matter more than the convenience:
|
|
228
|
+
|
|
229
|
+
- **A flagged result is never reported acceptable.** When the ladder is exhausted the
|
|
230
|
+
answer is `needs_human`, carrying the best attempt and what it still fails on. There
|
|
231
|
+
is no strength at which the loop shrugs and ships.
|
|
232
|
+
- **Every sampled frame must pass.** Pass several timestamps — a grade that is clean on
|
|
233
|
+
the frame you happened to check and bands two hundred frames later has not passed
|
|
234
|
+
anything. The report names the frame that failed.
|
|
235
|
+
- **It does not touch the project.** The loop returns an apply manifest and stops.
|
|
236
|
+
Applying a grade is still a deliberate, version-protected step, and a result carrying
|
|
237
|
+
unresolved flags should reach a human before it reaches a timeline.
|
|
238
|
+
|
|
239
|
+
Reach for it when applying an unfamiliar look LUT to unfamiliar footage, which is
|
|
240
|
+
exactly where "it looked fine on the first shot" goes wrong. The flags are advisory
|
|
241
|
+
thresholds, not standards — the raw measurements come back too, so a colorist can
|
|
242
|
+
disagree with the flag rather than only with the verdict.
|
|
243
|
+
|
|
214
244
|
## Safe Color Workflow
|
|
215
245
|
|
|
216
246
|
Before changing color:
|
package/docs/install.md
CHANGED
|
@@ -141,7 +141,7 @@ The MCP server comes in two modes:
|
|
|
141
141
|
|
|
142
142
|
| Mode | File | Tools | Best For |
|
|
143
143
|
|------|------|-------|----------|
|
|
144
|
-
| **Compound** (default) | `src/server.py` |
|
|
144
|
+
| **Compound** (default) | `src/server.py` | 36 | Most users — fast, clean, low context usage |
|
|
145
145
|
| **Full** | `src/resolve_mcp_server.py` | 353 | Power users who want one tool per API method |
|
|
146
146
|
|
|
147
147
|
The compound server's `timeline_item` tool includes dedicated actions for common workflows:
|
|
@@ -67,6 +67,28 @@ All actions are exposed through `timeline_item_color`.
|
|
|
67
67
|
and delete worked.
|
|
68
68
|
- Gallery capability and album list/create calls worked.
|
|
69
69
|
|
|
70
|
+
## Numeric grade QC (`media_analysis`)
|
|
71
|
+
|
|
72
|
+
Not part of the live kernel — no Resolve connection is involved — but it belongs to the
|
|
73
|
+
same decision. Both actions measure a decoded frame of the real result, never a
|
|
74
|
+
simulated transform, because LUT interpolation and encode rounding are where banding is
|
|
75
|
+
actually introduced.
|
|
76
|
+
|
|
77
|
+
- `assess_grade(source_path, time_seconds, graded_path|lut_path, working_space)` —
|
|
78
|
+
flags (`flat`, `washed_out`, `milky`, `noisy`, `clipped`, `posterized`, `banding`),
|
|
79
|
+
each with a remedy, plus the raw tonal/noise/damage measurements.
|
|
80
|
+
- `grade_loop(source_path, lut_path, times[], strength?, max_tries?, strength_floor?,
|
|
81
|
+
dry_run?)` — the retry ladder over `assess_grade`. Attenuates the look toward identity
|
|
82
|
+
until every sampled frame clears, or returns `needs_human` with the best attempt.
|
|
83
|
+
`dry_run` defaults to **true** and reports the ffmpeg decode budget first.
|
|
84
|
+
- `grade_loop_capabilities()` — dependency state, ladder constants, and which modes
|
|
85
|
+
exist. The in-loop **live** mode (apply in Resolve, render, assess) is **not built**;
|
|
86
|
+
the loop returns an apply manifest instead of driving the project.
|
|
87
|
+
|
|
88
|
+
Both are display-referred only. Log and scene-referred encodings run through the same
|
|
89
|
+
arithmetic happily and produce meaningless numbers, so `working_space` must be declared
|
|
90
|
+
and non-display-referred values are refused rather than guessed at.
|
|
91
|
+
|
|
70
92
|
## Boundaries
|
|
71
93
|
|
|
72
94
|
- Node graph internals are intentionally limited by Resolve's public API. The
|
|
@@ -6,7 +6,7 @@ Complete Resolve scripting API coverage, live-test status, and method-by-method
|
|
|
6
6
|
|
|
7
7
|
| Metric | Value |
|
|
8
8
|
|--------|-------|
|
|
9
|
-
| MCP Tools | **
|
|
9
|
+
| MCP Tools | **36** compound (default) / **353** granular |
|
|
10
10
|
| Kernel Actions | **136** guarded MCP workflow actions across 9 compound tools |
|
|
11
11
|
| API Methods Covered | **361/361** (100%) |
|
|
12
12
|
| Methods Live Tested | **338/361** (93.6%) |
|
|
@@ -17,7 +17,7 @@ Complete Resolve scripting API coverage, live-test status, and method-by-method
|
|
|
17
17
|
|
|
18
18
|
## API Coverage
|
|
19
19
|
|
|
20
|
-
Every non-deprecated method in the DaVinci Resolve Scripting API is covered. The default compound server exposes **
|
|
20
|
+
Every non-deprecated method in the DaVinci Resolve Scripting API is covered. The default compound server exposes **36 tools** that group related operations by action parameter, keeping LLM context windows lean. The full granular server provides **353 individual tools** for power users. Both modes cover all 13 API object classes. MCP-level kernel actions are tracked separately in [Kernel Action Coverage](../kernels/README.md).
|
|
21
21
|
|
|
22
22
|
The 34th compound tool is `timeline_versioning` (C6) — an MCP-level workflow
|
|
23
23
|
tool, not a wrapper around a Resolve API method. It surfaces the
|
package/install.py
CHANGED
|
@@ -37,7 +37,7 @@ from src.utils.update_check import (
|
|
|
37
37
|
|
|
38
38
|
# ─── Version ──────────────────────────────────────────────────────────────────
|
|
39
39
|
|
|
40
|
-
VERSION = "2.
|
|
40
|
+
VERSION = "2.101.0"
|
|
41
41
|
# Only hard floor: mcp[cli] requires Python 3.10+. There is no upper bound —
|
|
42
42
|
# Resolve's scripting bridge loads into newer interpreters on recent builds
|
|
43
43
|
# (Python 3.14 verified against Resolve Studio 20.3.2). Older Resolve builds
|
package/package.json
CHANGED
package/src/granular/common.py
CHANGED
|
@@ -87,7 +87,7 @@ if not logging.getLogger().handlers:
|
|
|
87
87
|
handlers=[logging.StreamHandler()],
|
|
88
88
|
)
|
|
89
89
|
|
|
90
|
-
VERSION = "2.
|
|
90
|
+
VERSION = "2.101.0"
|
|
91
91
|
logger = logging.getLogger("davinci-resolve-mcp")
|
|
92
92
|
logger.info(f"Starting DaVinci Resolve MCP Server v{VERSION}")
|
|
93
93
|
logger.info(f"Detected platform: {get_platform()}")
|
package/src/server.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"""
|
|
3
3
|
DaVinci Resolve MCP Server (Compound Tools)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
36 compound tools covering 100% of the DaVinci Resolve Scripting API (336 methods)
|
|
6
6
|
plus Fusion Fuse, DCTL, and Resolve-page Script authoring tools.
|
|
7
7
|
Each tool groups related operations via an 'action' parameter.
|
|
8
8
|
|
|
@@ -11,7 +11,7 @@ Usage:
|
|
|
11
11
|
python src/server.py --full # Start the 353-tool granular server instead
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
VERSION = "2.
|
|
14
|
+
VERSION = "2.101.0"
|
|
15
15
|
|
|
16
16
|
import base64
|
|
17
17
|
import os
|
|
@@ -317,7 +317,7 @@ def davinci_resolve_workflow() -> str:
|
|
|
317
317
|
return """Use this DaVinci Resolve MCP server as a guarded post-production control surface.
|
|
318
318
|
|
|
319
319
|
Core pattern:
|
|
320
|
-
- Prefer the
|
|
320
|
+
- Prefer the 36 compound tools and their action names over raw scripting.
|
|
321
321
|
- Start by probing state: resolve_control.get_version/get_page, project_manager.get_current, timeline.get_current, and media_pool.probe_media_pool.
|
|
322
322
|
- Before mutating timelines, media pools, render settings, grades, projects, databases, or extensions, prefer the matching probe, capabilities, boundary_report, safe_*, or dry_run action when one exists.
|
|
323
323
|
- Preserve source media integrity. Never transcode, proxy, rewrite, move, rename, or create derivatives of source media unless the user explicitly asks. Analysis output belongs in sidecars or analysis directories.
|
|
@@ -14730,6 +14730,17 @@ def setup(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any
|
|
|
14730
14730
|
if action in {"schema", "capabilities", "options"}:
|
|
14731
14731
|
return {
|
|
14732
14732
|
"actions": ["schema", "get_defaults", "set_defaults", "clear_defaults"],
|
|
14733
|
+
# Agents commonly call this first. The craft guidance is worth nothing if
|
|
14734
|
+
# nobody knows it is there, so the orientation call names it.
|
|
14735
|
+
"craft_guidance": {
|
|
14736
|
+
"tool": "knowledge",
|
|
14737
|
+
"start_with": "knowledge(action='topics')",
|
|
14738
|
+
"when": (
|
|
14739
|
+
"Before a creative or destructive operation — cutting, grading, "
|
|
14740
|
+
"conforming, tightening, delivering. The tools execute; this is "
|
|
14741
|
+
"where the reasoning and the measured numbers live."
|
|
14742
|
+
),
|
|
14743
|
+
},
|
|
14733
14744
|
"defaults": {
|
|
14734
14745
|
"media_analysis.timed_markers_default": {
|
|
14735
14746
|
"description": "Default answer for writing source-time analysis notes as Media Pool clip markers.",
|
|
@@ -20065,6 +20076,58 @@ async def media_analysis(action: str, params: Optional[Dict[str, Any]] = None, c
|
|
|
20065
20076
|
"metrics are undefined on them and will not be guessed at."
|
|
20066
20077
|
),
|
|
20067
20078
|
)
|
|
20079
|
+
if action == "grade_loop":
|
|
20080
|
+
# The retry ladder that consumes assess_grade's own verdict: apply the look,
|
|
20081
|
+
# measure the real decoded frame, and on any flag retry with the look
|
|
20082
|
+
# attenuated toward identity. Exhausting the ladder returns needs_human with
|
|
20083
|
+
# the best attempt — never a quiet success at a strength that still bands.
|
|
20084
|
+
from src.utils import grade_loop as _grade_loop_mod
|
|
20085
|
+
|
|
20086
|
+
source = str(p.get("source_path") or p.get("sourcePath") or "")
|
|
20087
|
+
lut = str(p.get("lut_path") or p.get("lutPath") or "")
|
|
20088
|
+
err, _clean = _validate_params(
|
|
20089
|
+
{"source_path": source, "lut_path": lut},
|
|
20090
|
+
{
|
|
20091
|
+
"source_path": {"type": str, "required": True, "non_empty": True},
|
|
20092
|
+
"lut_path": {"type": str, "required": True, "non_empty": True},
|
|
20093
|
+
},
|
|
20094
|
+
)
|
|
20095
|
+
if err:
|
|
20096
|
+
return _err(err)
|
|
20097
|
+
kwargs = dict(
|
|
20098
|
+
times=p.get("times"),
|
|
20099
|
+
time_seconds=p.get("time_seconds", p.get("timeSeconds")),
|
|
20100
|
+
strength=float(p.get("strength", 1.0) or 1.0),
|
|
20101
|
+
max_tries=int(p.get("max_tries", p.get("maxTries", _grade_loop_mod.DEFAULT_MAX_TRIES))),
|
|
20102
|
+
strength_floor=float(
|
|
20103
|
+
p.get("strength_floor", p.get("strengthFloor", _grade_loop_mod.DEFAULT_STRENGTH_FLOOR))
|
|
20104
|
+
),
|
|
20105
|
+
working_space=str(p.get("working_space") or p.get("workingSpace") or "rec709"),
|
|
20106
|
+
cost_tier=str(p.get("cost_tier") or p.get("costTier") or _grade_loop_mod.DEFAULT_COST_TIER),
|
|
20107
|
+
)
|
|
20108
|
+
try:
|
|
20109
|
+
# Dry run by default: the ladder can spend a dozen ffmpeg decodes per clip,
|
|
20110
|
+
# and the plan names that budget before anyone commits to it.
|
|
20111
|
+
if p.get("dry_run", True):
|
|
20112
|
+
return _ok(**_grade_loop_mod.plan(source, lut, **kwargs))
|
|
20113
|
+
return _ok(**_grade_loop_mod.run(
|
|
20114
|
+
source, lut,
|
|
20115
|
+
output_dir=(p.get("output_dir") or p.get("outputDir")) or None,
|
|
20116
|
+
**kwargs,
|
|
20117
|
+
))
|
|
20118
|
+
except (_grade_loop_mod.GradeLoopError, _grade_loop_mod.cube_lut.CubeLutError) as exc:
|
|
20119
|
+
return _err(str(exc), code="GRADE_LOOP_REFUSED", category="invalid_input",
|
|
20120
|
+
remediation=(
|
|
20121
|
+
"Supply an existing source_path and a 3D .cube lut_path, plus "
|
|
20122
|
+
"times=[seconds,...] to sample. A grade clean on one frame is "
|
|
20123
|
+
"not a grade that passed."
|
|
20124
|
+
))
|
|
20125
|
+
except _grade_loop_mod.image_qc.ImageQcError as exc:
|
|
20126
|
+
return _err(str(exc), code="IMAGE_QC_REFUSED", category="invalid_input")
|
|
20127
|
+
if action == "grade_loop_capabilities":
|
|
20128
|
+
from src.utils import grade_loop as _grade_loop_mod
|
|
20129
|
+
|
|
20130
|
+
return _ok(**_grade_loop_mod.capabilities())
|
|
20068
20131
|
if action == "image_qc_capabilities":
|
|
20069
20132
|
from src.utils import image_qc as _image_qc_mod
|
|
20070
20133
|
|
|
@@ -21017,6 +21080,8 @@ async def media_analysis(action: str, params: Optional[Dict[str, Any]] = None, c
|
|
|
21017
21080
|
"capabilities",
|
|
21018
21081
|
"recheck_capabilities",
|
|
21019
21082
|
"assess_grade",
|
|
21083
|
+
"grade_loop",
|
|
21084
|
+
"grade_loop_capabilities",
|
|
21020
21085
|
"image_qc_capabilities",
|
|
21021
21086
|
"install_guidance",
|
|
21022
21087
|
"resolve_output_root",
|
|
@@ -29471,6 +29536,83 @@ def script_plugin(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[
|
|
|
29471
29536
|
"execute", "run_inline", *_EXTENSION_KERNEL_ACTIONS])
|
|
29472
29537
|
|
|
29473
29538
|
|
|
29539
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
29540
|
+
# TOOL: knowledge
|
|
29541
|
+
#
|
|
29542
|
+
# The craft guidance in this repository — how to tighten a take without cutting
|
|
29543
|
+
# the breath out of it, what to look at before applying a grade, which API calls
|
|
29544
|
+
# silently lie — has always been readable only by an agent with this checkout on
|
|
29545
|
+
# disk. Over MCP there is no checkout, so a client on any other host operated the
|
|
29546
|
+
# tools without ever seeing the reasoning that makes the operation correct.
|
|
29547
|
+
#
|
|
29548
|
+
# This serves that corpus as content: the index, one resolved topic, or a search.
|
|
29549
|
+
# No Resolve connection is involved at any point.
|
|
29550
|
+
# ═══════════════════════════════════════════════════════════════════════════════
|
|
29551
|
+
|
|
29552
|
+
_KNOWLEDGE_ACTIONS = ["topics", "get", "search", "capabilities"]
|
|
29553
|
+
|
|
29554
|
+
|
|
29555
|
+
@mcp.tool()
|
|
29556
|
+
@_guard_missing_params
|
|
29557
|
+
def knowledge(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
29558
|
+
"""Editorial, colour, audio, and workflow guidance — readable without this checkout.
|
|
29559
|
+
|
|
29560
|
+
Read a topic BEFORE a creative or destructive operation, not after. The guidance
|
|
29561
|
+
carries measured numbers and known traps; operating the tools without it is how a
|
|
29562
|
+
technically-correct call produces an editorially wrong result.
|
|
29563
|
+
|
|
29564
|
+
Actions:
|
|
29565
|
+
topics(category?) -> {topics} — the index: id, summary, size, sections, related.
|
|
29566
|
+
Categories: workflow (task playbooks), guide, kernel (per-surface tool maps),
|
|
29567
|
+
reference (exhaustive ledgers), repo (contributing to this project).
|
|
29568
|
+
get(topic, section?, inline?) -> {content} — resolved prose. Accepts natural
|
|
29569
|
+
aliases ("tighten", "dead air", "grading"). `section` returns one heading's
|
|
29570
|
+
subtree; `inline=false` skips the referenced documents.
|
|
29571
|
+
search(query, limit?) -> {hits} — ranked topics with excerpts.
|
|
29572
|
+
capabilities() -> {topic_count, categories, corpus}
|
|
29573
|
+
|
|
29574
|
+
No Resolve connection required.
|
|
29575
|
+
"""
|
|
29576
|
+
p = _params(params)
|
|
29577
|
+
from src.utils import knowledge as _knowledge_mod
|
|
29578
|
+
|
|
29579
|
+
try:
|
|
29580
|
+
if action == "topics":
|
|
29581
|
+
category = p.get("category")
|
|
29582
|
+
listing = _knowledge_mod.topics(category=str(category) if category else None)
|
|
29583
|
+
return _ok(topics=listing, count=len(listing),
|
|
29584
|
+
categories=list(_knowledge_mod.CATEGORIES))
|
|
29585
|
+
if action == "get":
|
|
29586
|
+
err, _clean = _validate_params(p, {
|
|
29587
|
+
"topic": {"type": str, "required": True, "non_empty": True},
|
|
29588
|
+
})
|
|
29589
|
+
if err:
|
|
29590
|
+
return _err(err)
|
|
29591
|
+
section = p.get("section")
|
|
29592
|
+
return _ok(**_knowledge_mod.get(
|
|
29593
|
+
str(p["topic"]),
|
|
29594
|
+
section=str(section) if section else None,
|
|
29595
|
+
inline=bool(p.get("inline", True)),
|
|
29596
|
+
))
|
|
29597
|
+
if action == "search":
|
|
29598
|
+
err, _clean = _validate_params(p, {
|
|
29599
|
+
"query": {"type": str, "required": True, "non_empty": True},
|
|
29600
|
+
})
|
|
29601
|
+
if err:
|
|
29602
|
+
return _err(err)
|
|
29603
|
+
hits = _knowledge_mod.search(str(p["query"]), limit=int(p.get("limit", 5)))
|
|
29604
|
+
return _ok(hits=hits, count=len(hits))
|
|
29605
|
+
if action in {"capabilities", "schema"}:
|
|
29606
|
+
return _ok(**_knowledge_mod.capabilities(), actions=_KNOWLEDGE_ACTIONS)
|
|
29607
|
+
except _knowledge_mod.KnowledgeError as exc:
|
|
29608
|
+
# The message already names the real topics or sections, so an agent that
|
|
29609
|
+
# guessed wrong can correct itself without a second round-trip.
|
|
29610
|
+
return _err(str(exc), code="UNKNOWN_TOPIC", category="invalid_params",
|
|
29611
|
+
retryable=False)
|
|
29612
|
+
|
|
29613
|
+
return _unknown(action, _KNOWLEDGE_ACTIONS)
|
|
29614
|
+
|
|
29615
|
+
|
|
29474
29616
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
29475
29617
|
# MCP Resources — agentic-flow improvement E1
|
|
29476
29618
|
#
|
|
@@ -29515,6 +29657,32 @@ def _resource_mcp_version() -> Dict[str, Any]:
|
|
|
29515
29657
|
}
|
|
29516
29658
|
|
|
29517
29659
|
|
|
29660
|
+
@mcp.resource("knowledge://topics")
|
|
29661
|
+
@_safe_resource
|
|
29662
|
+
def _resource_knowledge_topics() -> Dict[str, Any]:
|
|
29663
|
+
"""The knowledge index — id, summary, category, size. Pure read of bundled docs.
|
|
29664
|
+
|
|
29665
|
+
A host that consumes resources learns what guidance exists without spending a turn
|
|
29666
|
+
on it, which is the difference between the `knowledge` tool being available and it
|
|
29667
|
+
being used.
|
|
29668
|
+
"""
|
|
29669
|
+
from src.utils import knowledge as _knowledge_mod
|
|
29670
|
+
|
|
29671
|
+
return {
|
|
29672
|
+
"topics": [
|
|
29673
|
+
{
|
|
29674
|
+
"topic": item["topic"],
|
|
29675
|
+
"title": item["title"],
|
|
29676
|
+
"category": item["category"],
|
|
29677
|
+
"summary": item["summary"],
|
|
29678
|
+
"length_lines": item["resolved_length_lines"],
|
|
29679
|
+
}
|
|
29680
|
+
for item in _knowledge_mod.topics()
|
|
29681
|
+
],
|
|
29682
|
+
"fetch_with": "knowledge(action='get', params={'topic': '<id>'})",
|
|
29683
|
+
}
|
|
29684
|
+
|
|
29685
|
+
|
|
29518
29686
|
@mcp.resource("status://resolve_connection")
|
|
29519
29687
|
@_safe_resource
|
|
29520
29688
|
def _resource_resolve_connection() -> Dict[str, Any]:
|
|
@@ -29734,5 +29902,5 @@ if __name__ == "__main__":
|
|
|
29734
29902
|
logger.error(f"Unknown --transport {transport!r}; use stdio|sse|streamable-http")
|
|
29735
29903
|
sys.exit(2)
|
|
29736
29904
|
|
|
29737
|
-
logger.info("Starting DaVinci Resolve MCP Server (
|
|
29905
|
+
logger.info("Starting DaVinci Resolve MCP Server (36 compound tools)")
|
|
29738
29906
|
run_fastmcp_stdio(mcp)
|