davinci-resolve-mcp 2.98.8 → 2.99.1
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 +117 -0
- package/README.md +2 -2
- package/README.zh-CN.md +3 -3
- package/docs/SKILL.md +47 -5
- package/docs/reference/api-limitations.md +28 -4
- package/install.py +1 -1
- package/package.json +1 -1
- package/src/granular/common.py +1 -1
- package/src/granular/timeline_item.py +20 -3
- package/src/server.py +690 -38
- package/src/utils/api_truth.py +86 -3
- package/src/utils/destructive_hook.py +28 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,123 @@
|
|
|
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.99.1
|
|
6
|
+
|
|
7
|
+
**`bulk_set_item_properties` could not set a clip colour on its own.** Reported
|
|
8
|
+
and fixed in [#157](https://github.com/samuelgursky/davinci-resolve-mcp/pull/157)
|
|
9
|
+
by @matoberuc-afk.
|
|
10
|
+
|
|
11
|
+
The action documents `clip_color` and `enabled` as per-op keys and has code to
|
|
12
|
+
apply both — but that code was unreachable for the op shape that needs it most.
|
|
13
|
+
The payload is built by `_merge_property_groups`, which merges only
|
|
14
|
+
`properties`/`transform`/`crop`/`composite`/`audio` and the duplicate-keyframe
|
|
15
|
+
keys. `clip_color` and `enabled` are not `SetProperty` keys, so they never landed
|
|
16
|
+
in that dict, and the `if not properties: continue` guard above returned early —
|
|
17
|
+
leaving the `clip_color` branch twenty-five lines below dead on exactly the ops
|
|
18
|
+
that carry no transform. Colour triage in one round trip is the main reason to
|
|
19
|
+
call a *bulk* setter, and it was the one shape that could not work.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **A colour-only or enabled-only op is now accepted** and applied.
|
|
24
|
+
- **A colour-only op could not fail.** Per-op success was
|
|
25
|
+
`all(row.get("success") for row in ...properties.values())`, and `all([])` is
|
|
26
|
+
`True` — with no property rows the op passed regardless of what `SetClipColor`
|
|
27
|
+
returned. Every branch that runs now votes.
|
|
28
|
+
- **The bulk path trusted the bare bool.** It called `item.SetClipColor` directly,
|
|
29
|
+
bypassing `_set_clip_color_checked` — the helper added for
|
|
30
|
+
[#124](https://github.com/samuelgursky/davinci-resolve-mcp/issues/124) that the
|
|
31
|
+
single-item path already used, because that bool lies twice: a name outside the
|
|
32
|
+
16-name Edit-page palette is refused with a bare `False`, and a generator or
|
|
33
|
+
title takes the call, returns `True`, and drops the colour. A failure now
|
|
34
|
+
carries `clip_color_detail`.
|
|
35
|
+
- `dry_run` reports `would_set_clip_color` / `would_set_enabled`, and the
|
|
36
|
+
`action_help` example shows the triage shape instead of a `properties`
|
|
37
|
+
dict with a `ClipColor` key that was never a valid `SetProperty` target.
|
|
38
|
+
|
|
39
|
+
### Live validation
|
|
40
|
+
|
|
41
|
+
Studio 19.1.3.7: three colour-only ops in one call, live readback
|
|
42
|
+
`['Apricot', 'Chocolate', 'Purple']` on the timeline items; a refused colour
|
|
43
|
+
returns `success: false` instead of passing on the empty-list vote.
|
|
44
|
+
|
|
45
|
+
## What's New in v2.99.0
|
|
46
|
+
|
|
47
|
+
**`timeline.ripple_insert`, and a verified-delete gate on `move_clips`.**
|
|
48
|
+
Contributed in [#156](https://github.com/samuelgursky/davinci-resolve-mcp/pull/156)
|
|
49
|
+
by @handst97, driven by a real data-loss incident: a session used `move_clips`
|
|
50
|
+
with a record offset smaller than the item duration to "open a gap",
|
|
51
|
+
`AppendToTimeline` returned items whose ids could not be read, the code counted
|
|
52
|
+
them as successful duplicates, and the delete phase removed 26 source clips.
|
|
53
|
+
|
|
54
|
+
### Added
|
|
55
|
+
|
|
56
|
+
- **`timeline.ripple_insert`** — insert media-pool source ranges at a record
|
|
57
|
+
point and shift all later video/audio items right. There is no ripple-insert
|
|
58
|
+
primitive in the scripting API, and duplicate-then-delete corrupts the timeline
|
|
59
|
+
when the shift is smaller than an item. This plans a rebuild instead: capture
|
|
60
|
+
every tail item's pool media and source trim, delete the tail (verified),
|
|
61
|
+
re-append it shifted, then place the inserts into the opened gap — **tail
|
|
62
|
+
first, so the worst mid-failure state is a gap, never lost content**. Dry-run
|
|
63
|
+
by default, with straddler / blocker / locked-track / subtitle detection;
|
|
64
|
+
executing is confirm-token gated and archives the timeline first. Shifted items
|
|
65
|
+
are re-created from pool media with transform/crop/composite/retime re-applied;
|
|
66
|
+
grades, keyframes, transitions and link state are NOT preserved (the archive
|
|
67
|
+
keeps them).
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
|
|
71
|
+
- **`move_clips` no longer deletes a source it could not verify.** Each duplicate
|
|
72
|
+
now carries `duplicate_verified` — a live item or a real id recovered from the
|
|
73
|
+
timeline — and sources are deleted only when every duplicate, primary and
|
|
74
|
+
linked, verified. Null-id appends keep the source with an explicit warning.
|
|
75
|
+
- **`safe_set_cdl` / `apply_look_to_items` preflight the node.** `NodeIndex` is
|
|
76
|
+
1-based and there is no `GetCDL`, so a bare `False` was undiagnosable; the node
|
|
77
|
+
count is now read before `SetCDL` and a failure comes back with a structured
|
|
78
|
+
reason and a clip-type-aware diagnosis.
|
|
79
|
+
- **Magic Mask reports the human step instead of a bare false.** Magic Mask v2
|
|
80
|
+
isolates via operator clicks and the API cannot place them, so `CreateMagicMask`
|
|
81
|
+
on a fresh item can only return `False`. It now returns `needs_hitl` with the
|
|
82
|
+
exact Color-page steps, and the mode aliases (`'Forward'` → `'F'`) are
|
|
83
|
+
normalized — the granular `ti_create_magic_mask` defaulted to a spelling
|
|
84
|
+
Resolve rejects.
|
|
85
|
+
|
|
86
|
+
### Fixed in review
|
|
87
|
+
|
|
88
|
+
Three defects found reviewing #156, each with a test that fails without the fix:
|
|
89
|
+
|
|
90
|
+
- **A dry-run plan archived a timeline version.** `ripple_insert` is the only
|
|
91
|
+
destructive action whose *default* call mutates nothing, and the pending-confirm
|
|
92
|
+
skip only fires while the confirm-token preference is on — so with it off,
|
|
93
|
+
routine planning calls littered the version chain.
|
|
94
|
+
- **Asymmetric per-track insert durations left an unreported gap.** Every track
|
|
95
|
+
shifts by the longest inserted run, so a shorter insert leaves a hole the
|
|
96
|
+
readback structurally cannot see — it only checks the positions it placed. The
|
|
97
|
+
plan and the result now carry `gap_frames_by_track` and a warning.
|
|
98
|
+
- **Subtitle straddlers passed the feasibility check.** Video and audio
|
|
99
|
+
straddlers already refuse the plan; a subtitle across the insert point stayed
|
|
100
|
+
put while the picture under it moved.
|
|
101
|
+
|
|
102
|
+
### Live validation
|
|
103
|
+
|
|
104
|
+
Verified on Studio 19.1.3.7 via `tests/live_ripple_insert_validation.py`:
|
|
105
|
+
dry-run plan exact (insert@86448, shift 24, tail 2, no straddlers), confirm-token
|
|
106
|
+
round-trip, post-insert layout `[(86400,48),(86448,24),(86472,48),(86520,48)]`,
|
|
107
|
+
`readback.missing=[]`, and ZoomX/Y=0.5 surviving the shift with
|
|
108
|
+
`property_restore_failures=0`.
|
|
109
|
+
|
|
110
|
+
### A note on the Fusion measurement in #156
|
|
111
|
+
|
|
112
|
+
The PR proposed recording that whether an API-created Fusion comp renders is
|
|
113
|
+
Resolve-version-dependent, from a 21.0.4 run where a wired comp delivered a render
|
|
114
|
+
bit-identical to the baseline. That reading does not survive: those comps were
|
|
115
|
+
built and set through this server, and the v2.98.5 comp-lock bug produced exactly
|
|
116
|
+
that symptom on any build. Running the PR's own phase-2 harness unchanged against
|
|
117
|
+
the fixed code now reports **RENDERED** (Blur 24.38 dB, Transform 7.95 dB) where
|
|
118
|
+
it previously reported IGNORED. The `AddFusionComp` entry records the 21.0.4
|
|
119
|
+
observation as independent evidence that the lock bug is not specific to
|
|
120
|
+
19.1.3.7 — the one thing this machine cannot test.
|
|
121
|
+
|
|
5
122
|
## What's New in v2.98.8
|
|
6
123
|
|
|
7
124
|
**The comp-lock mechanism, settled — and the v2.98.6 scope correction was itself
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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
8
|
[-blue.svg)](#server-modes)
|
|
@@ -211,7 +211,7 @@ The open-source servers are complete and fully functional on their own.
|
|
|
211
211
|
| App and project control | Launch/reconnect, page switching, project CRUD, project folders, databases, cloud project wrappers, settings, presets, archives |
|
|
212
212
|
| Media pool and ingest | Safe import, image sequences, multicam prep timelines, bin organization, metadata normalization, metadata field inventory, marks, annotations, relink/proxy/full-resolution guards |
|
|
213
213
|
| Media analysis | Source-safe file/clip/bin/project analysis, 2-pop/slate-clap sync-event detection, default Resolve metadata and Media Pool marker writeback, persisted analysis artifacts, existing-report reuse, host_chat_paths visual analysis (finalized per clip with `commit_vision`, works with any vision-capable MCP client) with opt-out, transcription with opt-out |
|
|
214
|
-
| Timeline editing and conform | Track/item probing, title text key scans/writes, copy/move/duplicate helpers, range operations, gaps/overlaps, source ranges, checked interchange exports/imports |
|
|
214
|
+
| Timeline editing and conform | Track/item probing, title text key scans/writes, copy/move/duplicate helpers, ripple insert, range operations, gaps/overlaps, source ranges, checked interchange exports/imports |
|
|
215
215
|
| Review annotations | Timeline/item/clip markers, custom data, flags, clip color, copy/move/sync cleanup, review reports, marker thumbnail review |
|
|
216
216
|
| Color and grading | Node graph probing, CDL validation, grade copy, DRX/LUT helpers, versions, Gallery stills, color groups |
|
|
217
217
|
| Fusion | Timeline-item comps, safe tool creation, input writes, port inspection, validated connections, scoped bulk writes |
|
package/README.zh-CN.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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
8
|
[-blue.svg)](#服务器模式)
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
[](https://www.python.org/downloads/)
|
|
13
13
|
[](https://opensource.org/licenses/MIT)
|
|
14
14
|
|
|
15
|
-
> 本翻译对应 v2.
|
|
15
|
+
> 本翻译对应 v2.99.1 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
|
|
16
16
|
|
|
17
17
|
一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
|
|
18
18
|
|
|
@@ -142,7 +142,7 @@ DRX 调色写入**针对 Resolve Studio 做过实机校准**:调色参数默
|
|
|
142
142
|
| 应用与项目控制 | 启动/重连、页面切换、项目增删改查、项目文件夹、数据库、云项目封装、设置、预设、归档 |
|
|
143
143
|
| 媒体池与摄入 | 安全导入、图像序列、多机位预备时间线、bin 整理、元数据规范化、元数据字段清单、标记、注释、重链/代理/全分辨率护栏 |
|
|
144
144
|
| 媒体分析 | 源媒体安全的文件/片段/bin/项目分析,2-pop/场记板同步事件检测,默认写回 Resolve 元数据与媒体池标记,持久化分析产物,复用既有报告,host_chat_paths 视觉分析(每个片段以 `commit_vision` 定稿,任何具备视觉能力的 MCP 客户端都可用)可选退出,转写可选退出 |
|
|
145
|
-
| 时间线剪辑与套底 |
|
|
145
|
+
| 时间线剪辑与套底 | 轨道/条目探测、标题文字键扫描/写入、复制/移动/克隆助手、波纹插入、区间操作、缝隙/重叠、源范围、带检查的交换格式导入导出 |
|
|
146
146
|
| 审阅注释 | 时间线/条目/片段标记、自定义数据、旗标、片段颜色、复制/移动/同步清理、审阅报告、标记缩略图审阅 |
|
|
147
147
|
| 调色 | 节点图探测、CDL 校验、调色复制、DRX/LUT 助手、版本、Gallery 静帧、调色组 |
|
|
148
148
|
| Fusion | 时间线条目合成、安全工具创建、输入写入、端口检查、带校验的连接、限定范围的批量写入 |
|
package/docs/SKILL.md
CHANGED
|
@@ -444,7 +444,11 @@ same timecodes, then restore the previous active version or node-enabled state
|
|
|
444
444
|
after any temporary bypass capture. Treat untreated frames as diagnostic
|
|
445
445
|
evidence, not as permission to discard an existing creative grade.
|
|
446
446
|
|
|
447
|
-
Prefer `safe_set_cdl` for small reversible primary corrections.
|
|
447
|
+
Prefer `safe_set_cdl` for small reversible primary corrections. `SetCDL`'s
|
|
448
|
+
`NodeIndex` is 1-BASED (scripting README line 6) and there is no `GetCDL`
|
|
449
|
+
readback — `safe_set_cdl` and `apply_look_to_items` now read the node graph's
|
|
450
|
+
`GetNumNodes` first and return a structured reason/diagnosis on a false
|
|
451
|
+
`SetCDL` instead of a bare boolean. Use DRX/stills
|
|
448
452
|
or grade copy only when the user accepts whole-grade replacement/transfer
|
|
449
453
|
semantics. Use DCTL/LUT authoring only for reusable mathematical transforms, not
|
|
450
454
|
as a substitute for hand-built windows, qualifiers, or tracked secondaries. Do
|
|
@@ -1377,7 +1381,20 @@ Key actions:
|
|
|
1377
1381
|
unsupported because Resolve's public scripting API does not expose transition
|
|
1378
1382
|
cloning. `copy_keyframes=True` adds the `keyframes` group.
|
|
1379
1383
|
- `copy_clips(...)` / `move_clips(...)` — same safe append path; `move_clips`
|
|
1380
|
-
deletes
|
|
1384
|
+
deletes only sources whose duplicate was VERIFIED live on the timeline
|
|
1385
|
+
(AppendToTimeline can return null-id items — e.g. into an occupied span — and
|
|
1386
|
+
unverified sources are kept with a warning; see api_truth
|
|
1387
|
+
'AppendToTimeline null-id'). NEVER use `move_clips` to open a gap for an
|
|
1388
|
+
insert; that is `ripple_insert`'s job.
|
|
1389
|
+
- `ripple_insert(clip_infos, record_frame|record_timecode, record_frame_mode?,
|
|
1390
|
+
dry_run?, confirm_token?)` — insert media-pool source ranges at a record point
|
|
1391
|
+
and shift ALL later video/audio items right. DRY-RUN by default (full plan
|
|
1392
|
+
with straddler/blocker detection); executing is confirm-token gated and
|
|
1393
|
+
archives the timeline first. Shifted items are re-created from pool media
|
|
1394
|
+
with transform/crop/composite/retime re-applied; grades, keyframes,
|
|
1395
|
+
transitions, and link state on shifted items are NOT preserved (the archive
|
|
1396
|
+
keeps them). Refuses mid-item insert points, non-pool items in the tail
|
|
1397
|
+
(titles/generators/Fusion comps), subtitle shifts, and locked tracks.
|
|
1381
1398
|
- `copy_range` / `duplicate_range` — copy exact video/audio source segments
|
|
1382
1399
|
from `start_frame`/`end_frame` or mark in/out to `record_frame`
|
|
1383
1400
|
- `overwrite_range` — delete whole destination overlaps, then copy the exact
|
|
@@ -1393,7 +1410,11 @@ Key actions:
|
|
|
1393
1410
|
`track_index?` (default 1), so multicam angles can be rebuilt onto V2/V3
|
|
1394
1411
|
rather than collapsing onto V1; missing tracks are added
|
|
1395
1412
|
- `bulk_set_item_properties(ops, dry_run?, readback?)` — apply transforms,
|
|
1396
|
-
crop/composite/audio/property groups to many timeline items in one call
|
|
1413
|
+
crop/composite/audio/property groups to many timeline items in one call. An op
|
|
1414
|
+
may carry `clip_color` and/or `enabled` with nothing else, which is the triage
|
|
1415
|
+
shape: paint a whole selection in one round trip. A colour is verified by
|
|
1416
|
+
readback, so a name outside the Edit-page palette and the generator/title case
|
|
1417
|
+
that returns True and drops the colour both fail the op instead of passing
|
|
1397
1418
|
- `apply_look_to_items(target_ids, cdl?|copy_from_item_id?, dry_run?)` — apply a
|
|
1398
1419
|
normalized CDL and/or copy a source grade to multiple video items
|
|
1399
1420
|
- `thumbnail_contact_sheet` / `marker_thumbnail_review` — sample Resolve
|
|
@@ -1403,7 +1424,11 @@ Key actions:
|
|
|
1403
1424
|
page and only while it is frontmost; the tool switches page automatically and
|
|
1404
1425
|
restores the previous one. Expect a page flash in the GUI,
|
|
1405
1426
|
and note that landing on Color can kick off cache/render work for the current
|
|
1406
|
-
clip — on a large timeline the switch is not free
|
|
1427
|
+
clip — on a large timeline the switch is not free.
|
|
1428
|
+
NOT WYSIWYG for Fusion: thumbnails do not reflect Fusion composition output
|
|
1429
|
+
(a warp demo read as identical before/after from a contact sheet,
|
|
1430
|
+
2026-08-19). Prove Fusion/grade claims with `gallery_stills grab_and_export`
|
|
1431
|
+
or an extracted RENDERED frame, never a thumbnail
|
|
1407
1432
|
- `edit_kernel_capabilities` — report supported, partially supported, and
|
|
1408
1433
|
unsupported timeline edit kernel behavior
|
|
1409
1434
|
- `probe_edit_kernel_item(clip_ids? selected? timeline_item?)` — read-only
|
|
@@ -1602,7 +1627,12 @@ Key actions:
|
|
|
1602
1627
|
clip version
|
|
1603
1628
|
- `stabilize`, `smart_reframe`
|
|
1604
1629
|
- `create_magic_mask(mode)` — mode: `"F"` forward, `"B"` backward, `"BI"` bidirectional
|
|
1605
|
-
(requires DaVinci Neural Engine and Color page)
|
|
1630
|
+
(requires DaVinci Neural Engine and Color page). Magic Mask v2 isolates via
|
|
1631
|
+
operator CLICKS on the subject (manual ch. 139; strokes are legacy v1) and
|
|
1632
|
+
the API cannot place clicks — with none present this returns
|
|
1633
|
+
`{needs_hitl: true, hitl: {steps...}}` instead of a bare false. Never call it
|
|
1634
|
+
as if it isolates a subject unattended; prove any isolation with a rendered
|
|
1635
|
+
frame (`gallery_stills grab_and_export`).
|
|
1606
1636
|
|
|
1607
1637
|
Color / Grade kernel actions (v2.11.0+) add safer grade inspection and
|
|
1608
1638
|
boundary helpers: `grade_capabilities`, `probe_grade_item`,
|
|
@@ -2118,6 +2148,18 @@ The server provides several mechanisms to inspect a frame as Resolve has process
|
|
|
2118
2148
|
it, including color grading, effects, and compositing — not just the raw source
|
|
2119
2149
|
file.
|
|
2120
2150
|
|
|
2151
|
+
WYSIWYG hierarchy (live-verified 2026-08-20): a `grab_and_export` gallery still
|
|
2152
|
+
faithfully reflects edit sizing (Inspector transforms) and grades; media-pool
|
|
2153
|
+
thumbnails and `thumbnail_contact_sheet` output do NOT reflect Fusion
|
|
2154
|
+
composition output. Also note that whether an API-created Fusion comp is
|
|
2155
|
+
honoured at render is Resolve-version-dependent: a wired comp rendered on
|
|
2156
|
+
Studio 19.1.3.7, but on Studio 21.0.4 the same Blur configuration and a
|
|
2157
|
+
Transform variant both rendered bit-identical to the no-comp baseline, and no
|
|
2158
|
+
API selects an item's active composition (api_truth
|
|
2159
|
+
'AddFusionComp'). The only acceptable proof of a Fusion or grade claim is a
|
|
2160
|
+
rendered frame: `grab_and_export`, an exported gallery still, or a frame
|
|
2161
|
+
extracted from a delivered render.
|
|
2162
|
+
|
|
2121
2163
|
**Start here: `timeline_frame(action="capture")`** — Returns the frame at the
|
|
2122
2164
|
playhead (or at any `timecode`/`frame` you name) as MCP image content, so a
|
|
2123
2165
|
multimodal assistant can simply look at it. It renders that one frame, which is
|
|
@@ -12,7 +12,7 @@ that none exists).
|
|
|
12
12
|
|
|
13
13
|
**Verified on:** DaVinci Resolve Studio 21.0.2
|
|
14
14
|
|
|
15
|
-
**Totals:**
|
|
15
|
+
**Totals:** 29 missing capabilities, 37 bugs / unreliable behaviors.
|
|
16
16
|
|
|
17
17
|
The authoritative source is the runtime-queryable `api_truth` ledger
|
|
18
18
|
(`resolve_control api_truth "<query>"`); this document is generated from
|
|
@@ -239,6 +239,22 @@ equivalent, blocking full automation.
|
|
|
239
239
|
- **Workaround / current handling:** Check GetRenderCodecs(format) first; when it is empty, treat the format as unreachable through this API rather than guessing a codec value. Render audio-only via ExportVideo=False on a format that does expose codecs, or drive it from a saved render preset.
|
|
240
240
|
- **Tags:** render, deliver, audio, unsupported
|
|
241
241
|
|
|
242
|
+
### TimelineItem.CreateMagicMask (needs operator clicks)
|
|
243
|
+
|
|
244
|
+
- **Object:** `TimelineItem`
|
|
245
|
+
- **Signature:** `(mode) -> bool`
|
|
246
|
+
- **Behavior:** Returns False when the item carries no Magic Mask clicks. Magic Mask v2 is click-driven (manual ch. 139; strokes are the legacy v1 interface) and the scripting API has no way to place a click, so on a fresh item the call can never isolate anything — it only tracks a mask the operator already seeded. Mode strings are 'F', 'B', 'BI'; long spellings like 'Forward' are rejected.
|
|
247
|
+
- **Workaround / current handling:** Treat CreateMagicMask as track-only: have the operator click the subject (Color page > Magic Mask palette) and then call it, or surface the HITL steps instead of a bare False. Verify isolation with a rendered frame (gallery_stills grab_and_export), never a thumbnail.
|
|
248
|
+
- **Tags:** ai, magic-mask, hitl, silent-failure
|
|
249
|
+
|
|
250
|
+
### TimelineItem.SetCDL (write-only, no GetCDL)
|
|
251
|
+
|
|
252
|
+
- **Object:** `TimelineItem`
|
|
253
|
+
- **Signature:** `({NodeIndex, Slope, Offset, Power, Saturation}) -> bool`
|
|
254
|
+
- **Behavior:** There is no GetCDL, so applied CDL values cannot be read back; the bool is the only signal and it returns False with no reason (missing node, still/generator item, values silently rejected). NodeIndex is 1-based (README line 6) and must not exceed Graph.GetNumNodes() — note TimelineItem.GetNumNodes is deprecated; the count lives on item.GetNodeGraph().
|
|
255
|
+
- **Workaround / current handling:** Read item.GetNodeGraph().GetNumNodes() before SetCDL and diagnose a False against the node count and clip type. Prove the applied look with a rendered frame (gallery_stills grab_and_export or Project.ExportCurrentFrameAsStill), not the return value.
|
|
256
|
+
- **Tags:** color, cdl, readback, silent-failure
|
|
257
|
+
|
|
242
258
|
## Bugs / Unreliable Behavior (please fix)
|
|
243
259
|
|
|
244
260
|
Methods that exist but misbehave — silent failures, unreliable return
|
|
@@ -350,9 +366,9 @@ values, or automation-hostile modal prompts.
|
|
|
350
366
|
### TimelineItem.AddFusionComp / LoadFusionCompByName
|
|
351
367
|
|
|
352
368
|
- **Object:** `TimelineItem (media-backed clip)`
|
|
353
|
-
- **Behavior:** A Fusion composition created on a media clip through the API is not applied at render WHEN MEDIAOUT HAS NO PATH FROM MEDIAIN. The original blanket form of this entry — 'never applied at render' — was too broad and was corrected on 2026-08-02: a comp wired MediaIn -> Blur -> MediaOut, created entirely through the API on an ordinary media clip, DOES render. PSNR between the plain and Fusion renders of the same timeline was 22.7 dB (identical would be infinite), the file shrank 22.5 MB -> 14.8 MB as a blur should, and the output was frame-for-frame identical in GUI and headless. A first attempt that wired ONLY MediaOut -> Blur, leaving the Blur with no source, made the render job come back 'Failed' with an 887-byte file — so an unrooted graph does not merely get bypassed, it can take the render down. What still stands is the original observation for the configuration it actually tested, which is retained below and has NOT been re-measured: AddFusionComp() returns the comp, AddTool/Connect/SetInput all succeed, and the whole graph reads back correctly (GetCompCount 1, MediaOut1.Input wired to the new tool, StyledText returning the value just set) — but the rendered output is byte-for-byte the untouched source media. Verified live on Studio 19.1.3.7 with the strongest form of the test: MediaOut1 fed ONLY by a Text+, with no path from MediaIn at all, still rendered the unmodified clip. LoadFusionCompByName on the sole comp does not activate it either. Contrast InsertFusionTitleIntoTimeline, whose comp DOES render — text set via SetInput('StyledText') appears in the output — so this is specific to comps attached to media-backed clips, not to Fusion through the API generally. REPRODUCED 2026-08-21 on Studio 19.1.3.7: a rooted MediaIn -> Blur -> MediaOut comp built entirely through the API renders (PSNR 24.38 dB vs the no-comp baseline), so the 2026-08-02 correction stands. Note that an important share of 'the comp was ignored' readings are NOT this entry at all but the Composition.Lock bug above — a parameter written under a comp lock reads back correctly and never reaches the render, which looks identical from the API side.
|
|
354
|
-
- **Workaround / current handling:** Wire the graph so MediaOut descends from MediaIn — that is the difference between a comp that renders and one that is silently bypassed, and it is what made this look like 'Fusion never renders from the API'. Never leave a tool unrooted: a MediaOut fed by a tool with no source failed the render job outright. For text or effects over picture, insert a Fusion title/generator as its own timeline clip and set its Text+ (fusion_comp set_text_plus), rather than attaching a comp to the media clip. Note the destination track cannot be chosen from the API (see the Track Selector entry), so overlaying onto an existing clip's track is not currently reachable end-to-end. Building the comp in the Fusion page UI works; only the API-created comp is ignored.
|
|
355
|
-
- **Tags:** fusion, silent-failure, render
|
|
369
|
+
- **Behavior:** A Fusion composition created on a media clip through the API is not applied at render WHEN MEDIAOUT HAS NO PATH FROM MEDIAIN. The original blanket form of this entry — 'never applied at render' — was too broad and was corrected on 2026-08-02: a comp wired MediaIn -> Blur -> MediaOut, created entirely through the API on an ordinary media clip, DOES render. PSNR between the plain and Fusion renders of the same timeline was 22.7 dB (identical would be infinite), the file shrank 22.5 MB -> 14.8 MB as a blur should, and the output was frame-for-frame identical in GUI and headless. A first attempt that wired ONLY MediaOut -> Blur, leaving the Blur with no source, made the render job come back 'Failed' with an 887-byte file — so an unrooted graph does not merely get bypassed, it can take the render down. What still stands is the original observation for the configuration it actually tested, which is retained below and has NOT been re-measured: AddFusionComp() returns the comp, AddTool/Connect/SetInput all succeed, and the whole graph reads back correctly (GetCompCount 1, MediaOut1.Input wired to the new tool, StyledText returning the value just set) — but the rendered output is byte-for-byte the untouched source media. Verified live on Studio 19.1.3.7 with the strongest form of the test: MediaOut1 fed ONLY by a Text+, with no path from MediaIn at all, still rendered the unmodified clip. LoadFusionCompByName on the sole comp does not activate it either. Contrast InsertFusionTitleIntoTimeline, whose comp DOES render — text set via SetInput('StyledText') appears in the output — so this is specific to comps attached to media-backed clips, not to Fusion through the API generally. REPRODUCED 2026-08-21 on Studio 19.1.3.7: a rooted MediaIn -> Blur -> MediaOut comp built entirely through the API renders (PSNR 24.38 dB vs the no-comp baseline), so the 2026-08-02 correction stands. Note that an important share of 'the comp was ignored' readings are NOT this entry at all but the Composition.Lock bug above — a parameter written under a comp lock reads back correctly and never reaches the render, which looks identical from the API side. That is what an independent 2026-08-20 report on Studio 21.0.4.5 was measuring: a wired MediaIn -> Blur -> MediaOut comp, and a Transform variant, both delivered renders bit-identical to the no-comp baseline (PSNR inf) when built and set through this server. It was read at the time as a version regression (19.1.3.7 honours a wired comp, 21.0.4.5 does not); with the lock bug identified, the simpler reading is that the same defect reproduces on 21.0.4.5 — and since the lock bug has only been isolated on 19.1.3.7, that report is the only evidence it is not build-specific. Treat 'a wired comp renders' as established for 19.1.3.7 and unverified elsewhere.
|
|
370
|
+
- **Workaround / current handling:** Wire the graph so MediaOut descends from MediaIn — that is the difference between a comp that renders and one that is silently bypassed, and it is what made this look like 'Fusion never renders from the API'. Never leave a tool unrooted: a MediaOut fed by a tool with no source failed the render job outright. For text or effects over picture, insert a Fusion title/generator as its own timeline clip and set its Text+ (fusion_comp set_text_plus), rather than attaching a comp to the media clip. Note the destination track cannot be chosen from the API (see the Track Selector entry), so overlaying onto an existing clip's track is not currently reachable end-to-end. Building the comp in the Fusion page UI works; only the API-created comp is ignored. Never claim a Fusion effect from comp readback alone — prove it with a rendered frame (gallery_stills grab_and_export before/after, or a delivered render). On setups where even the wired comp does not render (see the 2026-08-20 measurement), treat per-item Fusion effects as HITL (a human builds or activates the comp in the UI) and bake stills motion with ffmpeg when unattended output is required.
|
|
371
|
+
- **Tags:** fusion, silent-failure, render, readback, hitl
|
|
356
372
|
|
|
357
373
|
### Composition.Paste
|
|
358
374
|
|
|
@@ -532,3 +548,11 @@ values, or automation-hostile modal prompts.
|
|
|
532
548
|
- **Behavior:** PANEL-dependent, not mode-dependent — and the distinction took three revisions of this entry to pin down, so the evidence is recorded rather than summarised. Across four controlled 92-probe sweeps (2 GUI, 2 headless, 2026-08-01) it returned False and wrote nothing in ALL FOUR, while Timeline.GrabStill() succeeded in all four — so it is not being handed an empty still. In one earlier GUI session it DID work, returning True and writing 2 files. The variable that differed is not the mode: it is whether the Gallery panel was visible on the Color page, which depends on the restored workspace layout and which the harness does not control. A headless session can never satisfy it, so in practice the call never works headless; a GUI session satisfies it only sometimes. Project.ExportCurrentFrameAsStill worked in all four runs in both modes.
|
|
533
549
|
- **Workaround / current handling:** Do not use ExportStills unattended in either mode — a GUI session is not sufficient, only a GUI session with the Gallery panel open. Use Project.ExportCurrentFrameAsStill for pixels (verified in both modes, four for four) or drp.extract_node_graphs for grades. If ExportStills must be used, have the user open Workspace > Gallery first and verify the written files rather than trusting the return.
|
|
534
550
|
- **Tags:** gallery, stills, headless, unreliable-return
|
|
551
|
+
|
|
552
|
+
### MediaPool.AppendToTimeline (null-id timeline items)
|
|
553
|
+
|
|
554
|
+
- **Object:** `MediaPool`
|
|
555
|
+
- **Signature:** `([{clipInfo}, ...]) -> [TimelineItem]`
|
|
556
|
+
- **Behavior:** The returned TimelineItem objects can have an unreadable/empty GetUniqueId, notably when the clipInfo recordFrame lands in a span still occupied by another item (any duplicate-then-delete 'move' whose offset is smaller than the item duration hits this). The item may not actually exist on the timeline. A session that trusted the non-empty return and deleted the sources lost 26 clips (Portugal timeline, 2026-08-19).
|
|
557
|
+
- **Workaround / current handling:** Never treat AppendToTimeline's return as proof of placement. Re-enumerate the track and match on record frame + duration before any dependent delete; never shift items by duplicate-then-delete into occupied spans — use timeline.ripple_insert, which rebuilds the tail into free space and verifies by readback.
|
|
558
|
+
- **Tags:** editorial, silent-failure, unreliable-return, timeline
|
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.99.1"
|
|
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.99.1"
|
|
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()}")
|
|
@@ -1926,17 +1926,34 @@ def ti_load_burn_in_preset(preset_name: str, item_index: int = 0, track_type: st
|
|
|
1926
1926
|
|
|
1927
1927
|
|
|
1928
1928
|
@mcp.tool()
|
|
1929
|
-
def ti_create_magic_mask(mode: str = "
|
|
1929
|
+
def ti_create_magic_mask(mode: str = "F", item_index: int = 0, track_type: str = "video", track_index: int = 1) -> Dict[str, Any]:
|
|
1930
1930
|
"""Create a Magic Mask on a timeline item.
|
|
1931
1931
|
|
|
1932
|
+
Magic Mask v2 needs operator CLICKS on the subject (Color page > Magic Mask
|
|
1933
|
+
palette > click subject > Track Forward); the API cannot place them, so with
|
|
1934
|
+
no clicks present Resolve returns False and no isolation exists.
|
|
1935
|
+
|
|
1932
1936
|
Args:
|
|
1933
|
-
mode: '
|
|
1937
|
+
mode: 'F' (forward), 'B' (backward), or 'BI' (bidirection) per the
|
|
1938
|
+
scripting README ('Forward'/'Backward' are accepted as aliases).
|
|
1934
1939
|
item_index: 0-based item index. Default: 0.
|
|
1935
1940
|
"""
|
|
1941
|
+
aliases = {"F": "F", "FORWARD": "F", "B": "B", "BACKWARD": "B",
|
|
1942
|
+
"BI": "BI", "BIDIRECTION": "BI", "BIDIRECTIONAL": "BI"}
|
|
1943
|
+
normalized = aliases.get(str(mode).strip().upper())
|
|
1944
|
+
if not normalized:
|
|
1945
|
+
return {"error": "mode must be 'F', 'B', or 'BI'"}
|
|
1936
1946
|
item, err = _get_timeline_item(track_type, track_index, item_index)
|
|
1937
1947
|
if err:
|
|
1938
1948
|
return err
|
|
1939
|
-
|
|
1949
|
+
if bool(item.CreateMagicMask(normalized)):
|
|
1950
|
+
return {"success": True, "mode": normalized}
|
|
1951
|
+
return {
|
|
1952
|
+
"success": False,
|
|
1953
|
+
"needs_hitl": True,
|
|
1954
|
+
"hitl": "No Magic Mask clicks on this item — a human must click the subject "
|
|
1955
|
+
"on the Color page Magic Mask palette, then press Track Forward.",
|
|
1956
|
+
}
|
|
1940
1957
|
|
|
1941
1958
|
|
|
1942
1959
|
@mcp.tool()
|