davinci-resolve-mcp 2.86.3 → 2.87.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 CHANGED
@@ -2,6 +2,88 @@
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.87.0
6
+
7
+ The ten Resolve 21.0.4 scripting surfaces that the 24 Jul 2026 README refresh
8
+ documented beyond the three wired in v2.86.0. Contributed by @legionsound
9
+ (PR #139), who reported them as issues #135–#138 from Studio 21.0.4.5.
10
+
11
+ ### Added
12
+
13
+ - **`layout_presets` gains `list`** (`Resolve.GetLayoutPresetList`). The tool
14
+ had six actions and every one of them took a preset name the caller had no
15
+ way to enumerate.
16
+ - **`render_presets` gains `list_burnin` and `delete_burnin`**
17
+ (`Resolve.GetBurnInPresetList` / `DeleteBurnInPreset`). `list_burnin` is the
18
+ missing half of the burn-in surface: the `DataBurnIn` render setting and both
19
+ `load_burnin_preset` actions take a name nothing could list.
20
+ - **`resolve_control` gains the six `*_user_preferences_preset` actions** —
21
+ `list`, `save`, `load`, `delete`, `import`, `export`. Two caveats are carried
22
+ in the docstrings and answers rather than left to be discovered:
23
+ `load_user_preferences_preset` is **session-wide** (it swaps the user's global
24
+ Resolve preferences, not a project setting), and
25
+ `import_user_preferences_preset` answers with the README's own caveat that the
26
+ imported preset is *not* auto-loaded, so a caller follows with `load` instead
27
+ of stopping early.
28
+ - **`project_manager` gains `list_attributes`**
29
+ (`ProjectManager.GetProjectAttributesInCurrentFolder`) — `lastModifiedDate`,
30
+ `creationDate`, `notes`, and `liveCollaborationMode` per project in the
31
+ current folder, without loading any project.
32
+
33
+ All ten are `_requires_method`-guarded at 21.0.4, advertised in the capability
34
+ and preset-lifecycle probes, and covered by 21 stub contract cases in
35
+ `tests/test_resolve2104_preset_actions.py` (answers, name-required errors,
36
+ pre-21.0.4 guards, `_unknown` listings, capability advertisement on both a
37
+ 21.0.4 and a legacy stub).
38
+
39
+ ### Documentation
40
+
41
+ - **`docs/reference/resolve_scripting_api.txt` was the 26 May 2026 README** —
42
+ which is exactly why this delta went unnoticed. It is now the 24 Jul 2026
43
+ text these wrappers came from.
44
+ - **`api-coverage.md`: 351 → 361 methods**, with the ten new rows marked 🔬 and
45
+ carrying the contributor's Studio 21.0.4.5 results as a contributor report,
46
+ not as validation of ours — this machine runs Studio 19.1.3 and free 21.0.3,
47
+ neither of which is 21.0.4. `LoadUserPreferencesPreset` is recorded as
48
+ deliberately unexecuted even by the reporter, same class as
49
+ `DisableBackgroundTasksForCurrentResolveSession`.
50
+ - **The README key stats were stale at 349** since the v2.86.0 surfaces landed;
51
+ both READMEs and the live-tested badge now read 361 covered / 338 live-tested
52
+ (93.6%), and the English tested-against row picks up the free 21.0.3 build the
53
+ coverage doc already listed.
54
+
55
+ ## What's New in v2.86.4
56
+
57
+ The issue #132 follow-up, which turned out not to be a tool bug at all. No
58
+ behavior changed in any tool.
59
+
60
+ ### Fixed
61
+
62
+ - **The skills told an assistant to "route the user to the UI" and stopped
63
+ there, so it invented the directions.** In issue #132 a user was sent hunting
64
+ for a retime dropdown "in the lower left of the clip"; the keyframe tray was
65
+ never mentioned. That direction exists nowhere in this repo — no skill, doc,
66
+ or ledger entry describes where any Resolve control sits. The assistant
67
+ improvised the handoff and delivered it in the same confident register as the
68
+ API facts around it, which had been measured, so the user had no way to tell
69
+ the two apart. `resolve-edit` now carries the rule: **never improvise UI
70
+ geography.** Name the operation, say you cannot see the user's screen, and
71
+ treat a UI pointer already written into a skill (the playback-frame-rate path
72
+ in `resolve-rough-cut`) as the only kind to quote — verbatim, never extended
73
+ from memory. Where no pointer exists, point at Blackmagic's manual for their
74
+ build rather than supplying one. `resolve-rough-cut` picks up the same guard
75
+ at its own UI handoff.
76
+
77
+ Deliberately **not** fixed by adding the correct location. This repo verifies
78
+ API behavior and has no mechanism to version-guard a UI claim — every
79
+ `api_truth` entry is stamped with the build it was measured on because
80
+ unstamped claims rot, and UI geography moves between builds, pages and
81
+ layouts with no drift guard that would catch it going stale. The reporter hit
82
+ this on Resolve 21; the validation machine here is Studio 19.1.3.7, so
83
+ confirming a location here and publishing it for 21 would be the exact move
84
+ v2.82.1 exists to correct. Thanks to @magwa101 for coming back with the
85
+ detail that relocated the bug.
86
+
5
87
  ## What's New in v2.86.3
6
88
 
7
89
  A Simplified Chinese phrasing fix from the reviewer who asked for it when #122
package/README.md CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  English | [简体中文](README.zh-CN.md)
4
4
 
5
- [![Version](https://img.shields.io/badge/version-2.86.3-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-2.87.0-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
6
6
  [![npm](https://img.shields.io/npm/v/davinci-resolve-mcp.svg?label=npm&color=CB3837)](https://www.npmjs.com/package/davinci-resolve-mcp)
7
7
  [![API Coverage](https://img.shields.io/badge/API%20Coverage-100%25-brightgreen.svg)](docs/reference/api-coverage.md)
8
8
  [![Tools](https://img.shields.io/badge/MCP%20Tools-34%20(341%20full)-blue.svg)](#server-modes)
9
9
  [![Advanced](https://img.shields.io/badge/Advanced%20(offline)-18%20tools-blueviolet.svg)](#server-modes)
10
- [![Tested](https://img.shields.io/badge/Live%20Tested-96.8%25-green.svg)](docs/reference/api-coverage.md#test-results)
10
+ [![Tested](https://img.shields.io/badge/Live%20Tested-93.6%25-green.svg)](docs/reference/api-coverage.md#test-results)
11
11
  [![DaVinci Resolve](https://img.shields.io/badge/DaVinci%20Resolve-18.5+-darkred.svg)](https://www.blackmagicdesign.com/products/davinciresolve)
12
12
  [![Python](https://img.shields.io/badge/python-3.10+-green.svg)](https://www.python.org/downloads/)
13
13
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
@@ -265,10 +265,10 @@ The default server is a local stdio process launched by your MCP client; it does
265
265
  | MCP Tools | **34** compound / **341** granular (live server) |
266
266
  | Advanced (offline) tools | **18** — .drp/.drt/.drx + DB authoring, no Resolve running |
267
267
  | Kernel Actions | **136** guarded workflow actions across 9 compound tools |
268
- | API Methods Covered | **349/349** (100%) |
269
- | Methods Live Tested | **338/349** (96.8%) |
268
+ | API Methods Covered | **361/361** (100%) |
269
+ | Methods Live Tested | **338/361** (93.6%) |
270
270
  | Live Test Pass Rate | **338/338** (100%) |
271
- | Tested Against | DaVinci Resolve 19.1.3 Studio + Resolve 20.3.2 Studio + Resolve 21.0.2 Studio |
271
+ | Tested Against | DaVinci Resolve 19.1.3 Studio + Resolve 20.3.2 Studio + Resolve 21.0.2 Studio + Resolve 21.0.3 **free** (via the in-app bridge) |
272
272
 
273
273
  For method-by-method status, see [API Coverage and Test Results](docs/reference/api-coverage.md). For current workflow support, see [Kernel Action Coverage](docs/kernels/README.md).
274
274
 
package/README.zh-CN.md CHANGED
@@ -2,17 +2,17 @@
2
2
 
3
3
  [English](README.md) | 简体中文
4
4
 
5
- [![Version](https://img.shields.io/badge/version-2.86.3-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-2.87.0-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
6
6
  [![npm](https://img.shields.io/npm/v/davinci-resolve-mcp.svg?label=npm&color=CB3837)](https://www.npmjs.com/package/davinci-resolve-mcp)
7
7
  [![API Coverage](https://img.shields.io/badge/API%20Coverage-100%25-brightgreen.svg)](docs/reference/api-coverage.md)
8
8
  [![Tools](https://img.shields.io/badge/MCP%20Tools-34%20(341%20full)-blue.svg)](#服务器模式)
9
9
  [![Advanced](https://img.shields.io/badge/Advanced%20(offline)-18%20tools-blueviolet.svg)](#服务器模式)
10
- [![Tested](https://img.shields.io/badge/Live%20Tested-96.8%25-green.svg)](docs/reference/api-coverage.md#test-results)
10
+ [![Tested](https://img.shields.io/badge/Live%20Tested-93.6%25-green.svg)](docs/reference/api-coverage.md#test-results)
11
11
  [![DaVinci Resolve](https://img.shields.io/badge/DaVinci%20Resolve-18.5+-darkred.svg)](https://www.blackmagicdesign.com/products/davinciresolve)
12
12
  [![Python](https://img.shields.io/badge/python-3.10+-green.svg)](https://www.python.org/downloads/)
13
13
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)
14
14
 
15
- > 本翻译对应 v2.86.3 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
15
+ > 本翻译对应 v2.87.0 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
16
16
 
17
17
  一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
18
18
 
@@ -193,10 +193,10 @@ DRX 调色写入**针对 Resolve Studio 做过实机校准**:调色参数默
193
193
  | MCP 工具 | **34** 复合 / **341** 细粒度(实时服务器) |
194
194
  | Advanced(离线)工具 | **18**——.drp/.drt/.drx + 数据库创作,无需 Resolve 运行 |
195
195
  | 内核 action | 9 个复合工具下 **136** 个带护栏的工作流 action |
196
- | API 方法覆盖 | **349/349**(100%) |
197
- | 实机测试方法数 | **338/349**(96.8%) |
196
+ | API 方法覆盖 | **361/361**(100%) |
197
+ | 实机测试方法数 | **338/361**(93.6%) |
198
198
  | 实机测试通过率 | **338/338**(100%) |
199
- | 测试环境 | DaVinci Resolve 19.1.3 Studio + 20.3.2 Studio + 21.0.2 Studio |
199
+ | 测试环境 | DaVinci Resolve 19.1.3 Studio + 20.3.2 Studio + 21.0.2 Studio + 21.0.3 **免费版**(经内置桥接) |
200
200
 
201
201
  逐方法状态见 [API 覆盖与测试结果](docs/reference/api-coverage.md)。当前工作流支持见 [内核 action 覆盖](docs/kernels/README.md)。
202
202
 
package/docs/SKILL.md CHANGED
@@ -498,11 +498,21 @@ Key actions:
498
498
  - `get_keyframe_mode` / `set_keyframe_mode(mode)`
499
499
  - `get_fairlight_presets` — Resolve 20.2.2+; returns available Fairlight
500
500
  preset names
501
+ - `list/save/load/delete/import/export_user_preferences_preset` — Resolve
502
+ 21.0.4+; user-preferences presets. `load_...` is SESSION-WIDE: it swaps the
503
+ user's global Resolve preferences, so only call it when the user asked for
504
+ the switch. `import_...` does not activate the imported preset — follow with
505
+ `load_user_preferences_preset`
501
506
  - `quit` — terminates Resolve (destructive; confirm with user first)
502
507
 
503
508
  **`layout_presets`** — Save, load, export, import, delete UI layout presets.
509
+ `list` (Resolve 21.0.4+) enumerates the saved preset names the other actions
510
+ take.
504
511
 
505
512
  **`render_presets`** — Import and export render and burn-in presets.
513
+ `list_burnin` / `delete_burnin` (Resolve 21.0.4+) enumerate and remove burn-in
514
+ presets — `list_burnin` is the only way to discover the names the `DataBurnIn`
515
+ render setting and the `load_burnin_preset` actions expect.
506
516
 
507
517
  ---
508
518
 
@@ -510,11 +520,16 @@ Key actions:
510
520
 
511
521
  **`project_manager`** — CRUD on projects.
512
522
 
513
- Key actions: `list`, `get_current`, `create(name, media_location_path?)`,
523
+ Key actions: `list`, `list_attributes`, `get_current`,
524
+ `create(name, media_location_path?)`,
514
525
  `load(name)`, `save`, `close`,
515
526
  `delete(name)`, `import_project(path)`, `export_project(name, path)`, `archive`,
516
527
  `restore`
517
528
 
529
+ `list_attributes` (Resolve 21.0.4+) returns `lastModifiedDate`, `creationDate`,
530
+ `notes`, and `liveCollaborationMode` per project in the current folder without
531
+ loading any of them.
532
+
518
533
  Project / Database / Archive kernel actions (v2.15.0+) add guarded project
519
534
  lifecycle, settings, database, preset, and archive boundary helpers:
520
535
 
@@ -8,8 +8,8 @@ Complete Resolve scripting API coverage, live-test status, and method-by-method
8
8
  |--------|-------|
9
9
  | MCP Tools | **34** compound (default) / **341** granular |
10
10
  | Kernel Actions | **136** guarded MCP workflow actions across 9 compound tools |
11
- | API Methods Covered | **351/351** (100%) |
12
- | Methods Live Tested | **338/351** (96.3%) |
11
+ | API Methods Covered | **361/361** (100%) |
12
+ | Methods Live Tested | **338/361** (93.6%) |
13
13
  | Live Test Pass Rate | **338/338** (100%) |
14
14
  | API Object Classes | 13 |
15
15
  | Tested Against | DaVinci Resolve 19.1.3 Studio + Resolve 20.3.2 Studio + Resolve 21.0.2 Studio + Resolve 21.0.3 **free** (via the in-app bridge) |
@@ -109,7 +109,7 @@ source, these summaries are downstream of it:
109
109
  | Phase 5 | 6/6 | 100% | Scene cuts, subtitles from audio, graph node cache/tools/enable |
110
110
  | Resolve 20 delta | 12/12 | 100% | Resolve 20.0-20.2.2 scripting additions live-tested on 20.3.2 |
111
111
  | Resolve 21 delta | 7/7 | 100% | Resolve 21.0 additions that could be executed, live-tested on Studio 21.0.2.4 (`tests/live_resolve21_validation.py`). The other 6 need an AI Extras pack or are unsafe to run — counted as untested, not as passes |
112
- | **Total** | **338/338** | **100%** | **96.3% of the 351 covered methods tested live** |
112
+ | **Total** | **338/338** | **100%** | **93.6% of the 361 covered methods tested live** |
113
113
 
114
114
  #### Resolve 21 delta detail
115
115
 
@@ -130,7 +130,7 @@ marked 🔬 rather than ⚠️.
130
130
  | `Project.GenerateSpeech` | 🔬 | Requires AI Speech Generator; returned an error **string**, not a MediaPoolItem |
131
131
  | `Resolve.DisableBackgroundTasksForCurrentResolveSession` | 🔬 | Present in `dir()`; **not executed** — session-wide, returns None, and has no `Enable...` counterpart, so there is no undo short of restarting Resolve |
132
132
 
133
- ### Untested Methods (13 of 351)
133
+ ### Untested Methods (23 of 361)
134
134
 
135
135
  Every ☁️ and 🔬 row from the reference tables, listed here so the count is
136
136
  checkable rather than asserted.
@@ -150,12 +150,24 @@ checkable rather than asserted.
150
150
  | `Resolve.DisableBackgroundTasksForCurrentResolveSession` | Deliberately not executed: session-wide, returns `None`, and has no `Enable...` counterpart, so there is no undo short of restarting Resolve | No |
151
151
  | `MPI.GetTimeline` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
152
152
  | `TL.GetSelectedClips` | Requires a Resolve 21.0.4 build; the validation machine runs 19.1.3 | Yes |
153
+ | `Resolve.GetLayoutPresetList` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
154
+ | `Resolve.GetBurnInPresetList` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
155
+ | `Resolve.DeleteBurnInPreset` | Requires a Resolve 21.0.4 build; also, no scripting API creates a burn-in preset to round-trip against | Yes |
156
+ | `Resolve.GetUserPreferencesPresetList` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
157
+ | `Resolve.SaveUserPreferencesPreset` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
158
+ | `Resolve.LoadUserPreferencesPreset` | Deliberately not executed even by the reporter: swaps the user's global preferences session-wide | No |
159
+ | `Resolve.DeleteUserPreferencesPreset` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
160
+ | `Resolve.ImportUserPreferencesPreset` | Requires a Resolve 21.0.4 build and a preset file to import | Yes |
161
+ | `Resolve.ExportUserPreferencesPreset` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
162
+ | `PM.GetProjectAttributesInCurrentFolder` | Requires a Resolve 21.0.4 build; the validation machine runs Studio 19.1.3 and free 21.0.3 | Yes |
153
163
 
154
164
  The five AI Extras rows are untested for want of a downloadable pack, not because
155
165
  the wrappers are suspect — a report from anyone who has the packs installed would
156
- close them. The two 21.0.4 rows are untested for want of the build: this machine
166
+ close them. The twelve 21.0.4 rows are untested for want of the build: this machine
157
167
  runs 19.1.3, so they are guarded and unit-tested against stubs and carry a
158
- contributor's report rather than a result of ours. The remaining one is a
168
+ contributor's report rather than a result of ours (issue #131 for the first two;
169
+ issues #135–#138 for the ten from the 24 Jul 2026 README refresh, several with
170
+ save→list→delete round-trips on Studio 21.0.4.5). The remaining one is a
159
171
  decision rather than a gap: it is reachable, and
160
172
  running it during a validation sweep would disable background tasks for every
161
173
  project open in that Resolve instance.
@@ -199,6 +211,15 @@ Every method in the DaVinci Resolve Scripting API and its test status. Methods a
199
211
  | 21 | `SetKeyframeMode(keyframeMode)` | ⚠️ | API accepts; mode must match valid enum |
200
212
  | 22 | `GetFairlightPresets()` | ✅ | Returns a preset map. Live on 20.3.2 Studio and 21.0.3 free; **absent below 20.2.2** — confirmed missing on 19.1.3, so the version floor is verified from both sides |
201
213
  | 23 | `DisableBackgroundTasksForCurrentResolveSession()` | 🔬 | Resolve 21.0. Present in `dir()`; **not executed** — session-wide, returns `None`, no `Enable...` counterpart, so no undo short of restarting Resolve |
214
+ | 24 | `GetLayoutPresetList()` | 🔬 | Resolve 21.0.4 (24 Jul 2026 README). Not executed here — no 21.0.4 build; reporter's save→list→delete round-trip on Studio 21.0.4.5 in issue #137 |
215
+ | 25 | `GetBurnInPresetList()` | 🔬 | Resolve 21.0.4. Not executed here; reported returning a list on Studio 21.0.4.5 in issue #136 |
216
+ | 26 | `DeleteBurnInPreset(presetName)` | 🔬 | Resolve 21.0.4. `dir()`-present on 21.0.4.5 (issue #136); not executed anywhere — no scripting API creates a burn-in preset to round-trip against |
217
+ | 27 | `GetUserPreferencesPresetList()` | 🔬 | Resolve 21.0.4. Not executed here; reporter's save→list→delete round-trip on Studio 21.0.4.5 in issue #138 |
218
+ | 28 | `SaveUserPreferencesPreset(presetName)` | 🔬 | Resolve 21.0.4. Not executed here; part of the issue #138 round-trip on 21.0.4.5 |
219
+ | 29 | `LoadUserPreferencesPreset(presetName)` | 🔬 | Resolve 21.0.4. `dir()`-present on 21.0.4.5; **not executed** even by the reporter — swaps the user's global preferences session-wide (issue #138) |
220
+ | 30 | `DeleteUserPreferencesPreset(presetName)` | 🔬 | Resolve 21.0.4. Not executed here; part of the issue #138 round-trip on 21.0.4.5 |
221
+ | 31 | `ImportUserPreferencesPreset(presetFilePath, presetName)` | 🔬 | Resolve 21.0.4. `dir()`-present on 21.0.4.5 only (issue #138) — no preset file was on hand. Per the README the imported preset is **not** auto-loaded |
222
+ | 32 | `ExportUserPreferencesPreset(presetName, exportPath)` | 🔬 | Resolve 21.0.4. `dir()`-present on 21.0.4.5 only (issue #138) |
202
223
 
203
224
  ### ProjectManager
204
225
 
@@ -229,6 +250,7 @@ Every method in the DaVinci Resolve Scripting API and its test status. Methods a
229
250
  | 23 | `LoadCloudProject({cloudSettings})` | ☁️ | Requires cloud infrastructure |
230
251
  | 24 | `ImportCloudProject(filePath, {cloudSettings})` | ☁️ | Requires cloud infrastructure |
231
252
  | 25 | `RestoreCloudProject(folderPath, {cloudSettings})` | ☁️ | Requires cloud infrastructure |
253
+ | 26 | `GetProjectAttributesInCurrentFolder()` | 🔬 | Resolve 21.0.4 (24 Jul 2026 README). Not executed here — no 21.0.4 build; reported live on Studio 21.0.4.5 in issue #135, returning `lastModifiedDate` / `creationDate` / `notes` / `liveCollaborationMode` for all 8 projects in the current folder |
232
254
 
233
255
  ### Project
234
256
 
@@ -1,4 +1,4 @@
1
- Last Updated: 26 May 2026
1
+ Last Updated: 24 Jul 2026
2
2
  -------------------------
3
3
  In this package, you will find a brief introduction to the Scripting API for DaVinci Resolve Studio. Apart from this README.txt file, this package contains folders containing the basic import
4
4
  modules for scripting access (DaVinciResolve.py) and some representative examples.
@@ -91,6 +91,7 @@ Resolve
91
91
  GetProductName() --> string # Returns product name.
92
92
  GetVersion() --> [version fields] # Returns list of product version fields in [major, minor, patch, build, suffix] format.
93
93
  GetVersionString() --> string # Returns product version in "major.minor.patch[suffix].build" format.
94
+ GetLayoutPresetList() --> [presetNames...] # Returns list of layout preset names.
94
95
  LoadLayoutPreset(presetName) --> Bool # Loads UI layout from saved preset named 'presetName'.
95
96
  UpdateLayoutPreset(presetName) --> Bool # Overwrites preset named 'presetName' with current UI layout.
96
97
  ExportLayoutPreset(presetName, presetFilePath) --> Bool # Exports preset named 'presetName' to path 'presetFilePath'.
@@ -100,8 +101,16 @@ Resolve
100
101
  Quit() --> None # Quits the Resolve App.
101
102
  ImportRenderPreset(presetPath) --> Bool # Import a preset from presetPath (string) and set it as current preset for rendering.
102
103
  ExportRenderPreset(presetName, exportPath) --> Bool # Export a preset to a given path (string) if presetName(string) exists.
104
+ GetBurnInPresetList() --> [presetName...] # Returns a list of data burn in preset names.
105
+ DeleteBurnInPreset(presetName) --> Bool # Deletes the data burn in preset.
103
106
  ImportBurnInPreset(presetPath) --> Bool # Import a data burn in preset from a given presetPath (string)
104
107
  ExportBurnInPreset(presetName, exportPath) --> Bool # Export a data burn in preset to a given path (string) if presetName (string) exists.
108
+ GetUserPreferencesPresetList() --> [presetNames...] # Returns a list of user preferences preset names.
109
+ LoadUserPreferencesPreset(presetName) --> Bool # Loads the user preferences preset named 'presetName'.
110
+ SaveUserPreferencesPreset(presetName) --> Bool # Saves the current user preferences as a new preset named 'presetName'.
111
+ DeleteUserPreferencesPreset(presetName) --> Bool # Deletes the user preferences preset named 'presetName'.
112
+ ImportUserPreferencesPreset(presetFilePath, presetName) --> Bool # Imports user preferences preset from presetFilePath. Optional presetName argument to name the imported preset. The imported preset is not automatically loaded, use LoadUserPreferencesPreset(..) to load it.
113
+ ExportUserPreferencesPreset(presetName, exportPath) --> Bool # Exports the user preferences preset to exportPath.
105
114
  GetKeyframeMode() --> keyframeMode # Returns the currently set keyframe mode (int). Refer to section 'Keyframe Mode information' below for details.
106
115
  SetKeyframeMode(keyframeMode) --> Bool # Returns True when 'keyframeMode'(enum) is successfully set. Refer to section 'Keyframe Mode information' below for details.
107
116
  GetFairlightPresets() --> [presetNames...]. # Returns a list of Fairlight presets by name
@@ -122,6 +131,8 @@ ProjectManager
122
131
  CreateFolder(folderName) --> Bool # Creates a folder if folderName (string) is unique.
123
132
  DeleteFolder(folderName) --> Bool # Deletes the specified folder if it exists. Returns True in case of success.
124
133
  GetProjectListInCurrentFolder() --> [project names...] # Returns a list of project names in current folder.
134
+ GetProjectAttributesInCurrentFolder() --> {project name: {attributes}, ...} # Returns a dictionary of project names in the current folder to project attributes.
135
+ # Attributes include "lastModifiedDate", "creationDate", "notes", and "liveCollaborationMode".
125
136
  GetFolderListInCurrentFolder() --> [folder names...] # Returns a list of folder names in current folder.
126
137
  GotoRootFolder() --> Bool # Opens root folder in database.
127
138
  GotoParentFolder() --> Bool # Opens parent folder of current folder in database if current folder has parent.
@@ -295,6 +306,7 @@ MediaPoolItem
295
306
  # If no argument is specified, a dict of all set metadata properties is returned.
296
307
  SetMetadata(metadataType, metadataValue) --> Bool # Sets the given metadata to metadataValue (string). Returns True if successful.
297
308
  SetMetadata({metadata}) --> Bool # Sets the item metadata with specified 'metadata' dict. Returns True if successful.
309
+ GetTimeline() --> TimelineItem # Returns the timeline object if the mpItem is a timeline clip, or None if not.
298
310
  GetThirdPartyMetadata(metadataType=None) --> string|dict # Returns the third party metadata value for the key 'metadataType'.
299
311
  # If no argument is specified, a dict of all set third party metadata properties is returned.
300
312
  SetThirdPartyMetadata(metadataType, metadataValue) --> Bool # Sets/Add the given third party metadata to metadataValue (string). Returns True if successful.
@@ -385,6 +397,7 @@ Timeline
385
397
  DeleteClips([timelineItems], Bool) --> Bool # Deletes specified TimelineItems from the timeline, performing ripple delete if the second argument is True. Second argument is optional (The default for this is False)
386
398
  SetClipsLinked([timelineItems], Bool) --> Bool # Links or unlinks the specified TimelineItems depending on second argument.
387
399
  GetItemListInTrack(trackType, index) --> [items...] # Returns a list of timeline items on that track (based on trackType and index). 1 <= index <= GetTrackCount(trackType).
400
+ GetSelectedClips() --> [items...] # Returns the currently selected timeline items.
388
401
  AddMarker(frameId, color, name, note, duration, --> Bool # Creates a new marker at given frameId position and with given marker information. 'customData' is optional and helps to attach user specific data to the marker.
389
402
  customData)
390
403
  GetMarkers() --> {markers...} # Returns a dict (frameId -> {information}) of all markers and dicts with their information.
@@ -693,6 +706,10 @@ Affects:
693
706
  Affects:
694
707
  • x = Project:GetSetting('timelineFrameRate') and Project:SetSetting('timelineFrameRate', x)
695
708
 
709
+ "timelineSampleRate" - the property value is one of the sample rates available to the user in project settings under "Audio sample rate" option in the Fairlight tab.
710
+ Affects:
711
+ • x = Project:GetSetting('timelineSampleRate') and Project:SetSetting('timelineSampleRate', x)
712
+
696
713
  The following Clip properties have specifically enumerated values:
697
714
  "Super Scale" - the property value is an enumerated integer between 1 and 4 with these meanings: 1=no scaling, and 2, 3 and 4 represent the Super Scale multipliers 2x, 3x and 4x.
698
715
  for super scale multiplier '2x Enhanced', exactly 4 arguments must be passed as outlined below. If less than 4 arguments are passed, it will default to 2x.
@@ -844,6 +861,9 @@ The parameter setting is a dictionary containing the following keys:
844
861
  - "ReplaceExistingFilesInPlace": Bool
845
862
  - "ExportSubtitle": Bool
846
863
  - "SubtitleFormat": string (options: "BurnIn", "EmbeddedCaptions", "SeparateFile")
864
+ - "UseFullExtents": Bool
865
+ - "AddFrameHandles": int (>= 0). Ignored if use full extents is enabled.
866
+ - "DataBurnIn": string. (example: "Same as project", "None")
847
867
 
848
868
  Looking up timeline export properties
849
869
  -------------------------------------
package/install.py CHANGED
@@ -36,7 +36,7 @@ from src.utils.update_check import (
36
36
 
37
37
  # ─── Version ──────────────────────────────────────────────────────────────────
38
38
 
39
- VERSION = "2.86.3"
39
+ VERSION = "2.87.0"
40
40
  # Only hard floor: mcp[cli] requires Python 3.10+. There is no upper bound —
41
41
  # Resolve's scripting bridge loads into newer interpreters on recent builds
42
42
  # (Python 3.14 verified against Resolve Studio 20.3.2). Older Resolve builds
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "davinci-resolve-mcp",
3
- "version": "2.86.3",
3
+ "version": "2.87.0",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -85,7 +85,7 @@ if not logging.getLogger().handlers:
85
85
  handlers=[logging.StreamHandler()],
86
86
  )
87
87
 
88
- VERSION = "2.86.3"
88
+ VERSION = "2.87.0"
89
89
  logger = logging.getLogger("davinci-resolve-mcp")
90
90
  logger.info(f"Starting DaVinci Resolve MCP Server v{VERSION}")
91
91
  logger.info(f"Detected platform: {get_platform()}")
package/src/server.py CHANGED
@@ -11,7 +11,7 @@ Usage:
11
11
  python src/server.py --full # Start the 341-tool granular server instead
12
12
  """
13
13
 
14
- VERSION = "2.86.3"
14
+ VERSION = "2.87.0"
15
15
 
16
16
  import base64
17
17
  import os
@@ -13581,6 +13581,16 @@ def resolve_control(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
13581
13581
  get_fairlight_presets() -> {presets}
13582
13582
  set_high_priority() -> {success}
13583
13583
  disable_background_tasks_for_current_session() -> {success} — Resolve 21+
13584
+ list_user_preferences_presets() -> {presets} — Resolve 21.0.4+
13585
+ save_user_preferences_preset(name) -> {success} — Resolve 21.0.4+
13586
+ load_user_preferences_preset(name) -> {success} — Resolve 21.0.4+.
13587
+ SESSION-WIDE: swaps the user's global Resolve preferences, not a
13588
+ project setting. Only call when the user asked for the switch.
13589
+ delete_user_preferences_preset(name) -> {success} — Resolve 21.0.4+
13590
+ import_user_preferences_preset(path, name?) -> {success} — Resolve 21.0.4+.
13591
+ The imported preset is NOT auto-loaded; follow with
13592
+ load_user_preferences_preset to activate it.
13593
+ export_user_preferences_preset(name, path) -> {success} — Resolve 21.0.4+
13584
13594
  open_control_panel(port?, host?, open_browser?) -> {success, url, pid, port, status}
13585
13595
  — Launches the analysis control panel (src/analysis_dashboard.py) as a background process.
13586
13596
  Idempotent: returns the existing URL if already running.
@@ -13816,7 +13826,56 @@ def resolve_control(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
13816
13826
  return missing
13817
13827
  r.DisableBackgroundTasksForCurrentResolveSession()
13818
13828
  return _ok()
13819
- return _unknown(action, ["launch","runtime_mode","get_version","api_truth","check_version_support","verification_stats","job_status","list_jobs","mcp_update_status","set_mcp_update_policy","ignore_mcp_update","snooze_mcp_update","clear_mcp_update_preferences","get_page","open_page","get_keyframe_mode","set_keyframe_mode","quit","get_fairlight_presets","set_high_priority","disable_background_tasks_for_current_session","open_control_panel","control_panel_status","close_control_panel","save_state","restore_state"])
13829
+ elif action == "list_user_preferences_presets":
13830
+ missing = _requires_method(r, "GetUserPreferencesPresetList", "21.0.4")
13831
+ if missing:
13832
+ return missing
13833
+ return {"presets": _ser(r.GetUserPreferencesPresetList() or [])}
13834
+ elif action == "save_user_preferences_preset":
13835
+ missing = _requires_method(r, "SaveUserPreferencesPreset", "21.0.4")
13836
+ if missing:
13837
+ return missing
13838
+ if not p.get("name"):
13839
+ return _err("save_user_preferences_preset requires name")
13840
+ return {"success": bool(r.SaveUserPreferencesPreset(p["name"]))}
13841
+ elif action == "load_user_preferences_preset":
13842
+ missing = _requires_method(r, "LoadUserPreferencesPreset", "21.0.4")
13843
+ if missing:
13844
+ return missing
13845
+ if not p.get("name"):
13846
+ return _err("load_user_preferences_preset requires name")
13847
+ return {"success": bool(r.LoadUserPreferencesPreset(p["name"]))}
13848
+ elif action == "delete_user_preferences_preset":
13849
+ missing = _requires_method(r, "DeleteUserPreferencesPreset", "21.0.4")
13850
+ if missing:
13851
+ return missing
13852
+ if not p.get("name"):
13853
+ return _err("delete_user_preferences_preset requires name")
13854
+ return {"success": bool(r.DeleteUserPreferencesPreset(p["name"]))}
13855
+ elif action == "import_user_preferences_preset":
13856
+ missing = _requires_method(r, "ImportUserPreferencesPreset", "21.0.4")
13857
+ if missing:
13858
+ return missing
13859
+ if not p.get("path"):
13860
+ return _err("import_user_preferences_preset requires path")
13861
+ if p.get("name"):
13862
+ ok = bool(r.ImportUserPreferencesPreset(p["path"], p["name"]))
13863
+ else:
13864
+ ok = bool(r.ImportUserPreferencesPreset(p["path"]))
13865
+ return {"success": ok,
13866
+ "note": "The imported preset is not auto-loaded; use load_user_preferences_preset to activate it."}
13867
+ elif action == "export_user_preferences_preset":
13868
+ missing = _requires_method(r, "ExportUserPreferencesPreset", "21.0.4")
13869
+ if missing:
13870
+ return missing
13871
+ err, clean = _validate_params(p, {
13872
+ "name": {"type": str, "required": True, "non_empty": True},
13873
+ "path": {"type": str, "required": True, "non_empty": True},
13874
+ })
13875
+ if err:
13876
+ return _err(err)
13877
+ return {"success": bool(r.ExportUserPreferencesPreset(clean["name"], clean["path"]))}
13878
+ return _unknown(action, ["launch","runtime_mode","get_version","api_truth","check_version_support","verification_stats","job_status","list_jobs","mcp_update_status","set_mcp_update_policy","ignore_mcp_update","snooze_mcp_update","clear_mcp_update_preferences","get_page","open_page","get_keyframe_mode","set_keyframe_mode","quit","get_fairlight_presets","set_high_priority","disable_background_tasks_for_current_session","list_user_preferences_presets","save_user_preferences_preset","load_user_preferences_preset","delete_user_preferences_preset","import_user_preferences_preset","export_user_preferences_preset","open_control_panel","control_panel_status","close_control_panel","save_state","restore_state"])
13820
13879
 
13821
13880
 
13822
13881
  # ─── V2 C4: Per-field corrections with provenance + changelog ────────────────
@@ -14682,6 +14741,7 @@ def layout_presets(action: str, params: Optional[Dict[str, Any]] = None) -> Dict
14682
14741
  """Manage DaVinci Resolve UI layout presets.
14683
14742
 
14684
14743
  Actions:
14744
+ list() -> {presets} — Resolve 21.0.4+; saved layout preset names
14685
14745
  save(name) -> {success}
14686
14746
  load(name) -> {success}
14687
14747
  update(name) -> {success}
@@ -14694,7 +14754,12 @@ def layout_presets(action: str, params: Optional[Dict[str, Any]] = None) -> Dict
14694
14754
  if r is None:
14695
14755
  return _not_connected_error()
14696
14756
 
14697
- if action == "save":
14757
+ if action == "list":
14758
+ missing = _requires_method(r, "GetLayoutPresetList", "21.0.4")
14759
+ if missing:
14760
+ return missing
14761
+ return {"presets": _ser(r.GetLayoutPresetList() or [])}
14762
+ elif action == "save":
14698
14763
  if not p.get("name"):
14699
14764
  return _err("save requires name")
14700
14765
  return {"success": bool(r.SaveLayoutPreset(p["name"]))}
@@ -14716,7 +14781,7 @@ def layout_presets(action: str, params: Optional[Dict[str, Any]] = None) -> Dict
14716
14781
  return {"success": bool(r.ImportLayoutPreset(p["path"]))}
14717
14782
  elif action == "delete":
14718
14783
  return {"success": bool(r.DeleteLayoutPreset(p["name"]))}
14719
- return _unknown(action, ["save","load","update","export","import_preset","delete"])
14784
+ return _unknown(action, ["list","save","load","update","export","import_preset","delete"])
14720
14785
 
14721
14786
 
14722
14787
  # ═══════════════════════════════════════════════════════════════════════════════
@@ -14733,6 +14798,10 @@ def render_presets(action: str, params: Optional[Dict[str, Any]] = None) -> Dict
14733
14798
  export_render(name, path) -> {success}
14734
14799
  import_burnin(path) -> {success}
14735
14800
  export_burnin(name, path) -> {success}
14801
+ list_burnin() -> {presets} — Resolve 21.0.4+; burn-in preset names usable
14802
+ with the render tool's DataBurnIn setting and load_burnin_preset on
14803
+ project_settings / timeline_item
14804
+ delete_burnin(name) -> {success} — Resolve 21.0.4+
14736
14805
  """
14737
14806
  p = _params(params)
14738
14807
  r = get_resolve()
@@ -14747,7 +14816,19 @@ def render_presets(action: str, params: Optional[Dict[str, Any]] = None) -> Dict
14747
14816
  return {"success": bool(r.ImportBurnInPreset(p["path"]))}
14748
14817
  elif action == "export_burnin":
14749
14818
  return {"success": bool(r.ExportBurnInPreset(p["name"], p["path"]))}
14750
- return _unknown(action, ["import_render","export_render","import_burnin","export_burnin"])
14819
+ elif action == "list_burnin":
14820
+ missing = _requires_method(r, "GetBurnInPresetList", "21.0.4")
14821
+ if missing:
14822
+ return missing
14823
+ return {"presets": _ser(r.GetBurnInPresetList() or [])}
14824
+ elif action == "delete_burnin":
14825
+ missing = _requires_method(r, "DeleteBurnInPreset", "21.0.4")
14826
+ if missing:
14827
+ return missing
14828
+ if not p.get("name"):
14829
+ return _err("delete_burnin requires name")
14830
+ return {"success": bool(r.DeleteBurnInPreset(p["name"]))}
14831
+ return _unknown(action, ["import_render","export_render","import_burnin","export_burnin","list_burnin","delete_burnin"])
14751
14832
 
14752
14833
 
14753
14834
  # ═══════════════════════════════════════════════════════════════════════════════
@@ -14783,6 +14864,7 @@ _PROJECT_MANAGER_METHODS = [
14783
14864
  "CreateFolder",
14784
14865
  "DeleteFolder",
14785
14866
  "GetProjectListInCurrentFolder",
14867
+ "GetProjectAttributesInCurrentFolder",
14786
14868
  "GetFolderListInCurrentFolder",
14787
14869
  "GotoRootFolder",
14788
14870
  "GotoParentFolder",
@@ -14938,6 +15020,7 @@ def _project_capabilities(pm=None, project=None, resolve_obj=None) -> Dict[str,
14938
15020
  "kernel_actions": list(_PROJECT_KERNEL_ACTIONS),
14939
15021
  "resolve": {
14940
15022
  "layout_presets": {
15023
+ "list": _has_method(resolve_obj, "GetLayoutPresetList") if resolve_obj else True,
14941
15024
  "save": _has_method(resolve_obj, "SaveLayoutPreset") if resolve_obj else True,
14942
15025
  "load": _has_method(resolve_obj, "LoadLayoutPreset") if resolve_obj else True,
14943
15026
  "update": _has_method(resolve_obj, "UpdateLayoutPreset") if resolve_obj else True,
@@ -14950,6 +15033,16 @@ def _project_capabilities(pm=None, project=None, resolve_obj=None) -> Dict[str,
14950
15033
  "export_render": _has_method(resolve_obj, "ExportRenderPreset") if resolve_obj else True,
14951
15034
  "import_burnin": _has_method(resolve_obj, "ImportBurnInPreset") if resolve_obj else True,
14952
15035
  "export_burnin": _has_method(resolve_obj, "ExportBurnInPreset") if resolve_obj else True,
15036
+ "list_burnin": _has_method(resolve_obj, "GetBurnInPresetList") if resolve_obj else True,
15037
+ "delete_burnin": _has_method(resolve_obj, "DeleteBurnInPreset") if resolve_obj else True,
15038
+ },
15039
+ "user_preferences_presets": {
15040
+ "list": _has_method(resolve_obj, "GetUserPreferencesPresetList") if resolve_obj else True,
15041
+ "save": _has_method(resolve_obj, "SaveUserPreferencesPreset") if resolve_obj else True,
15042
+ "load": _has_method(resolve_obj, "LoadUserPreferencesPreset") if resolve_obj else True,
15043
+ "delete": _has_method(resolve_obj, "DeleteUserPreferencesPreset") if resolve_obj else True,
15044
+ "import": _has_method(resolve_obj, "ImportUserPreferencesPreset") if resolve_obj else True,
15045
+ "export": _has_method(resolve_obj, "ExportUserPreferencesPreset") if resolve_obj else True,
14953
15046
  },
14954
15047
  },
14955
15048
  }
@@ -15248,6 +15341,7 @@ def _preset_lifecycle_probe(resolve_obj, project, p: Dict[str, Any]) -> Dict[str
15248
15341
  "quick_export_presets": {"available": _has_method(project, "GetQuickExportRenderPresets")},
15249
15342
  "fairlight_presets": {"available": _has_method(resolve_obj, "GetFairlightPresets")},
15250
15343
  "layout_presets": {
15344
+ "list": _has_method(resolve_obj, "GetLayoutPresetList"),
15251
15345
  "save": _has_method(resolve_obj, "SaveLayoutPreset"),
15252
15346
  "load": _has_method(resolve_obj, "LoadLayoutPreset"),
15253
15347
  "update": _has_method(resolve_obj, "UpdateLayoutPreset"),
@@ -15260,6 +15354,16 @@ def _preset_lifecycle_probe(resolve_obj, project, p: Dict[str, Any]) -> Dict[str
15260
15354
  "export_render": _has_method(resolve_obj, "ExportRenderPreset"),
15261
15355
  "import_burnin": _has_method(resolve_obj, "ImportBurnInPreset"),
15262
15356
  "export_burnin": _has_method(resolve_obj, "ExportBurnInPreset"),
15357
+ "list_burnin": _has_method(resolve_obj, "GetBurnInPresetList"),
15358
+ "delete_burnin": _has_method(resolve_obj, "DeleteBurnInPreset"),
15359
+ },
15360
+ "user_preferences_presets": {
15361
+ "list": _has_method(resolve_obj, "GetUserPreferencesPresetList"),
15362
+ "save": _has_method(resolve_obj, "SaveUserPreferencesPreset"),
15363
+ "load": _has_method(resolve_obj, "LoadUserPreferencesPreset"),
15364
+ "delete": _has_method(resolve_obj, "DeleteUserPreferencesPreset"),
15365
+ "import": _has_method(resolve_obj, "ImportUserPreferencesPreset"),
15366
+ "export": _has_method(resolve_obj, "ExportUserPreferencesPreset"),
15263
15367
  },
15264
15368
  }
15265
15369
  try:
@@ -15641,6 +15745,9 @@ def project_manager(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
15641
15745
 
15642
15746
  Actions:
15643
15747
  list() -> {projects}
15748
+ list_attributes() -> {projects: {name: {lastModifiedDate, creationDate, notes, liveCollaborationMode}}}
15749
+ — Resolve 21.0.4+. Per-project attributes for the current folder without
15750
+ loading any project.
15644
15751
  get_current() -> {name, id}
15645
15752
  create(name, media_location_path?) -> {success, name}
15646
15753
  load(name) -> {success}
@@ -15722,6 +15829,11 @@ def project_manager(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
15722
15829
  return _project_boundary_report(r, pm, proj, p)
15723
15830
  elif action == "list":
15724
15831
  return {"projects": pm.GetProjectListInCurrentFolder()}
15832
+ elif action == "list_attributes":
15833
+ missing = _requires_method(pm, "GetProjectAttributesInCurrentFolder", "21.0.4")
15834
+ if missing:
15835
+ return missing
15836
+ return {"projects": _ser(pm.GetProjectAttributesInCurrentFolder() or {})}
15725
15837
  elif action == "get_current":
15726
15838
  proj = pm.GetCurrentProject()
15727
15839
  return {"name": proj.GetName(), "id": proj.GetUniqueId()} if proj else _err("No project open")
@@ -15776,7 +15888,7 @@ def project_manager(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
15776
15888
  if not p.get("path"):
15777
15889
  return _err("restore requires path")
15778
15890
  return {"success": bool(pm.RestoreProject(p["path"], p.get("name")))}
15779
- return _unknown(action, ["list","get_current","create","load","save","close","delete","import_project","export_project","archive","restore","lint","diff_to_spec","plan_spec","apply_spec", *_PROJECT_KERNEL_ACTIONS])
15891
+ return _unknown(action, ["list","list_attributes","get_current","create","load","save","close","delete","import_project","export_project","archive","restore","lint","diff_to_spec","plan_spec","apply_spec", *_PROJECT_KERNEL_ACTIONS])
15780
15892
 
15781
15893
 
15782
15894
  # ═══════════════════════════════════════════════════════════════════════════════