davinci-resolve-mcp 2.221.0 → 2.223.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/CHANGELOG.md +86 -0
- package/README.md +4 -4
- package/README.zh-CN.md +5 -5
- package/docs/SKILL.md +5 -1
- package/docs/contributing.md +1 -1
- package/docs/install.md +2 -2
- package/docs/reference/api-coverage.md +5 -1
- package/docs/reference/api-limitations.md +1 -1
- package/docs/reference/resolve211-alignment.md +54 -0
- package/docs/reference/resolve211-dctl-validation.md +31 -0
- package/docs/reference/resolve211-multicam.md +5 -5
- package/install.py +2 -2
- package/package.json +1 -1
- package/src/granular/common.py +1 -1
- package/src/granular/resolve_211.py +28 -0
- package/src/resolve_mcp_server.py +1 -1
- package/src/server.py +28 -6
- package/src/utils/api_truth.py +6 -1
- package/src/utils/destructive_hook.py +1 -0
- package/src/utils/execution_lifecycle.py +2 -1
- package/src/utils/resolve211_alignment.py +32 -0
- package/src/utils/resolve211_dctl.py +8 -0
- package/src/utils/resolve_versions.py +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,92 @@
|
|
|
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.223.0 — native Resolve 21.1 DCTL validation
|
|
6
|
+
|
|
7
|
+
Contributed by @legionsound (#215), live-validated on Studio 21.1.0.14.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`dctl validate_native`**, with the granular twin `validate_dctl_native`,
|
|
12
|
+
calling Resolve 21.1's own `ValidateDCTL`. The existing `dctl validate`
|
|
13
|
+
remains the static offline checker — this is new surface beside it, not a
|
|
14
|
+
change to what that action claims. Tool count 375 → 376.
|
|
15
|
+
- **Source layout is passed through unchanged and native diagnostics are
|
|
16
|
+
returned verbatim.** That is the rule recorded in the ledger back in
|
|
17
|
+
v2.216.1, when @legionsound narrowed `ValidateDCTL` to being sensitive to
|
|
18
|
+
source layout — the same function validates multi-line and fails on one line
|
|
19
|
+
— and it is now implemented rather than merely written down: a wrapper that
|
|
20
|
+
reflowed the user's source to make validation pass would be hiding the very
|
|
21
|
+
behaviour the ledger entry exists to warn about.
|
|
22
|
+
- `None` means valid, strings mean invalid, and an **unexpected native result
|
|
23
|
+
type is an error rather than a false success** — the distinction that keeps a
|
|
24
|
+
changed API from silently reading as "your shader is fine".
|
|
25
|
+
- Version-gated and explicitly classified **read-only**: verified here as LOW /
|
|
26
|
+
non-destructive. It does not install, encrypt, apply or render a DCTL, and
|
|
27
|
+
a successful validation is not a rendered shader test.
|
|
28
|
+
|
|
29
|
+
### Validation
|
|
30
|
+
|
|
31
|
+
- Full suite green: 3,470 passed, 1 skipped. Focused contracts cover CRLF and
|
|
32
|
+
Unicode preservation, so the source that reaches Resolve is byte-for-byte
|
|
33
|
+
what the caller supplied.
|
|
34
|
+
- Live evidence is @legionsound's on Studio 21.1.0.14, checked against the
|
|
35
|
+
official Resolve MCP and both community interfaces: the multi-line identity
|
|
36
|
+
fixture validates, its one-line form reproduces the known missing-return
|
|
37
|
+
diagnostic, and invalid source returns the missing-entry-function
|
|
38
|
+
diagnostic — with both wrappers matching native results exactly. Not
|
|
39
|
+
reproduced here; this machine is Studio 19.1.3.7, below the 21.1 floor.
|
|
40
|
+
- **Not claimed**: encryption, and any statement that validation implies a
|
|
41
|
+
shader renders correctly.
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- Adapted on merge, as with #212, #213 and #214: counts resolved to **376**,
|
|
46
|
+
confirmed by the agent-rule generator, generated files regenerated rather
|
|
47
|
+
than hand-merged. No behaviour changed in the adaptation.
|
|
48
|
+
|
|
49
|
+
## What's New in v2.222.0 — native Resolve 21.1 timecode and waveform alignment
|
|
50
|
+
|
|
51
|
+
Contributed by @legionsound (#212), live-validated on Studio 21.1.0.14.
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- **`timeline auto_align_clips`**, with the granular twin
|
|
56
|
+
`auto_align_timeline_clips`, calling native 21.1 `AutoAlignClips` on explicit
|
|
57
|
+
timeline item IDs, with the documented `SyncUsing` and `UseTrack` options
|
|
58
|
+
accepting either constant names or integral native values. Every ID resolves
|
|
59
|
+
before anything moves, order is preserved, malformed input is refused, and a
|
|
60
|
+
native `false` stays `false`. Tool count 374 → 375.
|
|
61
|
+
- **The wrapper does not silently expand the selection.** On the measured
|
|
62
|
+
build, waveform alignment refused a video-only selection, and an audio-only
|
|
63
|
+
selection aligned the audio while leaving its linked video at the old
|
|
64
|
+
position. Rather than quietly adding the linked items — which would move
|
|
65
|
+
clips the caller never named — the tool documentation tells callers to
|
|
66
|
+
include both sides of a linked pair, and an incomplete selection gets an
|
|
67
|
+
honest refusal it can act on.
|
|
68
|
+
|
|
69
|
+
### Validation
|
|
70
|
+
|
|
71
|
+
- Full suite green: 3,465 passed, 1 skipped. `timeline.auto_align_clips`
|
|
72
|
+
probed directly: MEDIUM / destructive / recognised, and present in the
|
|
73
|
+
destructive registry.
|
|
74
|
+
- Live evidence is @legionsound's on Studio 21.1.0.14, and the two modes carry
|
|
75
|
+
**different strengths of evidence**, kept distinct rather than averaged:
|
|
76
|
+
timecode alignment is a position result (sources one second apart, starts
|
|
77
|
+
0/48 moving to 0/24), while waveform/MIX was verified against rendered
|
|
78
|
+
output — identical speech starts 0/24 moving to 0/0, with complete decoded
|
|
79
|
+
video **and PCM audio** matching independently positioned manual reference
|
|
80
|
+
timelines exactly, for both wrappers. Not reproduced here; this machine is
|
|
81
|
+
Studio 19.1.3.7, below the 21.1 floor.
|
|
82
|
+
- **Not claimed**: other microphones, drift, variable frame rates, other track
|
|
83
|
+
selections and long recordings. Smart Switch remains separate and unstarted.
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
|
|
87
|
+
- Adapted on merge, as with #213 and #214: counts resolved to **375**,
|
|
88
|
+
confirmed by the agent-rule generator, generated files regenerated rather
|
|
89
|
+
than hand-merged. No behaviour changed in the adaptation.
|
|
90
|
+
|
|
5
91
|
## What's New in v2.221.0 — native Resolve 21.1 audio level normalization
|
|
6
92
|
|
|
7
93
|
Contributed by @legionsound (#214), live-validated on Studio 21.1.0.14.
|
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)
|
|
@@ -133,7 +133,7 @@ The command starts a loopback-only server and opens the control panel in your br
|
|
|
133
133
|
| Mode | Entry point | Tools | Best for |
|
|
134
134
|
|------|-------------|-------|----------|
|
|
135
135
|
| Compound | `src/server.py` | 36 | Default mode for most assistants. Related Resolve operations are grouped behind action parameters to keep context usage low. |
|
|
136
|
-
| Full / granular | `src/server.py --full` or `src/resolve_mcp_server.py` |
|
|
136
|
+
| Full / granular | `src/server.py --full` or `src/resolve_mcp_server.py` | 376 | Power users who want one MCP tool per Resolve API method. |
|
|
137
137
|
|
|
138
138
|
The compound server is recommended unless you specifically need the granular one-tool-per-method surface.
|
|
139
139
|
|
|
@@ -365,7 +365,7 @@ The default server is a local stdio process launched by your MCP client; it does
|
|
|
365
365
|
|
|
366
366
|
| Metric | Value |
|
|
367
367
|
|--------|-------|
|
|
368
|
-
| MCP Tools | **36** compound / **
|
|
368
|
+
| MCP Tools | **36** compound / **376** granular (live server) |
|
|
369
369
|
| Advanced (offline) tools | **18** — .drp/.drt/.drx + DB authoring, no Resolve running |
|
|
370
370
|
| Kernel Actions | **136** guarded workflow actions across 9 compound tools |
|
|
371
371
|
| 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.223.0 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
|
|
16
16
|
|
|
17
17
|
一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
|
|
18
18
|
|
|
@@ -88,7 +88,7 @@ venv/bin/python -m src.control_panel
|
|
|
88
88
|
| 模式 | 入口 | 工具数 | 适合谁 |
|
|
89
89
|
|------|------|--------|--------|
|
|
90
90
|
| Compound(复合) | `src/server.py` | 36 | 大多数助手的默认模式。相关的 Resolve 操作按 action 参数分组,压低上下文占用。 |
|
|
91
|
-
| Full / granular(细粒度) | `src/server.py --full` 或 `src/resolve_mcp_server.py` |
|
|
91
|
+
| Full / granular(细粒度) | `src/server.py --full` 或 `src/resolve_mcp_server.py` | 376 | 想要"一个 Resolve API 方法 = 一个 MCP 工具"的重度用户。 |
|
|
92
92
|
|
|
93
93
|
除非你明确需要一方法一工具的细粒度界面,否则推荐复合模式。
|
|
94
94
|
|
|
@@ -226,7 +226,7 @@ DRX 调色写入**针对 Resolve Studio 做过实机校准**:调色参数默
|
|
|
226
226
|
|
|
227
227
|
| 指标 | 数值 |
|
|
228
228
|
|------|------|
|
|
229
|
-
| MCP 工具 | **36** 复合 / **
|
|
229
|
+
| MCP 工具 | **36** 复合 / **376** 细粒度(实时服务器) |
|
|
230
230
|
| Advanced(离线)工具 | **18**——.drp/.drt/.drx + 数据库创作,无需 Resolve 运行 |
|
|
231
231
|
| 内核 action | 9 个复合工具下 **136** 个带护栏的工作流 action |
|
|
232
232
|
| API 方法覆盖 | **361/361**(100%) |
|
package/docs/SKILL.md
CHANGED
|
@@ -345,7 +345,7 @@ to the user as verified.
|
|
|
345
345
|
| Mode | Entry point | Tool count | Use when |
|
|
346
346
|
|---|---|---|---|
|
|
347
347
|
| Compound (default) | `src/server.py` | 36 tools | Most workflows — keeps context lean |
|
|
348
|
-
| Granular (full) | `src/server.py --full` |
|
|
348
|
+
| Granular (full) | `src/server.py --full` | 376 tools | Power users needing one tool per API method |
|
|
349
349
|
|
|
350
350
|
Resolve 21.1 adds [twelve read-only discovery controls](reference/resolve211-read-controls.md)
|
|
351
351
|
for edition, presets, audio formats/codecs, normalization modes, speed, fades
|
|
@@ -2488,3 +2488,7 @@ Native multicam creation and flattening: [21.1 controls](reference/resolve211-mu
|
|
|
2488
2488
|
Native timeline/clip output blanking: [21.1 controls](reference/resolve211-blanking.md), with explicit inheritance and pixel-bound evidence.
|
|
2489
2489
|
|
|
2490
2490
|
Native audio normalization: [21.1 controls](reference/resolve211-normalization.md), with independently measured exported-audio evidence.
|
|
2491
|
+
|
|
2492
|
+
Native timecode/waveform alignment: [21.1 controls](reference/resolve211-alignment.md), including linked-item selection semantics and rendered video/audio evidence.
|
|
2493
|
+
|
|
2494
|
+
Resolve-native DCTL validation: [21.1 controls](reference/resolve211-dctl-validation.md), separate from static validation and shader rendering.
|
package/docs/contributing.md
CHANGED
|
@@ -64,7 +64,7 @@ davinci-resolve-mcp/
|
|
|
64
64
|
├── install.py # Universal installer (macOS/Windows/Linux)
|
|
65
65
|
├── src/
|
|
66
66
|
│ ├── server.py # Compound MCP server — 36 tools (default)
|
|
67
|
-
│ ├── resolve_mcp_server.py # Thin full-server entrypoint —
|
|
67
|
+
│ ├── resolve_mcp_server.py # Thin full-server entrypoint — 376 tools
|
|
68
68
|
│ ├── granular/ # Modular full-server implementation
|
|
69
69
|
│ └── utils/ # Platform detection, Resolve connection helpers
|
|
70
70
|
├── tests/ # offline suite (test_*.py) + live harnesses (live_*.py):
|
package/docs/install.md
CHANGED
|
@@ -144,7 +144,7 @@ The MCP server comes in two modes:
|
|
|
144
144
|
| Mode | File | Tools | Best For |
|
|
145
145
|
|------|------|-------|----------|
|
|
146
146
|
| **Compound** (default) | `src/server.py` | 36 | Most users — fast, clean, low context usage |
|
|
147
|
-
| **Full** | `src/resolve_mcp_server.py` |
|
|
147
|
+
| **Full** | `src/resolve_mcp_server.py` | 376 | Power users who want one tool per API method |
|
|
148
148
|
|
|
149
149
|
The compound server's `timeline_item` tool includes dedicated actions for common workflows:
|
|
150
150
|
|
|
@@ -159,7 +159,7 @@ The compound server's `timeline_item` tool includes dedicated actions for common
|
|
|
159
159
|
|
|
160
160
|
The installer uses the compound server by default. To use the full server:
|
|
161
161
|
```bash
|
|
162
|
-
python src/server.py --full # Launch full
|
|
162
|
+
python src/server.py --full # Launch full 376-tool server
|
|
163
163
|
# Or point your MCP config directly at src/resolve_mcp_server.py
|
|
164
164
|
```
|
|
165
165
|
|
|
@@ -25,7 +25,7 @@ Every non-deprecated method in the bundled legacy README is represented. This
|
|
|
25
25
|
does not claim complete coverage of the newer Resolve 21.1 typed API. The
|
|
26
26
|
default compound server exposes **36 tools** that group related operations by
|
|
27
27
|
action parameter, keeping LLM context windows lean. The full granular server
|
|
28
|
-
provides **
|
|
28
|
+
provides **376 individual tools** for power users. The legacy coverage spans
|
|
29
29
|
13 API object classes. MCP-level kernel actions are tracked separately in
|
|
30
30
|
[Kernel Action Coverage](../kernels/README.md).
|
|
31
31
|
|
|
@@ -638,3 +638,7 @@ Native multicam creation and flattening: [21.1 controls](resolve211-multicam.md)
|
|
|
638
638
|
Native timeline/clip output blanking: [21.1 controls](resolve211-blanking.md), with explicit inheritance and pixel-bound evidence.
|
|
639
639
|
|
|
640
640
|
Native audio normalization: [21.1 controls](resolve211-normalization.md), with independently measured exported-audio evidence.
|
|
641
|
+
|
|
642
|
+
Native timecode/waveform alignment: [21.1 controls](resolve211-alignment.md), including linked-item selection semantics and rendered video/audio evidence.
|
|
643
|
+
|
|
644
|
+
Resolve-native DCTL validation: [21.1 controls](resolve211-dctl-validation.md), separate from static validation and shader rendering.
|
|
@@ -766,5 +766,5 @@ values, or automation-hostile modal prompts.
|
|
|
766
766
|
- **Object:** `Resolve`
|
|
767
767
|
- **Signature:** `(dctlSource) -> str | None (21.1+)`
|
|
768
768
|
- **Behavior:** Reported by @legionsound (issue #207) from Studio 21.1.0.14 on macOS; NOT reproduced here (no 21.1 install). The documented success result is None. A minimal identity transform written across several lines — `__DEVICE__ float3 transform(...)` with the body on its own lines — validates (None). The SAME function collapsed onto one line consistently returns 'DCTL Error: main DCTL function does not have return value.', which is false: the return statement is there. A genuinely invalid source returns 'cannot find main DCTL function.', so the validator does distinguish; it is the single-line layout it misreads. An earlier multi-line timeout did not reproduce after a Resolve restart with a 30-second limit. Nothing establishes a GPU compiler or render defect — this is the validator's parse, not the DCTL's execution. EncryptDCTL untested.
|
|
769
|
-
- **Workaround / current handling:** Any wrapper around ValidateDCTL must pass the native diagnostic through verbatim and must not reflow or rewrite the user's source to dodge it; ship the multi-line identity fixture as the known-good control. A 'no return value' error on a one-line function is this quirk, not a missing return — re-run the validation with the function laid out across lines before believing it. This server's own `dctl validate` is a static, offline check (entry point, brace balance, float suffixes) and does not call ValidateDCTL at all.
|
|
769
|
+
- **Workaround / current handling:** Any wrapper around ValidateDCTL must pass the native diagnostic through verbatim and must not reflow or rewrite the user's source to dodge it; ship the multi-line identity fixture as the known-good control. A 'no return value' error on a one-line function is this quirk, not a missing return — re-run the validation with the function laid out across lines before believing it. This server's own `dctl validate` is a static, offline check (entry point, brace balance, float suffixes) and does not call ValidateDCTL at all. The separate `dctl validate_native` and granular `validate_dctl_native` now expose the native validator without changing source or diagnostics. Both interfaces were contributor-validated against the three fixtures on Studio 21.1.0.14; see resolve211-dctl-validation.md.
|
|
770
770
|
- **Tags:** dctl, validation, unreliable-return, version-gated, reported
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Native Resolve 21.1 timeline alignment
|
|
2
|
+
|
|
3
|
+
Compound `timeline auto_align_clips` and granular `auto_align_timeline_clips`
|
|
4
|
+
accept `item_ids` (timeline item unique IDs, not media-pool IDs) and optional
|
|
5
|
+
`options`. All IDs must resolve in the current timeline's video/audio tracks;
|
|
6
|
+
missing or duplicate IDs are refused before writes. The requested order and
|
|
7
|
+
selection are preserved. This command does not implicitly add linked items.
|
|
8
|
+
|
|
9
|
+
**Include both video and linked audio IDs when synchronizing camera clips.**
|
|
10
|
+
Contributor observation on 21.1.0.14: waveform alignment of the video items alone
|
|
11
|
+
returned false; audio-only selection moved the audio while linked video stayed
|
|
12
|
+
put. Explicitly selecting all video/audio items aligned the complete fixture.
|
|
13
|
+
Do not assume that selecting one half of a linked pair moves the other half.
|
|
14
|
+
|
|
15
|
+
Options follow AutoAlignOptions: SyncUsing accepts
|
|
16
|
+
AUTO_ALIGN_CLIPS_USING_TIMECODE or AUTO_ALIGN_CLIPS_USING_WAVEFORM; UseTrack
|
|
17
|
+
accepts a native track number or AUTO_ALIGN_CLIPS_WAVEFORM_TRACK_MIX /
|
|
18
|
+
AUTO_ALIGN_CLIPS_WAVEFORM_TRACK_AUTOMATIC. Constant names resolve against the
|
|
19
|
+
live Resolve object; integral numeric native values are also accepted. Omitted
|
|
20
|
+
options stay omitted, using native defaults. Unknown keys, invalid named
|
|
21
|
+
constants and malformed types are rejected. Native false stays success:false.
|
|
22
|
+
|
|
23
|
+
The callable method and 21.1 floor are checked. The compound action is registered
|
|
24
|
+
as a destructive MEDIUM-risk write in both classifier tables. Explicit dry-run
|
|
25
|
+
requests are refused before mutation. Granular annotations mark a destructive,
|
|
26
|
+
non-idempotent write.
|
|
27
|
+
|
|
28
|
+
## Contributor validation and limits
|
|
29
|
+
|
|
30
|
+
Contributor-validated on macOS Studio 21.1.0.14, using synthetic speech and color
|
|
31
|
+
cards in a disposable 640x360/24 fps project. The two sources contain the same
|
|
32
|
+
speech but source timecodes differ by one second.
|
|
33
|
+
|
|
34
|
+
- Timecode fixture: deliberately placed at frames0/48, both wrappers moved the
|
|
35
|
+
selected video/audio to frames0/24, matching source timecodes.
|
|
36
|
+
- Waveform fixture with MIX: deliberately placed at frames0/24, both wrappers
|
|
37
|
+
moved all selected video/audio to frame0, matching identical speech content.
|
|
38
|
+
- For each mode, both wrappers' complete decoded ProRes video and PCM audio
|
|
39
|
+
matched an independently positioned manual reference exactly. This is full
|
|
40
|
+
rendered video/audio comparison, not just position readback.
|
|
41
|
+
|
|
42
|
+
Other audio track selections, dissimilar microphones, drift, variable frame rates,
|
|
43
|
+
long-form recordings and partly overlapping selections are not established by
|
|
44
|
+
this fixture. A failed chirp/video-only probe is not proof of a native bug.
|
|
45
|
+
Smart Switch is a separate operation and remains follow-up work.
|
|
46
|
+
|
|
47
|
+
`tests/live_resolve211_alignment.py OUTPUT_DIR` requires the disposable project
|
|
48
|
+
Codex Alignment Validation 20260909, with speech-red.mov and speech-blue.mov in
|
|
49
|
+
its root bin. Both are 24 fps color-card movies using the same synthetic spoken
|
|
50
|
+
recording; red timecode00:00:00:00 and blue timecode00:00:01:00. It creates manual,
|
|
51
|
+
compound and granular timelines for each mode, renders six movies and saves the
|
|
52
|
+
scratch project. Never substitute production media. Compare each mode's full
|
|
53
|
+
decoded video and audio against its manual reference. The helper tests also pin
|
|
54
|
+
ID ordering, malformed/missing input refusal, native failure and write gates.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Native DCTL validation in Resolve 21.1
|
|
2
|
+
|
|
3
|
+
Compound `dctl validate_native` and granular `validate_dctl_native` accept a
|
|
4
|
+
`source` string and call Resolve.ValidateDCTL. They return `valid`, `diagnostic`
|
|
5
|
+
and `checker: resolve_native`. The source is passed unchanged, including its
|
|
6
|
+
line layout. Native None means valid; a native string means invalid and is
|
|
7
|
+
returned verbatim, including whitespace. An unexpected native result type is a
|
|
8
|
+
protocol error, not a claim that the shader validated.
|
|
9
|
+
|
|
10
|
+
The existing `dctl validate` remains the static offline checker. Native validation
|
|
11
|
+
requires running Resolve 21.1 and a callable ValidateDCTL method. It does not
|
|
12
|
+
install a DCTL, encrypt it, apply a grade or render the shader. A successful
|
|
13
|
+
validation is not rendered-output evidence. The granular tool is read-only and
|
|
14
|
+
the compound action is explicitly classified as non-destructive.
|
|
15
|
+
|
|
16
|
+
## Contributor validation
|
|
17
|
+
|
|
18
|
+
Contributor-validated on macOS Studio 21.1.0.14 against the official MCP and both
|
|
19
|
+
actual community interfaces. The multiline identity fixture returns None. The
|
|
20
|
+
same function on one line returns `DCTL Error: main DCTL function does not have
|
|
21
|
+
return value.` Invalid source returns `cannot find main DCTL function.` Both
|
|
22
|
+
wrappers preserve these diagnostics exactly. The source-layout limitation is
|
|
23
|
+
already in api_truth; this wrapper exposes it honestly rather than reformatting
|
|
24
|
+
user code to hide it.
|
|
25
|
+
|
|
26
|
+
`python tests/live_resolve211_dctl.py` compares raw/native and both wrapper
|
|
27
|
+
results for all three fixtures. It neither changes projects nor installs files.
|
|
28
|
+
Unit tests also cover verbatim CRLF/Unicode source and diagnostic handling,
|
|
29
|
+
invalid inputs, missing methods, unexpected native result types, read-only risk
|
|
30
|
+
classification and preservation of the static checker route. Encryption and
|
|
31
|
+
actual GPU execution remain separate work.
|
|
@@ -48,11 +48,11 @@ Use the synthetic FFmpeg fixture commands in resolve211-native-transitions.md;
|
|
|
48
48
|
never run the fixture against production media. Compare complete decoded frames
|
|
49
49
|
from compound-before/compound-flattened/granular-before/granular-flattened.mov.
|
|
50
50
|
|
|
51
|
-
Smart Switch
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
positive speaking-camera test. This
|
|
55
|
-
multicam family completed.
|
|
51
|
+
Smart Switch remains separate work. Native timecode and waveform alignment now
|
|
52
|
+
have dedicated wrappers and rendered video/audio comparisons documented in
|
|
53
|
+
[alignment controls](resolve211-alignment.md). Silent color-card Smart Switch
|
|
54
|
+
returned false and is not a meaningful positive speaking-camera test. This
|
|
55
|
+
does not claim the entire multicam family completed.
|
|
56
56
|
|
|
57
57
|
Do not infer angle order from clip_ids order. The wrapper forwards the requested
|
|
58
58
|
source order, but this measurement establishes the selected angle and its
|
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.223.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
|
|
@@ -1543,7 +1543,7 @@ def verify_resolve_connection(python_path, api_path, lib_path):
|
|
|
1543
1543
|
|
|
1544
1544
|
def print_banner():
|
|
1545
1545
|
title = f"DaVinci Resolve MCP Server — Installer v{VERSION}"
|
|
1546
|
-
subtitle = "36 compound ·
|
|
1546
|
+
subtitle = "36 compound · 376 full · 3 platforms"
|
|
1547
1547
|
print()
|
|
1548
1548
|
print(bold(" ╔══════════════════════════════════════════════════════╗"))
|
|
1549
1549
|
print(bold(f" ║{title:^54}║"))
|
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.223.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()}")
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
"""Native Resolve 21.1 discovery and editing controls."""
|
|
2
|
+
from src.utils.resolve211_dctl import native_dctl_result
|
|
3
|
+
from src.utils.resolve211_alignment import auto_align
|
|
2
4
|
from src.utils.resolve211_normalization import normalize_audio
|
|
3
5
|
from src.utils.resolve211_blanking import validate_blanking
|
|
4
6
|
from src.utils.resolve211_multicam import create_multicam, resolve_constant, GRADES
|
|
@@ -291,3 +293,29 @@ def normalize_timeline_audio_level(item_ids: list[str], options: dict | None = N
|
|
|
291
293
|
if missing:
|
|
292
294
|
return missing
|
|
293
295
|
return normalize_audio(get_resolve(), timeline, item_ids, {} if options is None else options)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@mcp.tool(annotations=DESTRUCTIVE_TOOL)
|
|
299
|
+
def auto_align_timeline_clips(item_ids: list[str], options: dict | None = None) -> dict:
|
|
300
|
+
"""Native 21.1 alignment of current video/audio items by unique ID. Include linked audio AND video IDs to move both; selection is not expanded. Options SyncUsing and UseTrack accept documented constant names or integral native values."""
|
|
301
|
+
_, timeline, error = _get_timeline()
|
|
302
|
+
if error:
|
|
303
|
+
return error
|
|
304
|
+
missing = _requires_method(timeline, "AutoAlignClips", "21.1")
|
|
305
|
+
if missing:
|
|
306
|
+
return missing
|
|
307
|
+
return auto_align(get_resolve(), timeline, item_ids, {} if options is None else options)
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
@mcp.tool(annotations=READ_ONLY_TOOL)
|
|
311
|
+
def validate_dctl_native(source: str) -> dict:
|
|
312
|
+
"""Validate shader source with Resolve 21.1. Source layout and native diagnostics are preserved; success means validation, not a rendered shader test."""
|
|
313
|
+
if not isinstance(source, str):
|
|
314
|
+
return {"error": "source must be a string"}
|
|
315
|
+
r = get_resolve()
|
|
316
|
+
if r is None:
|
|
317
|
+
return {"error": "Not connected to DaVinci Resolve"}
|
|
318
|
+
missing = _requires_method(r, "ValidateDCTL", "21.1")
|
|
319
|
+
if missing:
|
|
320
|
+
return missing
|
|
321
|
+
return native_dctl_result(r, source)
|
|
@@ -34,7 +34,7 @@ from src.utils.update_check import start_background_update_check
|
|
|
34
34
|
if __name__ == "__main__":
|
|
35
35
|
try:
|
|
36
36
|
start_background_update_check(VERSION, project_dir, logger)
|
|
37
|
-
logger.info(f"Starting DaVinci Resolve MCP Server v{VERSION} (
|
|
37
|
+
logger.info(f"Starting DaVinci Resolve MCP Server v{VERSION} (376 granular tools)")
|
|
38
38
|
run_fastmcp_stdio(mcp)
|
|
39
39
|
except KeyboardInterrupt:
|
|
40
40
|
logger.info("Server shutdown requested")
|
package/src/server.py
CHANGED
|
@@ -8,10 +8,10 @@ Each tool groups related operations via an 'action' parameter.
|
|
|
8
8
|
|
|
9
9
|
Usage:
|
|
10
10
|
python src/server.py # Start the MCP server
|
|
11
|
-
python src/server.py --full # Start the
|
|
11
|
+
python src/server.py --full # Start the 376-tool granular server instead
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
VERSION = "2.
|
|
14
|
+
VERSION = "2.223.0"
|
|
15
15
|
|
|
16
16
|
import base64
|
|
17
17
|
import os
|
|
@@ -44,6 +44,8 @@ for p in [current_dir, project_dir]:
|
|
|
44
44
|
|
|
45
45
|
from src.utils.resolve211_multicam import create_multicam, resolve_constant, GRADES
|
|
46
46
|
from src.utils.resolve211_blanking import validate_blanking
|
|
47
|
+
from src.utils.resolve211_alignment import auto_align
|
|
48
|
+
from src.utils.resolve211_dctl import native_dctl_result
|
|
47
49
|
from src.utils.resolve211_normalization import normalize_audio
|
|
48
50
|
from src.utils.resolve211_edits import validate_edit_options, validate_transition_options, transition_result
|
|
49
51
|
|
|
@@ -25054,7 +25056,7 @@ def edit_engine(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[st
|
|
|
25054
25056
|
|
|
25055
25057
|
|
|
25056
25058
|
_TIMELINE_ACTIONS = [
|
|
25057
|
-
"set_output_blanking", "normalize_audio_level", "get_normalize_audio_modes", "get_output_blanking",
|
|
25059
|
+
"set_output_blanking", "normalize_audio_level", "auto_align_clips", "get_normalize_audio_modes", "get_output_blanking",
|
|
25058
25060
|
# Offline authoring — served without a Resolve connection, above the _check() gate.
|
|
25059
25061
|
"author_offline", "offline_fallback_capabilities",
|
|
25060
25062
|
"list", "get_current", "set_current", "get_name", "set_name", "get_start_frame",
|
|
@@ -25105,6 +25107,7 @@ def timeline(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str,
|
|
|
25105
25107
|
Actions:
|
|
25106
25108
|
set_output_blanking(options) -> {success} — native 21.1 pixel coordinates.
|
|
25107
25109
|
normalize_audio_level(item_ids, options?) -> {success} — native 21.1 normalization; audio timeline item IDs.
|
|
25110
|
+
auto_align_clips(item_ids, options?) -> {success} — native 21.1 timecode/waveform alignment; include both video/audio IDs to move linked pairs.
|
|
25108
25111
|
get_normalize_audio_modes() -> {modes} — documented on Resolve 21.1+.
|
|
25109
25112
|
get_output_blanking() -> {blanking} — documented on Resolve 21.1+. Pixel coordinates; empty on a clip inheriting timeline blanking.
|
|
25110
25113
|
list() -> {timelines}
|
|
@@ -25423,6 +25426,12 @@ def timeline(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str,
|
|
|
25423
25426
|
if not tl:
|
|
25424
25427
|
return _err("No current timeline")
|
|
25425
25428
|
|
|
25429
|
+
if action == "auto_align_clips":
|
|
25430
|
+
missing = _requires_method(tl, "AutoAlignClips", "21.1")
|
|
25431
|
+
if missing:
|
|
25432
|
+
return missing
|
|
25433
|
+
return auto_align(get_resolve(), tl, p.get("item_ids"), {} if p.get("options") is None else p["options"])
|
|
25434
|
+
|
|
25426
25435
|
if action == "normalize_audio_level":
|
|
25427
25436
|
missing = _requires_method(tl, "NormalizeAudioLevel", "21.1")
|
|
25428
25437
|
if missing:
|
|
@@ -31134,6 +31143,7 @@ def dctl(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]
|
|
|
31134
31143
|
remove(name, category?, subdir?, ext?) -> {success}
|
|
31135
31144
|
read(name, category?, subdir?, ext?) -> {source, encrypted}
|
|
31136
31145
|
validate(source) -> {valid, errors, warnings, checker}
|
|
31146
|
+
validate_native(source) -> {valid, diagnostic, checker} — Resolve 21.1 validation; source and diagnostic unchanged.
|
|
31137
31147
|
template(kind, name, options?) -> {source, kind, name, suggested_category}
|
|
31138
31148
|
— kind: 'transform' | 'transform_alpha' | 'transition' | 'matrix' |
|
|
31139
31149
|
'kernel' | 'lut_apply' | 'aces_idt' | 'aces_odt'
|
|
@@ -31287,6 +31297,18 @@ def dctl(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]
|
|
|
31287
31297
|
return {"source": src, "path": target,
|
|
31288
31298
|
"encrypted": ext == ".dctle", "category": cat}
|
|
31289
31299
|
|
|
31300
|
+
if action == "validate_native":
|
|
31301
|
+
source = p.get("source")
|
|
31302
|
+
if not isinstance(source, str):
|
|
31303
|
+
return _err("validate_native requires a source string")
|
|
31304
|
+
r = get_resolve()
|
|
31305
|
+
if r is None:
|
|
31306
|
+
return _not_connected_error()
|
|
31307
|
+
missing = _requires_method(r, "ValidateDCTL", "21.1")
|
|
31308
|
+
if missing:
|
|
31309
|
+
return missing
|
|
31310
|
+
return native_dctl_result(r, source)
|
|
31311
|
+
|
|
31290
31312
|
if action == "validate":
|
|
31291
31313
|
source = p.get("source")
|
|
31292
31314
|
if not isinstance(source, str):
|
|
@@ -31313,7 +31335,7 @@ def dctl(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]
|
|
|
31313
31335
|
}
|
|
31314
31336
|
|
|
31315
31337
|
return _unknown(action, ["path", "list", "install", "remove", "read",
|
|
31316
|
-
"validate", "template", "list_templates"])
|
|
31338
|
+
"validate_native", "validate", "template", "list_templates"])
|
|
31317
31339
|
|
|
31318
31340
|
|
|
31319
31341
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -32729,9 +32751,9 @@ if __name__ == "__main__":
|
|
|
32729
32751
|
start_background_update_check(VERSION, project_dir, logger, env=_setup_update_env())
|
|
32730
32752
|
_install_threaded_tool_dispatch(mcp)
|
|
32731
32753
|
|
|
32732
|
-
# Support --full flag to run the
|
|
32754
|
+
# Support --full flag to run the 376-tool granular server instead
|
|
32733
32755
|
if "--full" in sys.argv:
|
|
32734
|
-
logger.info("Starting full
|
|
32756
|
+
logger.info("Starting full 376-tool granular server...")
|
|
32735
32757
|
sys.argv = [arg for arg in sys.argv if arg != "--full"]
|
|
32736
32758
|
from src.granular import mcp as granular_mcp
|
|
32737
32759
|
|
package/src/utils/api_truth.py
CHANGED
|
@@ -3263,7 +3263,12 @@ API_TRUTH: List[Dict[str, Any]] = [
|
|
|
3263
3263
|
"across lines before believing it. This server's own "
|
|
3264
3264
|
"`dctl validate` is a static, offline check (entry point, "
|
|
3265
3265
|
"brace balance, float suffixes) and does not call "
|
|
3266
|
-
"ValidateDCTL at all."
|
|
3266
|
+
"ValidateDCTL at all. The separate `dctl validate_native` and "
|
|
3267
|
+
"granular `validate_dctl_native` now expose the native "
|
|
3268
|
+
"validator without changing source or diagnostics. Both "
|
|
3269
|
+
"interfaces were contributor-validated against the three "
|
|
3270
|
+
"fixtures on Studio 21.1.0.14; see "
|
|
3271
|
+
"resolve211-dctl-validation.md.",
|
|
3267
3272
|
"tags": ["dctl", "validation", "unreliable-return", "version-gated", "reported"],
|
|
3268
3273
|
"submit": "bug",
|
|
3269
3274
|
},
|
|
@@ -275,6 +275,7 @@ class RiskClassificationHook(LifecycleHook):
|
|
|
275
275
|
# RippleTimeline true, moves every clip after it; `set_fades` rewrites
|
|
276
276
|
# how the clip's edges render. Existing content altered, not deleted.
|
|
277
277
|
("media_pool", "create_multicam_clip"),
|
|
278
|
+
("timeline", "auto_align_clips"),
|
|
278
279
|
("timeline_item", "flatten_multicam"),
|
|
279
280
|
("timeline", "set_output_blanking"),
|
|
280
281
|
("timeline_item", "set_output_blanking"),
|
|
@@ -403,7 +404,7 @@ class RiskClassificationHook(LifecycleHook):
|
|
|
403
404
|
else BlastRadius.ITEM
|
|
404
405
|
)
|
|
405
406
|
reasons.append(f"Recoverable edit to existing state: {action}")
|
|
406
|
-
elif any(action.startswith(p) for p in cls._READ_ONLY_PREFIXES) or action in {"read", "status", "info"}:
|
|
407
|
+
elif any(action.startswith(p) for p in cls._READ_ONLY_PREFIXES) or action in {"read", "status", "info"} or pair == ("dctl", "validate_native"):
|
|
407
408
|
level = RiskLevel.LOW
|
|
408
409
|
destructive = False
|
|
409
410
|
radius = BlastRadius.ITEM
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Native alignment with strict ID resolution before any timeline mutation."""
|
|
2
|
+
from src.utils.resolve211_multicam import resolve_constant
|
|
3
|
+
|
|
4
|
+
OPTIONS = {
|
|
5
|
+
'SyncUsing': ('AUTO_ALIGN_CLIPS_USING_TIMECODE','AUTO_ALIGN_CLIPS_USING_WAVEFORM'),
|
|
6
|
+
'UseTrack': ('AUTO_ALIGN_CLIPS_WAVEFORM_TRACK_MIX','AUTO_ALIGN_CLIPS_WAVEFORM_TRACK_AUTOMATIC'),
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def auto_align(r, timeline, item_ids, options):
|
|
11
|
+
if not isinstance(item_ids,list) or not item_ids or any(not isinstance(i,str) or not i for i in item_ids):
|
|
12
|
+
return {'error':'item_ids must be a non-empty list of timeline item unique IDs'}
|
|
13
|
+
if len(set(item_ids))!=len(item_ids):
|
|
14
|
+
return {'error':'item_ids must not contain duplicates'}
|
|
15
|
+
if not isinstance(options,dict) or set(options)-set(OPTIONS):
|
|
16
|
+
return {'error':'options must contain only SyncUsing and/or UseTrack'}
|
|
17
|
+
normalized={}
|
|
18
|
+
for key,value in options.items():
|
|
19
|
+
normalized[key],error=resolve_constant(r,value,OPTIONS[key])
|
|
20
|
+
if error:
|
|
21
|
+
return {'error':key+': '+error}
|
|
22
|
+
wanted=set(item_ids)
|
|
23
|
+
found={}
|
|
24
|
+
for track_type in ('video','audio'):
|
|
25
|
+
for index in range(1,timeline.GetTrackCount(track_type)+1):
|
|
26
|
+
for item in timeline.GetItemListInTrack(track_type,index) or []:
|
|
27
|
+
uid=item.GetUniqueId()
|
|
28
|
+
if uid in wanted:
|
|
29
|
+
found[uid]=item
|
|
30
|
+
if any(uid not in found for uid in item_ids):
|
|
31
|
+
return {'error':'One or more item_ids were not found in current video/audio tracks; no alignment performed'}
|
|
32
|
+
return {'success':bool(timeline.AutoAlignClips([found[uid] for uid in item_ids],normalized))}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Native DCTL diagnostics are evidence; never reformat the user's shader source."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def native_dctl_result(resolve, source):
|
|
5
|
+
diagnostic=resolve.ValidateDCTL(source)
|
|
6
|
+
if diagnostic is not None and not isinstance(diagnostic,str):
|
|
7
|
+
return {'error':'ValidateDCTL returned an unexpected diagnostic type'}
|
|
8
|
+
return {'valid':diagnostic is None,'diagnostic':diagnostic,'checker':'resolve_native'}
|
|
@@ -225,6 +225,8 @@ _EVIDENCE_GATES: List[Dict[str, Any]] = [
|
|
|
225
225
|
# disagree, which is what keeps the two from drifting apart again.
|
|
226
226
|
|
|
227
227
|
CODE_FLOORS: Dict[str, str] = {
|
|
228
|
+
"Timeline.AutoAlignClips": "21.1",
|
|
229
|
+
"Resolve.ValidateDCTL": "21.1",
|
|
228
230
|
"MediaPool.CreateMulticamClip": "21.1",
|
|
229
231
|
"TimelineItem.FlattenMulticam": "21.1",
|
|
230
232
|
"Timeline.SetOutputBlanking": "21.1",
|