davinci-resolve-mcp 2.215.2 → 2.216.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 +31 -0
- package/README.md +4 -4
- package/README.zh-CN.md +5 -5
- package/docs/SKILL.md +5 -1
- package/docs/contributing.md +1 -1
- package/docs/install.md +2 -2
- package/docs/reference/api-coverage.md +20 -4
- package/docs/reference/resolve211-read-controls.md +60 -0
- package/install.py +2 -2
- package/package.json +1 -1
- package/src/granular/__init__.py +1 -0
- package/src/granular/common.py +1 -1
- package/src/granular/resolve_211.py +148 -0
- package/src/resolve_mcp_server.py +1 -1
- package/src/server.py +87 -8
- package/src/utils/resolve_versions.py +14 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,37 @@
|
|
|
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.216.0 — twelve Resolve 21.1 read-only controls, in both server modes
|
|
6
|
+
|
|
7
|
+
Contributed by @legionsound (#206), live-measured on Studio 21.1.0.14.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Twelve readers for data 21.1 exposes natively**, each as a compound
|
|
12
|
+
action and a granular tool: `resolve_control is_studio`,
|
|
13
|
+
`get_keyboard_presets`, `get_current_keyboard_preset`; `project_settings
|
|
14
|
+
get_project_settings_presets`; `render get_audio_formats`,
|
|
15
|
+
`get_audio_codecs(format)`; `timeline get_normalize_audio_modes`,
|
|
16
|
+
`get_output_blanking`; `timeline_item get_speed`, `get_fades`,
|
|
17
|
+
`get_output_blanking`, `get_use_timeline_for_output_blanking`. Native
|
|
18
|
+
values pass through untouched, including `false`, empty inherited blanking
|
|
19
|
+
and fractional fade durations; the four blanking values are pixel
|
|
20
|
+
coordinates, not margins. A build without the method returns an explicit
|
|
21
|
+
"requires DaVinci Resolve 21.1+" error — confirmed here on Studio 19.1.3.7,
|
|
22
|
+
where all twelve refuse cleanly. The granular count moves from 353 to 365
|
|
23
|
+
tools; docs, generated agent rules, the version ledger and the coverage
|
|
24
|
+
reference are updated together. `GetProjectLastModifiedTime` was deferred
|
|
25
|
+
because it returned `None` for existing projects on the test install.
|
|
26
|
+
See `docs/reference/resolve211-read-controls.md`.
|
|
27
|
+
|
|
28
|
+
### Validation
|
|
29
|
+
|
|
30
|
+
- The PR's offline contracts (absent methods, argument forwarding, preserved
|
|
31
|
+
false/empty payloads, invalid locators), the full offline suite, drift
|
|
32
|
+
guards and the advanced Node suite. Positive payloads are the contributor's
|
|
33
|
+
measurement on 21.1.0.14 through both interfaces; the version-floor refusal
|
|
34
|
+
is measured here on 19.1.3.7.
|
|
35
|
+
|
|
5
36
|
## What's New in v2.215.2 — the granular server accepts Resolve 21.1's lowercase item types; the 21.1 typed API ships as reference
|
|
6
37
|
|
|
7
38
|
Both contributed by @legionsound (#204, #205), measured 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` | 365 | 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 / **365** 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.216.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` | 365 | 想要"一个 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** 复合 / **365** 细粒度(实时服务器) |
|
|
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,11 @@ 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` | 365 tools | Power users needing one tool per API method |
|
|
349
|
+
|
|
350
|
+
Resolve 21.1 adds [twelve read-only discovery controls](reference/resolve211-read-controls.md)
|
|
351
|
+
for edition, presets, audio formats/codecs, normalization modes, speed, fades
|
|
352
|
+
and blanking in both server interfaces. These readers do not invoke setters.
|
|
349
353
|
|
|
350
354
|
This skill document covers the **compound server** (the default). Each compound
|
|
351
355
|
tool accepts an `action` string and an optional `params` object.
|
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 — 365 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` | 365 | 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 365-tool server
|
|
163
163
|
# Or point your MCP config directly at src/resolve_mcp_server.py
|
|
164
164
|
```
|
|
165
165
|
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
# API Coverage and Test Results
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Legacy-reference coverage, incremental Resolve 21.1 coverage, live-test status,
|
|
4
|
+
and method-by-method reference.
|
|
4
5
|
|
|
5
6
|
## Key Stats
|
|
6
7
|
|
|
8
|
+
The API totals below refer to the bundled legacy README baseline. Resolve 21.1
|
|
9
|
+
additions are tracked separately in the section below.
|
|
10
|
+
|
|
7
11
|
| Metric | Value |
|
|
8
12
|
|--------|-------|
|
|
9
|
-
| MCP Tools | **36** compound (default) / **
|
|
13
|
+
| MCP Tools | **36** compound (default) / **365** granular |
|
|
10
14
|
| Kernel Actions | **136** guarded MCP workflow actions across 9 compound tools |
|
|
11
15
|
| API Methods Covered | **361/361** (100%) |
|
|
12
16
|
| Methods Live Tested | **338/361** (93.6%) |
|
|
@@ -17,7 +21,18 @@ Complete Resolve scripting API coverage, live-test status, and method-by-method
|
|
|
17
21
|
|
|
18
22
|
## API Coverage
|
|
19
23
|
|
|
20
|
-
Every non-deprecated method in the
|
|
24
|
+
Every non-deprecated method in the bundled legacy README is represented. This
|
|
25
|
+
does not claim complete coverage of the newer Resolve 21.1 typed API. The
|
|
26
|
+
default compound server exposes **36 tools** that group related operations by
|
|
27
|
+
action parameter, keeping LLM context windows lean. The full granular server
|
|
28
|
+
provides **365 individual tools** for power users. The legacy coverage spans
|
|
29
|
+
13 API object classes. MCP-level kernel actions are tracked separately in
|
|
30
|
+
[Kernel Action Coverage](../kernels/README.md).
|
|
31
|
+
|
|
32
|
+
Twelve additional [Resolve 21.1 read-only controls](resolve211-read-controls.md)
|
|
33
|
+
are implemented in both interfaces and were live-measured by a contributor on
|
|
34
|
+
Studio 21.1.0.14. Their evidence and limitations are recorded separately from
|
|
35
|
+
the historical aggregate above.
|
|
21
36
|
|
|
22
37
|
The 34th compound tool is `timeline_versioning` (C6) — an MCP-level workflow
|
|
23
38
|
tool, not a wrapper around a Resolve API method. It surfaces the
|
|
@@ -28,7 +43,8 @@ for usage.
|
|
|
28
43
|
Workflow helpers can go beyond one-to-one API method coverage while still using
|
|
29
44
|
only public Resolve calls. For example, `media_pool.setup_multicam_timeline`
|
|
30
45
|
prepares a stacked timeline for Resolve's multicam UI, but native multicam clip
|
|
31
|
-
creation
|
|
46
|
+
creation is available in Resolve 21.1 through CreateMulticamClip; that native
|
|
47
|
+
route is not implemented by this helper. Similarly,
|
|
32
48
|
`media_analysis.detect_sync_events` is a source-safe FFmpeg/FFprobe helper for
|
|
33
49
|
advisory 2-pop and slate-clap sync points; it is not a Resolve API method.
|
|
34
50
|
`media_analysis.add_sync_event_markers` is an explicit marker-write helper for
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Resolve 21.1 read-only discovery
|
|
2
|
+
|
|
3
|
+
Twelve readers expose native 21.1 data in both compound and granular modes.
|
|
4
|
+
They do not load presets, alter timeline settings, create render jobs, change
|
|
5
|
+
clips or perform normalization. Calls require a callable native method; older
|
|
6
|
+
builds without it return an explicit method/version error.
|
|
7
|
+
|
|
8
|
+
| Compound tool/action | Granular tool | Result |
|
|
9
|
+
|---|---|---|
|
|
10
|
+
| resolve_control is_studio | is_resolve_studio | is_studio boolean |
|
|
11
|
+
| resolve_control get_keyboard_presets | get_keyboard_presets | presets list |
|
|
12
|
+
| resolve_control get_current_keyboard_preset | get_current_keyboard_preset | active preset name |
|
|
13
|
+
| project_settings get_project_settings_presets | get_project_settings_presets | preset records with Name, Width, Height |
|
|
14
|
+
| render get_audio_formats | get_audio_render_formats | format descriptions mapped to extensions |
|
|
15
|
+
| render get_audio_codecs | get_audio_render_codecs | codec descriptions mapped to native codec identifiers |
|
|
16
|
+
| timeline get_normalize_audio_modes | get_normalize_audio_modes | native normalization-mode names |
|
|
17
|
+
| timeline get_output_blanking | get_timeline_output_blanking | blanking dictionary |
|
|
18
|
+
| timeline_item get_speed | get_timeline_item_speed | native speed options |
|
|
19
|
+
| timeline_item get_fades | get_timeline_item_fades | FadeIn/FadeOut frame durations |
|
|
20
|
+
| timeline_item get_output_blanking | get_timeline_item_output_blanking | blanking dictionary; empty when inherited |
|
|
21
|
+
| timeline_item get_use_timeline_for_output_blanking | get_timeline_item_use_timeline_for_output_blanking | use_timeline boolean |
|
|
22
|
+
|
|
23
|
+
For audio codecs, `format` is a file extension from the audio formats reader,
|
|
24
|
+
for example `wav`, not the display label `Wave`. Empty and non-string inputs
|
|
25
|
+
are refused before making the native call.
|
|
26
|
+
|
|
27
|
+
Clip readers accept `track_type` (`video` or `audio`), a 1-based `track_index`
|
|
28
|
+
and a 0-based `item_index`; defaults are video, 1, 0. Granular readers refuse
|
|
29
|
+
negative item indexes and invalid track types/indexes before lookup. These are
|
|
30
|
+
the existing compound item-location conventions.
|
|
31
|
+
|
|
32
|
+
The payload is passed through without coercing false, empty lists/dictionaries
|
|
33
|
+
or fractional frame durations. Empty clip blanking is valid when inheritance
|
|
34
|
+
is enabled. The four blanking values are native pixel coordinates; do not
|
|
35
|
+
reinterpret them as four independent margin widths. The speed reader returns
|
|
36
|
+
the native options rather than trying to derive speed from source/timeline
|
|
37
|
+
frame spans.
|
|
38
|
+
|
|
39
|
+
## Validation and limits
|
|
40
|
+
|
|
41
|
+
Contributed live measurement on macOS **DaVinci Resolve Studio 21.1.0.14**,
|
|
42
|
+
September 9, 2026: all twelve readers returned matching domain payloads through
|
|
43
|
+
both interfaces. Compound `_operation` instrumentation is separate from that
|
|
44
|
+
domain payload. No project/preset/clip mutation was invoked.
|
|
45
|
+
|
|
46
|
+
```sh
|
|
47
|
+
python -m unittest tests.test_resolve211_read_controls
|
|
48
|
+
python tests/live_resolve211_read_controls.py
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The live script requires an existing project and timeline with a normal video
|
|
52
|
+
clip on video track 1. Its receipt omits project, clip and preset names.
|
|
53
|
+
Offline contracts exercise all readers, absent methods, preserved false/empty
|
|
54
|
+
values, required argument forwarding, and invalid granular item locators.
|
|
55
|
+
|
|
56
|
+
This is not a claim of full 21.1 API coverage, or a promise that corresponding
|
|
57
|
+
setters/multicam/transitions/normalization are implemented. The generic
|
|
58
|
+
GetProjectLastModifiedTime reader was intentionally deferred after the native
|
|
59
|
+
API returned None for several existing projects on the test installation.
|
|
60
|
+
That observation needs investigation rather than an advertised timestamp.
|
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.216.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 · 365 full · 3 platforms"
|
|
1547
1547
|
print()
|
|
1548
1548
|
print(bold(" ╔══════════════════════════════════════════════════════╗"))
|
|
1549
1549
|
print(bold(f" ║{title:^54}║"))
|
package/package.json
CHANGED
package/src/granular/__init__.py
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.216.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()}")
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""Read-only discovery added to the typed Resolve 21.1 API surface."""
|
|
2
|
+
from src.granular.common import (
|
|
3
|
+
mcp, READ_ONLY_TOOL, get_resolve, get_current_project,
|
|
4
|
+
_get_timeline, _get_timeline_item, _requires_method, has_method,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@mcp.tool(annotations=READ_ONLY_TOOL)
|
|
9
|
+
def is_resolve_studio() -> dict:
|
|
10
|
+
"""Read IsStudio (documented on Resolve 21.1+)."""
|
|
11
|
+
r = get_resolve()
|
|
12
|
+
if r is None:
|
|
13
|
+
return {"error": "Not connected to DaVinci Resolve"}
|
|
14
|
+
if not has_method(r, "IsStudio"):
|
|
15
|
+
return {"error": "IsStudio is unavailable on this Resolve build"}
|
|
16
|
+
return {"is_studio": r.IsStudio()}
|
|
17
|
+
|
|
18
|
+
@mcp.tool(annotations=READ_ONLY_TOOL)
|
|
19
|
+
def get_keyboard_presets() -> dict:
|
|
20
|
+
"""Read GetKeyboardPresetList (documented on Resolve 21.1+)."""
|
|
21
|
+
r = get_resolve()
|
|
22
|
+
if r is None:
|
|
23
|
+
return {"error": "Not connected to DaVinci Resolve"}
|
|
24
|
+
missing = _requires_method(r, "GetKeyboardPresetList", "21.1")
|
|
25
|
+
if missing:
|
|
26
|
+
return missing
|
|
27
|
+
return {"presets": r.GetKeyboardPresetList()}
|
|
28
|
+
|
|
29
|
+
@mcp.tool(annotations=READ_ONLY_TOOL)
|
|
30
|
+
def get_current_keyboard_preset() -> dict:
|
|
31
|
+
"""Read GetCurrentKeyboardPreset (documented on Resolve 21.1+)."""
|
|
32
|
+
r = get_resolve()
|
|
33
|
+
if r is None:
|
|
34
|
+
return {"error": "Not connected to DaVinci Resolve"}
|
|
35
|
+
missing = _requires_method(r, "GetCurrentKeyboardPreset", "21.1")
|
|
36
|
+
if missing:
|
|
37
|
+
return missing
|
|
38
|
+
return {"name": r.GetCurrentKeyboardPreset()}
|
|
39
|
+
|
|
40
|
+
@mcp.tool(annotations=READ_ONLY_TOOL)
|
|
41
|
+
def get_project_settings_presets() -> dict:
|
|
42
|
+
"""Read GetProjectSettingsPresetList (documented on Resolve 21.1+)."""
|
|
43
|
+
_, proj = get_current_project()
|
|
44
|
+
if proj is None:
|
|
45
|
+
return {"error": "No project currently open"}
|
|
46
|
+
missing = _requires_method(proj, "GetProjectSettingsPresetList", "21.1")
|
|
47
|
+
if missing:
|
|
48
|
+
return missing
|
|
49
|
+
return {"presets": proj.GetProjectSettingsPresetList()}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@mcp.tool(annotations=READ_ONLY_TOOL)
|
|
53
|
+
def get_audio_render_formats() -> dict:
|
|
54
|
+
"""Read GetAudioRenderFormats (documented on Resolve 21.1+)."""
|
|
55
|
+
_, proj = get_current_project()
|
|
56
|
+
if proj is None:
|
|
57
|
+
return {"error": "No project currently open"}
|
|
58
|
+
missing = _requires_method(proj, "GetAudioRenderFormats", "21.1")
|
|
59
|
+
if missing:
|
|
60
|
+
return missing
|
|
61
|
+
return {"formats": proj.GetAudioRenderFormats()}
|
|
62
|
+
|
|
63
|
+
@mcp.tool(annotations=READ_ONLY_TOOL)
|
|
64
|
+
def get_audio_render_codecs(format: str) -> dict:
|
|
65
|
+
"""Read GetAudioRenderCodecs (documented on Resolve 21.1+). Pass an audio file extension such as wav."""
|
|
66
|
+
if not isinstance(format, str) or not format.strip():
|
|
67
|
+
return {"error": "get_audio_codecs requires a non-empty format string"}
|
|
68
|
+
_, proj = get_current_project()
|
|
69
|
+
if proj is None:
|
|
70
|
+
return {"error": "No project currently open"}
|
|
71
|
+
missing = _requires_method(proj, "GetAudioRenderCodecs", "21.1")
|
|
72
|
+
if missing:
|
|
73
|
+
return missing
|
|
74
|
+
return {"codecs": proj.GetAudioRenderCodecs(format)}
|
|
75
|
+
|
|
76
|
+
@mcp.tool(annotations=READ_ONLY_TOOL)
|
|
77
|
+
def get_normalize_audio_modes() -> dict:
|
|
78
|
+
"""Read GetNormalizeAudioModes (documented on Resolve 21.1+)."""
|
|
79
|
+
_, tl, error = _get_timeline()
|
|
80
|
+
if error:
|
|
81
|
+
return error
|
|
82
|
+
missing = _requires_method(tl, "GetNormalizeAudioModes", "21.1")
|
|
83
|
+
if missing:
|
|
84
|
+
return missing
|
|
85
|
+
return {"modes": tl.GetNormalizeAudioModes()}
|
|
86
|
+
|
|
87
|
+
@mcp.tool(annotations=READ_ONLY_TOOL)
|
|
88
|
+
def get_timeline_output_blanking() -> dict:
|
|
89
|
+
"""Read GetOutputBlanking (documented on Resolve 21.1+). Pixel coordinates; clips inheriting timeline blanking return an empty dict."""
|
|
90
|
+
_, tl, error = _get_timeline()
|
|
91
|
+
if error:
|
|
92
|
+
return error
|
|
93
|
+
missing = _requires_method(tl, "GetOutputBlanking", "21.1")
|
|
94
|
+
if missing:
|
|
95
|
+
return missing
|
|
96
|
+
return {"blanking": tl.GetOutputBlanking()}
|
|
97
|
+
|
|
98
|
+
@mcp.tool(annotations=READ_ONLY_TOOL)
|
|
99
|
+
def get_timeline_item_speed(track_type: str = "video", track_index: int = 1, item_index: int = 0) -> dict:
|
|
100
|
+
"""Read GetSpeed (documented on Resolve 21.1+)."""
|
|
101
|
+
if track_type not in ("video", "audio") or track_index < 1 or item_index < 0:
|
|
102
|
+
return {"error": "Use video/audio, a 1-based track index and a non-negative item index"}
|
|
103
|
+
item, error = _get_timeline_item(track_type, track_index, item_index)
|
|
104
|
+
if error:
|
|
105
|
+
return error
|
|
106
|
+
missing = _requires_method(item, "GetSpeed", "21.1")
|
|
107
|
+
if missing:
|
|
108
|
+
return missing
|
|
109
|
+
return {"speed": item.GetSpeed()}
|
|
110
|
+
|
|
111
|
+
@mcp.tool(annotations=READ_ONLY_TOOL)
|
|
112
|
+
def get_timeline_item_fades(track_type: str = "video", track_index: int = 1, item_index: int = 0) -> dict:
|
|
113
|
+
"""Read GetFades (documented on Resolve 21.1+). Durations are in frames."""
|
|
114
|
+
if track_type not in ("video", "audio") or track_index < 1 or item_index < 0:
|
|
115
|
+
return {"error": "Use video/audio, a 1-based track index and a non-negative item index"}
|
|
116
|
+
item, error = _get_timeline_item(track_type, track_index, item_index)
|
|
117
|
+
if error:
|
|
118
|
+
return error
|
|
119
|
+
missing = _requires_method(item, "GetFades", "21.1")
|
|
120
|
+
if missing:
|
|
121
|
+
return missing
|
|
122
|
+
return {"fades": item.GetFades()}
|
|
123
|
+
|
|
124
|
+
@mcp.tool(annotations=READ_ONLY_TOOL)
|
|
125
|
+
def get_timeline_item_output_blanking(track_type: str = "video", track_index: int = 1, item_index: int = 0) -> dict:
|
|
126
|
+
"""Read GetOutputBlanking (documented on Resolve 21.1+). Pixel coordinates; clips inheriting timeline blanking return an empty dict."""
|
|
127
|
+
if track_type not in ("video", "audio") or track_index < 1 or item_index < 0:
|
|
128
|
+
return {"error": "Use video/audio, a 1-based track index and a non-negative item index"}
|
|
129
|
+
item, error = _get_timeline_item(track_type, track_index, item_index)
|
|
130
|
+
if error:
|
|
131
|
+
return error
|
|
132
|
+
missing = _requires_method(item, "GetOutputBlanking", "21.1")
|
|
133
|
+
if missing:
|
|
134
|
+
return missing
|
|
135
|
+
return {"blanking": item.GetOutputBlanking()}
|
|
136
|
+
|
|
137
|
+
@mcp.tool(annotations=READ_ONLY_TOOL)
|
|
138
|
+
def get_timeline_item_use_timeline_for_output_blanking(track_type: str = "video", track_index: int = 1, item_index: int = 0) -> dict:
|
|
139
|
+
"""Read GetUseTimelineForOutputBlanking (documented on Resolve 21.1+)."""
|
|
140
|
+
if track_type not in ("video", "audio") or track_index < 1 or item_index < 0:
|
|
141
|
+
return {"error": "Use video/audio, a 1-based track index and a non-negative item index"}
|
|
142
|
+
item, error = _get_timeline_item(track_type, track_index, item_index)
|
|
143
|
+
if error:
|
|
144
|
+
return error
|
|
145
|
+
missing = _requires_method(item, "GetUseTimelineForOutputBlanking", "21.1")
|
|
146
|
+
if missing:
|
|
147
|
+
return missing
|
|
148
|
+
return {"use_timeline": item.GetUseTimelineForOutputBlanking()}
|
|
@@ -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} (365 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 365-tool granular server instead
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
VERSION = "2.
|
|
14
|
+
VERSION = "2.216.0"
|
|
15
15
|
|
|
16
16
|
import base64
|
|
17
17
|
import os
|
|
@@ -16571,6 +16571,9 @@ def resolve_control(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
16571
16571
|
"""App-level DaVinci Resolve operations.
|
|
16572
16572
|
|
|
16573
16573
|
Actions:
|
|
16574
|
+
is_studio() -> {is_studio} — documented on Resolve 21.1+.
|
|
16575
|
+
get_keyboard_presets() -> {presets} — documented on Resolve 21.1+.
|
|
16576
|
+
get_current_keyboard_preset() -> {name} — documented on Resolve 21.1+.
|
|
16574
16577
|
launch(headless?) -> {success, message, running, headless, guidance}
|
|
16575
16578
|
— Launch DaVinci Resolve if not running. Call this FIRST if any tool returns
|
|
16576
16579
|
a 'Not connected' error. headless=true starts it with no UI (-nogui):
|
|
@@ -16928,6 +16931,21 @@ def resolve_control(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
16928
16931
|
if r is None:
|
|
16929
16932
|
return _not_connected_error()
|
|
16930
16933
|
|
|
16934
|
+
if action == "is_studio":
|
|
16935
|
+
if not _has_method(r, "IsStudio"):
|
|
16936
|
+
return _err("IsStudio is unavailable on this Resolve build")
|
|
16937
|
+
return {"is_studio": _ser(r.IsStudio())}
|
|
16938
|
+
if action == "get_keyboard_presets":
|
|
16939
|
+
missing = _requires_method(r, "GetKeyboardPresetList", "21.1")
|
|
16940
|
+
if missing:
|
|
16941
|
+
return missing
|
|
16942
|
+
return {"presets": _ser(r.GetKeyboardPresetList())}
|
|
16943
|
+
if action == "get_current_keyboard_preset":
|
|
16944
|
+
missing = _requires_method(r, "GetCurrentKeyboardPreset", "21.1")
|
|
16945
|
+
if missing:
|
|
16946
|
+
return missing
|
|
16947
|
+
return {"name": _ser(r.GetCurrentKeyboardPreset())}
|
|
16948
|
+
|
|
16931
16949
|
if action == "get_version":
|
|
16932
16950
|
update_env = _setup_update_env()
|
|
16933
16951
|
mcp_update = get_cached_update_status(project_dir, VERSION, env=update_env)
|
|
@@ -17048,7 +17066,7 @@ def resolve_control(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
|
|
|
17048
17066
|
if err:
|
|
17049
17067
|
return _err(err)
|
|
17050
17068
|
return {"success": bool(r.ExportUserPreferencesPreset(clean["name"], clean["path"]))}
|
|
17051
|
-
return _unknown(action, ["launch","runtime_mode","get_version","api_truth","check_version_support","verification_stats","job_status","list_jobs","get_execution_trace","get_execution","list_recent_executions","begin_execution","end_execution","export_execution_report","clear_executions","inspect_operation","list_lifecycle_hooks","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"])
|
|
17069
|
+
return _unknown(action, ["is_studio","get_keyboard_presets","get_current_keyboard_preset","launch","runtime_mode","get_version","api_truth","check_version_support","verification_stats","job_status","list_jobs","get_execution_trace","get_execution","list_recent_executions","begin_execution","end_execution","export_execution_report","clear_executions","inspect_operation","list_lifecycle_hooks","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"])
|
|
17052
17070
|
|
|
17053
17071
|
|
|
17054
17072
|
# ─── V2 C4: Per-field corrections with provenance + changelog ────────────────
|
|
@@ -19342,6 +19360,7 @@ def project_settings(action: str, params: Optional[Dict[str, Any]] = None) -> Di
|
|
|
19342
19360
|
"""Project metadata, settings, and color groups.
|
|
19343
19361
|
|
|
19344
19362
|
Actions:
|
|
19363
|
+
get_project_settings_presets() -> {presets} — documented on Resolve 21.1+.
|
|
19345
19364
|
get_name() -> {name}
|
|
19346
19365
|
set_name(name) -> {success}
|
|
19347
19366
|
get_setting(name?) -> {settings} — omit name for all settings
|
|
@@ -19370,6 +19389,12 @@ def project_settings(action: str, params: Optional[Dict[str, Any]] = None) -> Di
|
|
|
19370
19389
|
if err:
|
|
19371
19390
|
return err
|
|
19372
19391
|
|
|
19392
|
+
if action == "get_project_settings_presets":
|
|
19393
|
+
missing = _requires_method(proj, "GetProjectSettingsPresetList", "21.1")
|
|
19394
|
+
if missing:
|
|
19395
|
+
return missing
|
|
19396
|
+
return {"presets": _ser(proj.GetProjectSettingsPresetList())}
|
|
19397
|
+
|
|
19373
19398
|
if action == "get_name":
|
|
19374
19399
|
return {"name": proj.GetName()}
|
|
19375
19400
|
elif action == "set_name":
|
|
@@ -19497,7 +19522,7 @@ def project_settings(action: str, params: Optional[Dict[str, Any]] = None) -> Di
|
|
|
19497
19522
|
result = _ai_result_payload(proj.ResetIntellisearchAnalysis())
|
|
19498
19523
|
_rec.success = result["success"]
|
|
19499
19524
|
return result
|
|
19500
|
-
return _unknown(action, ["get_name","set_name","get_setting","set_setting","get_unique_id","get_presets","set_preset","refresh_luts","get_gallery","export_frame_as_still","project_summary","load_burnin_preset","insert_audio","get_color_groups","add_color_group","delete_color_group","apply_fairlight_preset","generate_speech","reset_intellisearch_analysis"])
|
|
19525
|
+
return _unknown(action, ["get_project_settings_presets","get_name","set_name","get_setting","set_setting","get_unique_id","get_presets","set_preset","refresh_luts","get_gallery","export_frame_as_still","project_summary","load_burnin_preset","insert_audio","get_color_groups","add_color_group","delete_color_group","apply_fairlight_preset","generate_speech","reset_intellisearch_analysis"])
|
|
19501
19526
|
|
|
19502
19527
|
|
|
19503
19528
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -20311,6 +20336,8 @@ def render(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, An
|
|
|
20311
20336
|
"""Render pipeline: jobs, presets, formats, codecs, and rendering.
|
|
20312
20337
|
|
|
20313
20338
|
Actions:
|
|
20339
|
+
get_audio_formats() -> {formats} — documented on Resolve 21.1+.
|
|
20340
|
+
get_audio_codecs(format) -> {codecs} — documented on Resolve 21.1+. Audio file extension, e.g. wav.
|
|
20314
20341
|
add_job() -> {job_id}
|
|
20315
20342
|
delete_job(job_id) -> {success}
|
|
20316
20343
|
delete_all_jobs() -> {success}
|
|
@@ -20582,6 +20609,19 @@ def render(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, An
|
|
|
20582
20609
|
return _ok()
|
|
20583
20610
|
elif action == "is_rendering":
|
|
20584
20611
|
return {"rendering": bool(proj.IsRenderingInProgress())}
|
|
20612
|
+
elif action == "get_audio_formats":
|
|
20613
|
+
missing = _requires_method(proj, "GetAudioRenderFormats", "21.1")
|
|
20614
|
+
if missing:
|
|
20615
|
+
return missing
|
|
20616
|
+
return {"formats": _ser(proj.GetAudioRenderFormats())}
|
|
20617
|
+
elif action == "get_audio_codecs":
|
|
20618
|
+
if not isinstance(p.get("format"), str) or not p["format"].strip():
|
|
20619
|
+
return _err("get_audio_codecs requires a non-empty format string")
|
|
20620
|
+
missing = _requires_method(proj, "GetAudioRenderCodecs", "21.1")
|
|
20621
|
+
if missing:
|
|
20622
|
+
return missing
|
|
20623
|
+
return {"codecs": _ser(proj.GetAudioRenderCodecs(p["format"]))}
|
|
20624
|
+
|
|
20585
20625
|
elif action == "get_formats":
|
|
20586
20626
|
return {"formats": _ser(proj.GetRenderFormats())}
|
|
20587
20627
|
elif action == "get_codecs":
|
|
@@ -20699,7 +20739,7 @@ def render(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, An
|
|
|
20699
20739
|
return _safe_quick_export(proj, p)
|
|
20700
20740
|
elif action == "export_render_boundary_report":
|
|
20701
20741
|
return _export_render_boundary_report(proj, p)
|
|
20702
|
-
return _unknown(action, ["add_job","delete_job","delete_all_jobs","list_jobs","get_job_status","verify_output","start","stop","is_rendering","get_formats","get_codecs","get_format_and_codec","set_format_and_codec","get_mode","set_mode","get_resolutions","get_settings","set_settings","list_presets","load_preset","save_preset","delete_preset","quick_export_presets","quick_export",*_RENDER_KERNEL_ACTIONS])
|
|
20742
|
+
return _unknown(action, ["get_audio_formats","get_audio_codecs","add_job","delete_job","delete_all_jobs","list_jobs","get_job_status","verify_output","start","stop","is_rendering","get_formats","get_codecs","get_format_and_codec","set_format_and_codec","get_mode","set_mode","get_resolutions","get_settings","set_settings","list_presets","load_preset","save_preset","delete_preset","quick_export_presets","quick_export",*_RENDER_KERNEL_ACTIONS])
|
|
20703
20743
|
|
|
20704
20744
|
|
|
20705
20745
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -25003,6 +25043,7 @@ def edit_engine(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[st
|
|
|
25003
25043
|
|
|
25004
25044
|
|
|
25005
25045
|
_TIMELINE_ACTIONS = [
|
|
25046
|
+
"get_normalize_audio_modes", "get_output_blanking",
|
|
25006
25047
|
# Offline authoring — served without a Resolve connection, above the _check() gate.
|
|
25007
25048
|
"author_offline", "offline_fallback_capabilities",
|
|
25008
25049
|
"list", "get_current", "set_current", "get_name", "set_name", "get_start_frame",
|
|
@@ -25051,6 +25092,8 @@ def timeline(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str,
|
|
|
25051
25092
|
(resolve_control api_truth "GetSourceStartFrame").
|
|
25052
25093
|
|
|
25053
25094
|
Actions:
|
|
25095
|
+
get_normalize_audio_modes() -> {modes} — documented on Resolve 21.1+.
|
|
25096
|
+
get_output_blanking() -> {blanking} — documented on Resolve 21.1+. Pixel coordinates; empty on a clip inheriting timeline blanking.
|
|
25054
25097
|
list() -> {timelines}
|
|
25055
25098
|
get_current() -> {name, id, start_frame, end_frame, start_timecode}
|
|
25056
25099
|
set_current(index|id|name) -> {success} — id/name are stable across archives; index is 1-based
|
|
@@ -25367,6 +25410,17 @@ def timeline(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str,
|
|
|
25367
25410
|
if not tl:
|
|
25368
25411
|
return _err("No current timeline")
|
|
25369
25412
|
|
|
25413
|
+
if action == "get_normalize_audio_modes":
|
|
25414
|
+
missing = _requires_method(tl, "GetNormalizeAudioModes", "21.1")
|
|
25415
|
+
if missing:
|
|
25416
|
+
return missing
|
|
25417
|
+
return {"modes": _ser(tl.GetNormalizeAudioModes())}
|
|
25418
|
+
if action == "get_output_blanking":
|
|
25419
|
+
missing = _requires_method(tl, "GetOutputBlanking", "21.1")
|
|
25420
|
+
if missing:
|
|
25421
|
+
return missing
|
|
25422
|
+
return {"blanking": _ser(tl.GetOutputBlanking())}
|
|
25423
|
+
|
|
25370
25424
|
if action == "clip_where":
|
|
25371
25425
|
return _timeline_clip_where(tl, p)
|
|
25372
25426
|
if action == "get_current":
|
|
@@ -26218,6 +26272,10 @@ def timeline_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[
|
|
|
26218
26272
|
Identify by track_type, track_index, item_index (item_index is 0-BASED: 0 = first clip; track_index is 1-based).
|
|
26219
26273
|
|
|
26220
26274
|
Actions:
|
|
26275
|
+
get_speed(...) -> {speed} — documented on Resolve 21.1+.
|
|
26276
|
+
get_fades(...) -> {fades} — documented on Resolve 21.1+. Native frame durations.
|
|
26277
|
+
get_output_blanking(...) -> {blanking} — documented on Resolve 21.1+. Pixel coordinates; empty on a clip inheriting timeline blanking.
|
|
26278
|
+
get_use_timeline_for_output_blanking(...) -> {use_timeline} — documented on Resolve 21.1+.
|
|
26221
26279
|
get_name(track_type?, track_index?, item_index?) -> {name}
|
|
26222
26280
|
get_property(key?, ...) -> {properties}
|
|
26223
26281
|
set_property(key, value, ...) -> {success}
|
|
@@ -26268,6 +26326,27 @@ def timeline_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[
|
|
|
26268
26326
|
if err:
|
|
26269
26327
|
return err
|
|
26270
26328
|
|
|
26329
|
+
if action == "get_speed":
|
|
26330
|
+
missing = _requires_method(item, "GetSpeed", "21.1")
|
|
26331
|
+
if missing:
|
|
26332
|
+
return missing
|
|
26333
|
+
return {"speed": _ser(item.GetSpeed())}
|
|
26334
|
+
if action == "get_fades":
|
|
26335
|
+
missing = _requires_method(item, "GetFades", "21.1")
|
|
26336
|
+
if missing:
|
|
26337
|
+
return missing
|
|
26338
|
+
return {"fades": _ser(item.GetFades())}
|
|
26339
|
+
if action == "get_output_blanking":
|
|
26340
|
+
missing = _requires_method(item, "GetOutputBlanking", "21.1")
|
|
26341
|
+
if missing:
|
|
26342
|
+
return missing
|
|
26343
|
+
return {"blanking": _ser(item.GetOutputBlanking())}
|
|
26344
|
+
if action == "get_use_timeline_for_output_blanking":
|
|
26345
|
+
missing = _requires_method(item, "GetUseTimelineForOutputBlanking", "21.1")
|
|
26346
|
+
if missing:
|
|
26347
|
+
return missing
|
|
26348
|
+
return {"use_timeline": _ser(item.GetUseTimelineForOutputBlanking())}
|
|
26349
|
+
|
|
26271
26350
|
if action == "get_name":
|
|
26272
26351
|
return {"name": item.GetName()}
|
|
26273
26352
|
elif action == "get_property":
|
|
@@ -26443,7 +26522,7 @@ def timeline_item(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[
|
|
|
26443
26522
|
return _err(f"Invalid interpolation. Must be one of: {', '.join(valid)}")
|
|
26444
26523
|
return {"success": bool(item.SetKeyframeInterpolation(p["property"], p["frame"], p["interpolation"]))}
|
|
26445
26524
|
|
|
26446
|
-
return _unknown(action, ["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"])
|
|
26525
|
+
return _unknown(action, ["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"])
|
|
26447
26526
|
|
|
26448
26527
|
|
|
26449
26528
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -32565,9 +32644,9 @@ if __name__ == "__main__":
|
|
|
32565
32644
|
start_background_update_check(VERSION, project_dir, logger, env=_setup_update_env())
|
|
32566
32645
|
_install_threaded_tool_dispatch(mcp)
|
|
32567
32646
|
|
|
32568
|
-
# Support --full flag to run the
|
|
32647
|
+
# Support --full flag to run the 365-tool granular server instead
|
|
32569
32648
|
if "--full" in sys.argv:
|
|
32570
|
-
logger.info("Starting full
|
|
32649
|
+
logger.info("Starting full 365-tool granular server...")
|
|
32571
32650
|
sys.argv = [arg for arg in sys.argv if arg != "--full"]
|
|
32572
32651
|
from src.granular import mcp as granular_mcp
|
|
32573
32652
|
|
|
@@ -225,6 +225,20 @@ _EVIDENCE_GATES: List[Dict[str, Any]] = [
|
|
|
225
225
|
# disagree, which is what keeps the two from drifting apart again.
|
|
226
226
|
|
|
227
227
|
CODE_FLOORS: Dict[str, str] = {
|
|
228
|
+
# Documented in the shipped 21.1 scripting CHANGELOG; read-only contributor
|
|
229
|
+
# validation on Studio 21.1.0.14. No claim of a live older-build bisect.
|
|
230
|
+
"Resolve.GetKeyboardPresetList": "21.1",
|
|
231
|
+
"Resolve.GetCurrentKeyboardPreset": "21.1",
|
|
232
|
+
"Project.GetProjectSettingsPresetList": "21.1",
|
|
233
|
+
"Project.GetAudioRenderFormats": "21.1",
|
|
234
|
+
"Project.GetAudioRenderCodecs": "21.1",
|
|
235
|
+
"Timeline.GetNormalizeAudioModes": "21.1",
|
|
236
|
+
"Timeline.GetOutputBlanking": "21.1",
|
|
237
|
+
"TimelineItem.GetSpeed": "21.1",
|
|
238
|
+
"TimelineItem.GetFades": "21.1",
|
|
239
|
+
"TimelineItem.GetOutputBlanking": "21.1",
|
|
240
|
+
"TimelineItem.GetUseTimelineForOutputBlanking": "21.1",
|
|
241
|
+
|
|
228
242
|
"MediaPoolItem.LinkProxyMedia": "17.0",
|
|
229
243
|
"MediaPoolItem.LinkFullResolutionMedia": "20.0",
|
|
230
244
|
"MediaPoolItem.MonitorGrowingFile": "20.0",
|