davinci-resolve-mcp 2.219.0 → 2.220.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 +50 -0
- package/README.md +4 -4
- package/README.zh-CN.md +5 -5
- package/docs/SKILL.md +2 -1
- package/docs/contributing.md +1 -1
- package/docs/install.md +2 -2
- package/docs/reference/api-coverage.md +2 -1
- package/docs/reference/readwrite-symmetry.md +2 -2
- package/docs/reference/resolve211-blanking.md +55 -0
- package/install.py +2 -2
- package/package.json +1 -1
- package/src/granular/common.py +1 -1
- package/src/granular/resolve_211.py +49 -0
- package/src/resolve_mcp_server.py +1 -1
- package/src/server.py +36 -6
- package/src/utils/destructive_hook.py +3 -0
- package/src/utils/execution_lifecycle.py +3 -0
- package/src/utils/resolve211_blanking.py +17 -0
- package/src/utils/resolve_versions.py +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
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.220.0 — native Resolve 21.1 output blanking, timeline and clip
|
|
6
|
+
|
|
7
|
+
Contributed by @legionsound (#213), live-validated on Studio 21.1.0.14.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **`timeline set_output_blanking`** and **`timeline_item set_output_blanking`**,
|
|
12
|
+
with granular twins `set_timeline_output_blanking` and
|
|
13
|
+
`set_timeline_item_output_blanking`, calling native 21.1 `SetOutputBlanking`.
|
|
14
|
+
`Top`/`Bottom`/`Left`/`Right` are **native pixel coordinates, not independent
|
|
15
|
+
margin widths** — the wrapper says so rather than letting the names imply
|
|
16
|
+
otherwise. Whole-valued native floats round-trip without conversion,
|
|
17
|
+
malformed values and unknown keys are refused, and no undocumented geometry
|
|
18
|
+
limits are invented.
|
|
19
|
+
- **`timeline_item set_use_timeline_for_output_blanking`**, with the granular
|
|
20
|
+
twin `set_timeline_item_use_timeline_for_output_blanking`, as an explicit
|
|
21
|
+
inheritance switch.
|
|
22
|
+
- **A clip override does not silently disable timeline inheritance.** Measured:
|
|
23
|
+
a clip write while inheritance was on returned false without changing
|
|
24
|
+
inheritance, and succeeded only after an explicit disable. The wrapper
|
|
25
|
+
preserves that native false instead of quietly flipping the switch on the
|
|
26
|
+
caller's behalf — a clip that stops inheriting is a change nobody asked for.
|
|
27
|
+
- All three actions carry method floors, entries in **both** write-risk tables,
|
|
28
|
+
destructive granular annotations and dry-run refusal tests. Tool count 370 →
|
|
29
|
+
373.
|
|
30
|
+
|
|
31
|
+
### Validation
|
|
32
|
+
|
|
33
|
+
- Full suite green: 3,457 passed, 1 skipped. All six mutating actions added
|
|
34
|
+
across #209, #211 and #213 probed directly against the classifier and the
|
|
35
|
+
destructive registry — every one MEDIUM / destructive / recognised in both.
|
|
36
|
+
- Live evidence is @legionsound's on Studio 21.1.0.14: both interfaces exported
|
|
37
|
+
synthetic red-clip PNGs with exact lit-pixel bounds — full `0/0/640/360`,
|
|
38
|
+
timeline `64/36/576/324`, clip `128/72/512/288`, restored inheritance
|
|
39
|
+
`64/36/576/324` — and a native partial write of `Top: 80.0` updated one
|
|
40
|
+
coordinate while preserving the others on readback. Recorded as sampled-frame
|
|
41
|
+
and readback results, **not** whole-movie or out-of-range behaviour claims.
|
|
42
|
+
Not reproduced here; this machine is Studio 19.1.3.7, below the 21.1 floor.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- Adapted on merge. The branch was cut before #211, so its tool-count bump
|
|
47
|
+
(368 → 371) and every generated agent-rule file conflicted with the 370 that
|
|
48
|
+
multicam had landed. Counts were resolved to **373** — confirmed
|
|
49
|
+
independently by the agent-rule generator rather than by arithmetic alone —
|
|
50
|
+
and the generated files were regenerated instead of hand-merged, which is
|
|
51
|
+
the only resolution that cannot silently disagree with its source. Both
|
|
52
|
+
documentation pointers were kept. No behaviour was changed in the
|
|
53
|
+
adaptation.
|
|
54
|
+
|
|
5
55
|
## What's New in v2.219.0 — native Resolve 21.1 multicam creation and flattening
|
|
6
56
|
|
|
7
57
|
Contributed by @legionsound (#211), 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` | 373 | 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 / **373** 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.220.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` | 373 | 想要"一个 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** 复合 / **373** 细粒度(实时服务器) |
|
|
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` | 373 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
|
|
@@ -2485,3 +2485,4 @@ Native Resolve 21.1 speed and fade setters: see [speed/fades](reference/resolve2
|
|
|
2485
2485
|
Native 21.1 transition creation: see [transition controls](reference/resolve211-native-transitions.md) for options, item-index changes and contributor-rendered evidence.
|
|
2486
2486
|
|
|
2487
2487
|
Native multicam creation and flattening: [21.1 controls](reference/resolve211-multicam.md), with contributor render evidence and remaining family coverage.
|
|
2488
|
+
Native timeline/clip output blanking: [21.1 controls](reference/resolve211-blanking.md), with explicit inheritance and pixel-bound evidence.
|
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 — 373 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` | 373 | 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 373-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 **373 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
|
|
|
@@ -635,3 +635,4 @@ Native speed and fade setters in both interfaces are documented in [Resolve 21.1
|
|
|
635
635
|
Native 21.1 transition creation: see [transition controls](resolve211-native-transitions.md) for options, item-index changes and contributor-rendered evidence.
|
|
636
636
|
|
|
637
637
|
Native multicam creation and flattening: [21.1 controls](resolve211-multicam.md), with contributor render evidence and remaining family coverage.
|
|
638
|
+
Native timeline/clip output blanking: [21.1 controls](resolve211-blanking.md), with explicit inheritance and pixel-bound evidence.
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
# Read/Write Symmetry Audit
|
|
4
4
|
|
|
5
|
-
- write-style action occurrences scanned: **
|
|
6
|
-
- write-style action occurrences with a matching read: **
|
|
5
|
+
- write-style action occurrences scanned: **124**
|
|
6
|
+
- write-style action occurrences with a matching read: **76**
|
|
7
7
|
- distinct high-signal `set_` actions without a direct/known readback: **4**
|
|
8
8
|
|
|
9
9
|
## High-signal gaps — `set_` with no direct/known readback
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Resolve 21.1 output blanking
|
|
2
|
+
|
|
3
|
+
The native setters are exposed in both interfaces:
|
|
4
|
+
|
|
5
|
+
| Compound tool/action | Granular tool |
|
|
6
|
+
|---|---|
|
|
7
|
+
| timeline set_output_blanking | set_timeline_output_blanking |
|
|
8
|
+
| timeline_item set_output_blanking | set_timeline_item_output_blanking |
|
|
9
|
+
| timeline_item set_use_timeline_for_output_blanking | set_timeline_item_use_timeline_for_output_blanking |
|
|
10
|
+
|
|
11
|
+
Blanking setters take `options` containing one or more Top/Bottom/Left/Right
|
|
12
|
+
pixel coordinates. These are bounds, not four independent margin widths. For a
|
|
13
|
+
640x360 frame, full picture is Top0/Bottom360/Left0/Right640. Whole-valued floats
|
|
14
|
+
from native getter results are accepted; fractional/non-finite values, booleans,
|
|
15
|
+
unknown keys and empty dictionaries are refused. No undocumented geometry bounds
|
|
16
|
+
are invented, and partial dictionaries are forwarded unchanged to Resolve.
|
|
17
|
+
|
|
18
|
+
The inheritance setter requires a strict `use_timeline` boolean. To set a clip
|
|
19
|
+
override, disable inheritance explicitly first. On the measured build, setting
|
|
20
|
+
clip blanking while inheritance was enabled returned false and changed nothing.
|
|
21
|
+
The wrapper preserves that failure and never silently changes inheritance.
|
|
22
|
+
Re-enabling inheritance returns the clip to the timeline's blanking. Native
|
|
23
|
+
getter payloads may be empty when the clip inherits timeline blanking.
|
|
24
|
+
|
|
25
|
+
All three actions have 21.1 method floors, registered destructive MEDIUM-risk
|
|
26
|
+
writes in both classifier tables, and destructive granular annotations. Explicit
|
|
27
|
+
compound dry-run requests are refused before writes. Native failure is preserved
|
|
28
|
+
as success:false. Clip locators use the existing 1-based tracks/0-based item
|
|
29
|
+
indexes.
|
|
30
|
+
|
|
31
|
+
## Contributor validation
|
|
32
|
+
|
|
33
|
+
Contributor-validated on macOS Studio 21.1.0.14 using a generated solid red clip
|
|
34
|
+
in a disposable 640x360 project, through both actual community interfaces.
|
|
35
|
+
Resolve-exported PNGs showed exact lit-pixel bounds:
|
|
36
|
+
|
|
37
|
+
- Full picture: left0/top0/right640/bottom360.
|
|
38
|
+
- Timeline blanking: left64/top36/right576/bottom324.
|
|
39
|
+
- Clip override: left128/top72/right512/bottom288.
|
|
40
|
+
- Inheritance restored: timeline bounds64/36/576/324 again.
|
|
41
|
+
|
|
42
|
+
The override frame was visually inspected; both interfaces returned the same
|
|
43
|
+
bounds. Inherited clip-set refusal was also verified live. This is sampled-frame
|
|
44
|
+
and readback evidence, not whole-movie verification, or proof of
|
|
45
|
+
negative-coordinate, out-of-frame or audio-item behavior. A native-only partial
|
|
46
|
+
update with Top80.0 also succeeded and preserved Bottom/Left/Right in readback;
|
|
47
|
+
that extra case was not image-verified. Those cases are not
|
|
48
|
+
claimed tested.
|
|
49
|
+
|
|
50
|
+
`tests/live_resolve211_blanking.py OUTPUT_DIR` requires the disposable project
|
|
51
|
+
Codex Blanking Validation 20260909 with one synthetic red.mov video clip, at
|
|
52
|
+
640x360/24 fps with timeline start00:00:00:00. Generate red.mov using the color
|
|
53
|
+
fixture in resolve211-native-transitions.md. The script invokes both wrappers,
|
|
54
|
+
exports comparison PNGs and restores full timeline blanking/inheritance before
|
|
55
|
+
saving the scratch project. Never substitute production media.
|
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.220.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 · 373 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.220.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,5 @@
|
|
|
1
1
|
"""Native Resolve 21.1 discovery and editing controls."""
|
|
2
|
+
from src.utils.resolve211_blanking import validate_blanking
|
|
2
3
|
from src.utils.resolve211_multicam import create_multicam, resolve_constant, GRADES
|
|
3
4
|
from src.utils.resolve211_edits import validate_edit_options, validate_transition_options, transition_result
|
|
4
5
|
from src.granular.common import (
|
|
@@ -229,3 +230,51 @@ def flatten_timeline_item_multicam(grade_option: str = "FLATTEN_MULTICAM_COPY_GR
|
|
|
229
230
|
if error:
|
|
230
231
|
return {"error": error}
|
|
231
232
|
return {"success": bool(item.FlattenMulticam(grade))}
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@mcp.tool(annotations=DESTRUCTIVE_TOOL)
|
|
236
|
+
def set_timeline_output_blanking(options: dict) -> dict:
|
|
237
|
+
"""Set native 21.1 timeline Top/Bottom/Left/Right pixel coordinates."""
|
|
238
|
+
error = validate_blanking(options)
|
|
239
|
+
if error:
|
|
240
|
+
return {"error": error}
|
|
241
|
+
_, tl, error = _get_timeline()
|
|
242
|
+
if error:
|
|
243
|
+
return error
|
|
244
|
+
missing = _requires_method(tl, "SetOutputBlanking", "21.1")
|
|
245
|
+
if missing:
|
|
246
|
+
return missing
|
|
247
|
+
return {"success": bool(tl.SetOutputBlanking(dict(options)))}
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
@mcp.tool(annotations=DESTRUCTIVE_TOOL)
|
|
251
|
+
def set_timeline_item_output_blanking(options: dict, track_type: str = "video", track_index: int = 1, item_index: int = 0) -> dict:
|
|
252
|
+
"""Set native 21.1 clip pixel coordinates. Disable timeline blanking inheritance first; this call does not change inheritance."""
|
|
253
|
+
error = validate_blanking(options)
|
|
254
|
+
if error:
|
|
255
|
+
return {"error": error}
|
|
256
|
+
if track_type not in ("video", "audio") or track_index < 1 or item_index < 0:
|
|
257
|
+
return {"error": "Use video/audio, a 1-based track index and a non-negative item index"}
|
|
258
|
+
item, error = _get_timeline_item(track_type, track_index, item_index)
|
|
259
|
+
if error:
|
|
260
|
+
return error
|
|
261
|
+
missing = _requires_method(item, "SetOutputBlanking", "21.1")
|
|
262
|
+
if missing:
|
|
263
|
+
return missing
|
|
264
|
+
return {"success": bool(item.SetOutputBlanking(dict(options)))}
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
@mcp.tool(annotations=DESTRUCTIVE_TOOL)
|
|
268
|
+
def set_timeline_item_use_timeline_for_output_blanking(use_timeline: bool, track_type: str = "video", track_index: int = 1, item_index: int = 0) -> dict:
|
|
269
|
+
"""Explicitly enable/disable native 21.1 timeline blanking inheritance for a clip."""
|
|
270
|
+
if type(use_timeline) is not bool:
|
|
271
|
+
return {"error": "use_timeline must be a boolean"}
|
|
272
|
+
if track_type not in ("video", "audio") or track_index < 1 or item_index < 0:
|
|
273
|
+
return {"error": "Use video/audio, a 1-based track index and a non-negative item index"}
|
|
274
|
+
item, error = _get_timeline_item(track_type, track_index, item_index)
|
|
275
|
+
if error:
|
|
276
|
+
return error
|
|
277
|
+
missing = _requires_method(item, "SetUseTimelineForOutputBlanking", "21.1")
|
|
278
|
+
if missing:
|
|
279
|
+
return missing
|
|
280
|
+
return {"success": bool(item.SetUseTimelineForOutputBlanking(use_timeline))}
|
|
@@ -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} (373 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 373-tool granular server instead
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
VERSION = "2.
|
|
14
|
+
VERSION = "2.220.0"
|
|
15
15
|
|
|
16
16
|
import base64
|
|
17
17
|
import os
|
|
@@ -43,6 +43,7 @@ for p in [current_dir, project_dir]:
|
|
|
43
43
|
sys.path.insert(0, p)
|
|
44
44
|
|
|
45
45
|
from src.utils.resolve211_multicam import create_multicam, resolve_constant, GRADES
|
|
46
|
+
from src.utils.resolve211_blanking import validate_blanking
|
|
46
47
|
from src.utils.resolve211_edits import validate_edit_options, validate_transition_options, transition_result
|
|
47
48
|
|
|
48
49
|
# Platform-specific Resolve paths
|
|
@@ -25052,7 +25053,7 @@ def edit_engine(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[st
|
|
|
25052
25053
|
|
|
25053
25054
|
|
|
25054
25055
|
_TIMELINE_ACTIONS = [
|
|
25055
|
-
"get_normalize_audio_modes", "get_output_blanking",
|
|
25056
|
+
"set_output_blanking", "get_normalize_audio_modes", "get_output_blanking",
|
|
25056
25057
|
# Offline authoring — served without a Resolve connection, above the _check() gate.
|
|
25057
25058
|
"author_offline", "offline_fallback_capabilities",
|
|
25058
25059
|
"list", "get_current", "set_current", "get_name", "set_name", "get_start_frame",
|
|
@@ -25101,6 +25102,7 @@ def timeline(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str,
|
|
|
25101
25102
|
(resolve_control api_truth "GetSourceStartFrame").
|
|
25102
25103
|
|
|
25103
25104
|
Actions:
|
|
25105
|
+
set_output_blanking(options) -> {success} — native 21.1 pixel coordinates.
|
|
25104
25106
|
get_normalize_audio_modes() -> {modes} — documented on Resolve 21.1+.
|
|
25105
25107
|
get_output_blanking() -> {blanking} — documented on Resolve 21.1+. Pixel coordinates; empty on a clip inheriting timeline blanking.
|
|
25106
25108
|
list() -> {timelines}
|
|
@@ -25419,6 +25421,15 @@ def timeline(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str,
|
|
|
25419
25421
|
if not tl:
|
|
25420
25422
|
return _err("No current timeline")
|
|
25421
25423
|
|
|
25424
|
+
if action == "set_output_blanking":
|
|
25425
|
+
error = validate_blanking(p.get("options"))
|
|
25426
|
+
if error:
|
|
25427
|
+
return _err(error)
|
|
25428
|
+
missing = _requires_method(tl, "SetOutputBlanking", "21.1")
|
|
25429
|
+
if missing:
|
|
25430
|
+
return missing
|
|
25431
|
+
return {"success": bool(tl.SetOutputBlanking(dict(p["options"])))}
|
|
25432
|
+
|
|
25422
25433
|
if action == "get_normalize_audio_modes":
|
|
25423
25434
|
missing = _requires_method(tl, "GetNormalizeAudioModes", "21.1")
|
|
25424
25435
|
if missing:
|
|
@@ -26282,6 +26293,8 @@ def timeline_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[
|
|
|
26282
26293
|
|
|
26283
26294
|
Actions:
|
|
26284
26295
|
flatten_multicam(grade_option?, ...) -> {success} — native 21.1, replaces multicam with its current angle.
|
|
26296
|
+
set_output_blanking(options, ...) -> {success} — native 21.1; disable timeline inheritance first.
|
|
26297
|
+
set_use_timeline_for_output_blanking(use_timeline, ...) -> {success} — explicit inheritance switch.
|
|
26285
26298
|
add_transition(options, ...) -> {success, transition?} — native 21.1 transition; reports actual span.
|
|
26286
26299
|
set_speed(options, ...) -> {success} — native 21.1 speed options; RippleTimeline defaults false.
|
|
26287
26300
|
set_fades(options, ...) -> {success} — native 21.1 FadeIn/FadeOut integer frames.
|
|
@@ -26348,6 +26361,23 @@ def timeline_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[
|
|
|
26348
26361
|
return _err(error)
|
|
26349
26362
|
return {"success": bool(item.FlattenMulticam(grade))}
|
|
26350
26363
|
|
|
26364
|
+
if action == "set_output_blanking":
|
|
26365
|
+
error = validate_blanking(p.get("options"))
|
|
26366
|
+
if error:
|
|
26367
|
+
return _err(error)
|
|
26368
|
+
missing = _requires_method(item, "SetOutputBlanking", "21.1")
|
|
26369
|
+
if missing:
|
|
26370
|
+
return missing
|
|
26371
|
+
return {"success": bool(item.SetOutputBlanking(dict(p["options"])))}
|
|
26372
|
+
|
|
26373
|
+
if action == "set_use_timeline_for_output_blanking":
|
|
26374
|
+
if type(p.get("use_timeline")) is not bool:
|
|
26375
|
+
return _err("use_timeline must be a boolean")
|
|
26376
|
+
missing = _requires_method(item, "SetUseTimelineForOutputBlanking", "21.1")
|
|
26377
|
+
if missing:
|
|
26378
|
+
return missing
|
|
26379
|
+
return {"success": bool(item.SetUseTimelineForOutputBlanking(p["use_timeline"]))}
|
|
26380
|
+
|
|
26351
26381
|
if action == "add_transition":
|
|
26352
26382
|
options = p.get("options")
|
|
26353
26383
|
error = validate_transition_options(options)
|
|
@@ -26569,7 +26599,7 @@ def timeline_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[
|
|
|
26569
26599
|
return _err(f"Invalid interpolation. Must be one of: {', '.join(valid)}")
|
|
26570
26600
|
return {"success": bool(item.SetKeyframeInterpolation(p["property"], p["frame"], p["interpolation"]))}
|
|
26571
26601
|
|
|
26572
|
-
return _unknown(action, ["flatten_multicam","add_transition","set_speed","set_fades","get_speed","get_fades","get_output_blanking","get_use_timeline_for_output_blanking","get_name","get_property","set_property","get_duration","get_start","get_end","get_source_start_frame","get_source_end_frame","get_source_start_time","get_source_end_time","get_left_offset","get_right_offset","set_clip_enabled","get_clip_enabled","update_sidecar","get_unique_id","get_media_pool_item","get_stereo_convergence","get_stereo_left_window","get_stereo_right_window","get_linked_items","get_track_type_and_index","get_source_audio_mapping","load_burnin_preset","set_name","get_voice_isolation_state","set_voice_isolation_state","get_retime","set_retime","get_transform","set_transform","get_crop","set_crop","get_composite","set_composite","get_audio","set_audio","get_keyframes","add_keyframe","modify_keyframe","delete_keyframe","set_keyframe_interpolation"])
|
|
26602
|
+
return _unknown(action, ["set_output_blanking","set_use_timeline_for_output_blanking","flatten_multicam","add_transition","set_speed","set_fades","get_speed","get_fades","get_output_blanking","get_use_timeline_for_output_blanking","get_name","get_property","set_property","get_duration","get_start","get_end","get_source_start_frame","get_source_end_frame","get_source_start_time","get_source_end_time","get_left_offset","get_right_offset","set_clip_enabled","get_clip_enabled","update_sidecar","get_unique_id","get_media_pool_item","get_stereo_convergence","get_stereo_left_window","get_stereo_right_window","get_linked_items","get_track_type_and_index","get_source_audio_mapping","load_burnin_preset","set_name","get_voice_isolation_state","set_voice_isolation_state","get_retime","set_retime","get_transform","set_transform","get_crop","set_crop","get_composite","set_composite","get_audio","set_audio","get_keyframes","add_keyframe","modify_keyframe","delete_keyframe","set_keyframe_interpolation"])
|
|
26573
26603
|
|
|
26574
26604
|
|
|
26575
26605
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -32691,9 +32721,9 @@ if __name__ == "__main__":
|
|
|
32691
32721
|
start_background_update_check(VERSION, project_dir, logger, env=_setup_update_env())
|
|
32692
32722
|
_install_threaded_tool_dispatch(mcp)
|
|
32693
32723
|
|
|
32694
|
-
# Support --full flag to run the
|
|
32724
|
+
# Support --full flag to run the 373-tool granular server instead
|
|
32695
32725
|
if "--full" in sys.argv:
|
|
32696
|
-
logger.info("Starting full
|
|
32726
|
+
logger.info("Starting full 373-tool granular server...")
|
|
32697
32727
|
sys.argv = [arg for arg in sys.argv if arg != "--full"]
|
|
32698
32728
|
from src.granular import mcp as granular_mcp
|
|
32699
32729
|
|
|
@@ -87,6 +87,7 @@ DESTRUCTIVE_ACTIONS_BY_TOOL: Dict[str, FrozenSet[str]] = {
|
|
|
87
87
|
"execute_swap",
|
|
88
88
|
}),
|
|
89
89
|
"timeline": frozenset({
|
|
90
|
+
"set_output_blanking",
|
|
90
91
|
"delete_clips",
|
|
91
92
|
"move_clips",
|
|
92
93
|
"duplicate_clips",
|
|
@@ -138,6 +139,8 @@ DESTRUCTIVE_ACTIONS_BY_TOOL: Dict[str, FrozenSet[str]] = {
|
|
|
138
139
|
"create_subtitles",
|
|
139
140
|
}),
|
|
140
141
|
"timeline_item": frozenset({
|
|
142
|
+
"set_output_blanking",
|
|
143
|
+
"set_use_timeline_for_output_blanking",
|
|
141
144
|
"set_clip_enabled",
|
|
142
145
|
"set_property",
|
|
143
146
|
"set_name",
|
|
@@ -276,6 +276,9 @@ class RiskClassificationHook(LifecycleHook):
|
|
|
276
276
|
# how the clip's edges render. Existing content altered, not deleted.
|
|
277
277
|
("media_pool", "create_multicam_clip"),
|
|
278
278
|
("timeline_item", "flatten_multicam"),
|
|
279
|
+
("timeline", "set_output_blanking"),
|
|
280
|
+
("timeline_item", "set_output_blanking"),
|
|
281
|
+
("timeline_item", "set_use_timeline_for_output_blanking"),
|
|
279
282
|
("timeline_item", "add_transition"),
|
|
280
283
|
("timeline_item", "set_speed"),
|
|
281
284
|
("timeline_item", "set_fades"),
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Preserve native pixel coordinates without inventing undocumented geometry limits."""
|
|
2
|
+
import math
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def validate_blanking(options):
|
|
6
|
+
if not isinstance(options,dict) or not options:
|
|
7
|
+
return 'options must be a non-empty dictionary'
|
|
8
|
+
if set(options)-{'Top','Bottom','Left','Right'}:
|
|
9
|
+
return 'Only Top, Bottom, Left and Right are accepted'
|
|
10
|
+
for value in options.values():
|
|
11
|
+
try:
|
|
12
|
+
valid = type(value) in (int,float) and math.isfinite(value) and int(value)==value
|
|
13
|
+
except OverflowError:
|
|
14
|
+
valid = False
|
|
15
|
+
if not valid:
|
|
16
|
+
return 'Blanking values must be finite whole-pixel coordinates'
|
|
17
|
+
return None
|
|
@@ -227,6 +227,9 @@ _EVIDENCE_GATES: List[Dict[str, Any]] = [
|
|
|
227
227
|
CODE_FLOORS: Dict[str, str] = {
|
|
228
228
|
"MediaPool.CreateMulticamClip": "21.1",
|
|
229
229
|
"TimelineItem.FlattenMulticam": "21.1",
|
|
230
|
+
"Timeline.SetOutputBlanking": "21.1",
|
|
231
|
+
"TimelineItem.SetOutputBlanking": "21.1",
|
|
232
|
+
"TimelineItem.SetUseTimelineForOutputBlanking": "21.1",
|
|
230
233
|
"TimelineItem.AddTransition": "21.1",
|
|
231
234
|
"TimelineItem.SetSpeed": "21.1",
|
|
232
235
|
"TimelineItem.SetFades": "21.1",
|