davinci-resolve-mcp 2.224.3 → 3.0.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 +197 -0
- package/README.md +1 -1
- package/README.zh-CN.md +2 -2
- package/docs/SKILL.md +21 -13
- package/docs/authoring/script-plugin-authoring.md +33 -65
- package/docs/kernels/extension-authoring-kernel.md +8 -13
- package/install.py +1 -1
- package/package.json +1 -1
- package/scripts/install_resolve_bridge.py +9 -4
- package/src/granular/common.py +1 -1
- package/src/server.py +62 -311
- package/src/utils/destructive_hook.py +81 -1
- package/src/utils/execution_lifecycle.py +38 -10
- package/src/utils/extension_authoring_live_probe.py +4 -18
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,203 @@
|
|
|
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 v3.0.1 — project deletion is gated, and the open project is refused by default
|
|
6
|
+
|
|
7
|
+
A security fix, published as [GHSA-gmp7-qjp9-m7gm](https://github.com/samuelgursky/davinci-resolve-mcp/security/advisories/GHSA-gmp7-qjp9-m7gm).
|
|
8
|
+
One behaviour change existing callers may notice is called out below.
|
|
9
|
+
|
|
10
|
+
### Security
|
|
11
|
+
|
|
12
|
+
- **`project_manager delete` permanently deleted any named project — including
|
|
13
|
+
the one open in Resolve — without confirmation, and no gate saw it.** It runs
|
|
14
|
+
through `delete_project_safely`, a *reliability* helper that works around
|
|
15
|
+
DeleteProject's flakiness and session lock by closing the open project and
|
|
16
|
+
then deleting it. The `project_manager` tool carried no `@_destructive_op`,
|
|
17
|
+
`delete` was not registered, and the CRITICAL risk rule written for project
|
|
18
|
+
deletion named `delete_project`, an action no tool dispatches. So it matched
|
|
19
|
+
nothing: safe mode, dry-run refusal and the security audit log never saw a
|
|
20
|
+
project deletion.
|
|
21
|
+
- **Thirteen further deletes that the classifier already rated HIGH were not
|
|
22
|
+
enforced, for the same reason.** They are the three `render` deletes,
|
|
23
|
+
`render_presets.delete_burnin`, `gallery_stills.delete_stills`, `fusion_comp`
|
|
24
|
+
`delete_tool` and `delete_keyframe`, `timeline_item.delete_keyframe`, the three
|
|
25
|
+
`media_pool_item_markers` deletes, `project_settings.delete_color_group` and
|
|
26
|
+
`resolve_control.delete_user_preferences_preset`. Safe mode is enforced only
|
|
27
|
+
by the decorator, and only for registered actions, so these ratings were a
|
|
28
|
+
promise nothing kept.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **The raw `delete` now refuses the currently open project unless
|
|
33
|
+
`close_current=True`**, matching `safe_project_delete`. This is the one change
|
|
34
|
+
existing callers may notice: a call that used to close and delete the open
|
|
35
|
+
project now returns an error, until it passes `close_current=True`.
|
|
36
|
+
- All of the actions above are registered and their tools decorated. The project
|
|
37
|
+
delete is CRITICAL, and `safe_project_delete` and the thirteen others are HIGH,
|
|
38
|
+
so all are blocked while safe mode is on. An explicit dry run is refused
|
|
39
|
+
unless the action honours it natively; only `safe_project_delete` does, and
|
|
40
|
+
keeps working. Every call is audited. None of them archives the timeline,
|
|
41
|
+
except the keyframe deletes on `fusion_comp` and `timeline_item`, which change
|
|
42
|
+
timeline items and so still do.
|
|
43
|
+
- **`remove_motion_blur`** (on `folder` and `media_pool_item`) is **re-rated
|
|
44
|
+
MEDIUM**. It renders new media and never touches the source, and was already
|
|
45
|
+
confirm-gated for exactly that reason, but the `remove_` name-prefix rule had
|
|
46
|
+
rated it HIGH on its name alone. It is now audited, and not blocked by safe
|
|
47
|
+
mode.
|
|
48
|
+
- **The name-prefix rule is now a fallback for unlisted actions**: an explicit
|
|
49
|
+
lower rating wins. Before, it fired ahead of the LOW and MEDIUM tables and
|
|
50
|
+
could not be overridden. No existing rating changed except `remove_motion_blur`
|
|
51
|
+
— no LOW, MEDIUM or graph-LUT entry started with `delete_` or `remove_`.
|
|
52
|
+
|
|
53
|
+
### Fixed
|
|
54
|
+
|
|
55
|
+
- **Nine risk rules named actions that no tool dispatches, and so protected
|
|
56
|
+
nothing.** On `project_manager`: `delete_project`, now repointed at the real
|
|
57
|
+
`delete`, plus `close_project_without_saving` and `save_project_as`. On
|
|
58
|
+
`edit_engine`: `auto_cut_silence` and `ripple_trim`. On `timeline`: `cut_clip`,
|
|
59
|
+
`delete_clip_by_id`, `delete_markers` and `ripple_delete` — which is a CutList
|
|
60
|
+
entry kind inside `apply_cuts`, not an action. The rest were removed; since
|
|
61
|
+
they never matched, removing them changes nothing at runtime. An existing test
|
|
62
|
+
even asserted that the dead `delete_project` rule classified as CRITICAL — true
|
|
63
|
+
of a name no tool uses, and part of how it survived. It now tests the real
|
|
64
|
+
`delete`.
|
|
65
|
+
- **The bridge installer's Lua canary gave only the pre-21.1 diagnosis** — that
|
|
66
|
+
Resolve cannot find a Python 3. On free 21.1 that is wrong, because Python
|
|
67
|
+
scripting moved to Studio (#203). Its comments and printed output now give both
|
|
68
|
+
causes, the newer first (#219). The printed post-install guidance was already
|
|
69
|
+
corrected in v2.224.1.
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
|
|
73
|
+
- **`tests/test_write_enforcement_ratchet.py`** fails the suite in three cases:
|
|
74
|
+
- an action is rated destructive but not enforced;
|
|
75
|
+
- a risk rule names an action no tool dispatches;
|
|
76
|
+
- a new write-style action appears with neither a rating nor a registry entry.
|
|
77
|
+
|
|
78
|
+
The 144 unrated write-style actions that exist today are frozen as a backlog.
|
|
79
|
+
Rating one forces its removal from the list, so it can only shrink. This is
|
|
80
|
+
the second instance of this gap in two days, and the first time it cannot
|
|
81
|
+
come back unnoticed.
|
|
82
|
+
- `tests/test_project_delete_guard.py` pins the delete guard, its rating and its
|
|
83
|
+
enforcement.
|
|
84
|
+
|
|
85
|
+
### Documentation
|
|
86
|
+
|
|
87
|
+
- Removed a stale tool count from `docs/authoring/script-plugin-authoring.md`.
|
|
88
|
+
|
|
89
|
+
### Validation
|
|
90
|
+
|
|
91
|
+
- Full suite green: 3,482 passed, 1 skipped. Every static and drift gate is clean, including
|
|
92
|
+
the native-dry-run scan. That scan requires the native-dry-run list to match,
|
|
93
|
+
exactly, the registered actions whose handlers read `dry_run`, which is how
|
|
94
|
+
`safe_project_delete` was confirmed as the only one.
|
|
95
|
+
- No live Resolve run. The gating is decorator-level and verified offline. The
|
|
96
|
+
delete guard is tested against a fake project manager, deliberately:
|
|
97
|
+
exercising it live means deleting a real project.
|
|
98
|
+
|
|
99
|
+
## What's New in v3.0.0 — the server no longer executes caller-supplied code, and every plugin write is gated
|
|
100
|
+
|
|
101
|
+
**A breaking release.** Two public actions are removed. The rest of the change
|
|
102
|
+
is a security fix, published as [GHSA-vh75-g46q-hgcw](https://github.com/samuelgursky/davinci-resolve-mcp/security/advisories/GHSA-vh75-g46q-hgcw).
|
|
103
|
+
|
|
104
|
+
### Removed (breaking)
|
|
105
|
+
|
|
106
|
+
- **`script_plugin run_inline`** ran a caller's source directly. Python ran as a
|
|
107
|
+
subprocess on the host, with the user's privileges and a live Resolve handle.
|
|
108
|
+
Lua ran inside Resolve's Fusion engine with `os` and `io` in scope, so
|
|
109
|
+
`os.execute` reached the shell.
|
|
110
|
+
- **`script_plugin execute`** ran an installed script. `install` accepts
|
|
111
|
+
caller-supplied source, so the two together did the same thing in two steps.
|
|
112
|
+
- **`probe_script_lifecycle`'s `execute` option.** The probe now **refuses**
|
|
113
|
+
`execute=true` up front, before generating or installing anything. Skipping it
|
|
114
|
+
silently would have reported a probe as complete for a step it never ran.
|
|
115
|
+
|
|
116
|
+
Both actions shipped in v2.5.0 as documented features, and the agent guidance
|
|
117
|
+
recommended `run_inline` for conversational queries. They are removed under the
|
|
118
|
+
maintainer's policy that this server does not execute caller-supplied code, in
|
|
119
|
+
any form. Calling either now returns an error that names the removal and points
|
|
120
|
+
to the replacement, rather than a bare "unknown action".
|
|
121
|
+
|
|
122
|
+
### Migration
|
|
123
|
+
|
|
124
|
+
- Install the script with `script_plugin install`, then run it yourself from
|
|
125
|
+
**Workspace → Scripts** inside Resolve. Python output appears in Resolve's
|
|
126
|
+
Console.
|
|
127
|
+
- For queries and edits in conversation, use the typed tools.
|
|
128
|
+
|
|
129
|
+
### Security
|
|
130
|
+
|
|
131
|
+
- **Plugin-folder writes passed every gate as reads.** `install` and `remove`
|
|
132
|
+
on `dctl`, `fuse_plugin` and `script_plugin`, plus `safe_install_extension`
|
|
133
|
+
and `safe_remove_extension`, were in neither write table. The risk classifier
|
|
134
|
+
returned `recognised=False` — a bare `remove` misses the `remove_*` prefix
|
|
135
|
+
rule — the destructive registry had no entry, and `fuse_plugin` and
|
|
136
|
+
`script_plugin` carried no `@_destructive_op` at all. So safe mode, dry-run
|
|
137
|
+
refusal and the security audit log treated them as reads. These are the
|
|
138
|
+
actions that put files into folders Resolve and Fusion later load and run: a
|
|
139
|
+
Fuse registers on the next restart, a script runs when clicked.
|
|
140
|
+
- **`run_inline` and `execute` were in the same state.** With safe mode on, the
|
|
141
|
+
setting whose whole purpose is to block dangerous operations let arbitrary
|
|
142
|
+
code execution through as a read.
|
|
143
|
+
- All of this is fixed here, for every version from v2.5.0 onward, and published
|
|
144
|
+
as the advisory linked above. A read-only audit confirmed `script_plugin` was
|
|
145
|
+
the only path in the repository that ran caller-supplied code: the Node
|
|
146
|
+
advanced server spawns fixed binaries only, never with `shell: true`.
|
|
147
|
+
|
|
148
|
+
### Fixed
|
|
149
|
+
|
|
150
|
+
- **A dry run of `install` or `remove` wrote or deleted the file for real.**
|
|
151
|
+
Dry-run refusal only applies to registered actions, so `dry_run=true` was
|
|
152
|
+
silently ignored. It is now refused with `DRY_RUN_UNAVAILABLE`. For a genuine
|
|
153
|
+
preview, use `safe_install_extension` / `safe_remove_extension`, which honour
|
|
154
|
+
`dry_run` themselves.
|
|
155
|
+
- **The lifecycle probes skipped the gate.** They called the raw `_safe_*`
|
|
156
|
+
helpers directly, and `safe_remove_extension` unlinks the file itself, so
|
|
157
|
+
their installs and cleanup deletes reached disk ungated. They now go through
|
|
158
|
+
`script_plugin(...)`, and an AST guard keeps it that way.
|
|
159
|
+
- **Plugin writes would have snapshotted the open timeline.** Once registered,
|
|
160
|
+
every write falls into version-on-mutate archiving — and `dctl encrypt_native`,
|
|
161
|
+
registered in v2.224.0, already archived a timeline version on every call. A
|
|
162
|
+
new non-timeline exemption keeps these writes gated and audited but skips the
|
|
163
|
+
archive. It also never resolves the versioning context, which reaches Resolve:
|
|
164
|
+
installing a shader must neither touch the project nor launch Resolve.
|
|
165
|
+
|
|
166
|
+
### Changed — risk ratings
|
|
167
|
+
|
|
168
|
+
- `install` and `safe_install_extension`: **MEDIUM** — audited and dry-run-honest,
|
|
169
|
+
not blocked by safe mode, like the other create-style writes.
|
|
170
|
+
- `remove` and `safe_remove_extension`: **HIGH** — blocked while safe mode is on.
|
|
171
|
+
`allow_risky_operation: true` overrides a single call.
|
|
172
|
+
- Safe mode is off by default, and `confirmation_required` is informational, not
|
|
173
|
+
a token demand. So for most users the visible change is that these calls are
|
|
174
|
+
now audited, and a dry run means a dry run.
|
|
175
|
+
|
|
176
|
+
### Documentation
|
|
177
|
+
|
|
178
|
+
- `docs/SKILL.md`, `docs/authoring/script-plugin-authoring.md` (retitled; its
|
|
179
|
+
execution section replaced by how to run an installed script) and the
|
|
180
|
+
extension-authoring kernel map describe the gated, execution-free surface.
|
|
181
|
+
So does the agent-facing prompt guidance, which had told agents to prefer
|
|
182
|
+
`run_inline` for inspecting Resolve state.
|
|
183
|
+
- Two measured facts about Resolve's Lua bridge, found while building the
|
|
184
|
+
removed `run_inline`, are kept as reference because they describe Resolve
|
|
185
|
+
itself: `fusion.Execute()` is a no-op from the Python bridge in 20.x, and
|
|
186
|
+
`fusion.RunScript()` returns before the script finishes.
|
|
187
|
+
|
|
188
|
+
### Validation
|
|
189
|
+
|
|
190
|
+
- Full suite green: 3,472 passed, 1 skipped. The drop from the previous run is
|
|
191
|
+
exactly the deleted execution tests, less the five new policy tests.
|
|
192
|
+
- New tests pin both halves: every plugin write is a rated, recognised write; a
|
|
193
|
+
dry run on the real tools is refused rather than executed; the safe-install
|
|
194
|
+
dry run still works; plugin writes never archive or reach Resolve; safe mode
|
|
195
|
+
blocks deletes and not installs; the removed actions refuse with a migration
|
|
196
|
+
pointer; the probe refuses `execute` before any side effect; and an AST scan
|
|
197
|
+
finds no `RunScript`, `Execute`, `exec` or `eval` call anywhere in `src/`.
|
|
198
|
+
- No live Resolve run. The actions that remain behave as before apart from the
|
|
199
|
+
gate, which is decorator-level and verified offline. The removed actions can
|
|
200
|
+
only be verified absent, which the tests do.
|
|
201
|
+
|
|
5
202
|
## What's New in v2.224.3 — the Windows import guard covers the advanced server
|
|
6
203
|
|
|
7
204
|
Contributed by @Dev-next-gen (#222). Test-only; no behaviour changed.
|
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)
|
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
|
-
> 本翻译对应
|
|
15
|
+
> 本翻译对应 v3.0.1 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
|
|
16
16
|
|
|
17
17
|
一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
|
|
18
18
|
|
package/docs/SKILL.md
CHANGED
|
@@ -118,39 +118,47 @@ directories. They are *authoring* tools — every other tool in this server wrap
|
|
|
118
118
|
Resolve's scripting API, while these three emit and install plugin/script
|
|
119
119
|
source. Status: lifecycle-verified in DaVinci Resolve Studio 20.3.2.9 for
|
|
120
120
|
MCP-marked install/read/list/remove, regular DCTL `refresh_luts`, ACES/Fuse
|
|
121
|
-
restart-required classification
|
|
122
|
-
|
|
121
|
+
restart-required classification. Script execution — `execute` and `run_inline` —
|
|
122
|
+
was removed in v3.0.0: this server does not run caller-supplied code. Use
|
|
123
|
+
`docs/kernels/extension-authoring-kernel.md` for the
|
|
123
124
|
kernel boundary map, `docs/authoring/fuse-dctl-authoring.md` for the Fuse + DCTL coverage
|
|
124
125
|
matrix, and `docs/authoring/script-plugin-authoring.md` for the script DSL spec and the
|
|
125
|
-
|
|
126
|
+
install paths. For hand-authoring `.setting` template files
|
|
126
127
|
(Edit effects/transitions/titles/generators and Fusion macros) — the format,
|
|
127
128
|
control catalog, thumbnail conventions, install paths, and gotchas, plus copyable
|
|
128
129
|
starter templates — see `docs/authoring/setting-files/`.
|
|
129
130
|
|
|
131
|
+
**Plugin writes are gated like every other write.** `install` and `remove` on all
|
|
132
|
+
three tools, and `script_plugin`'s `safe_install_extension` / `safe_remove_extension`,
|
|
133
|
+
are registered destructive actions. An explicit `dry_run=true` on `install` or
|
|
134
|
+
`remove` is refused with `DRY_RUN_UNAVAILABLE` rather than executed — for a real
|
|
135
|
+
preview use `safe_install_extension` / `safe_remove_extension`, which honour
|
|
136
|
+
`dry_run` themselves. `remove` is rated HIGH and is blocked while safe mode is on
|
|
137
|
+
(`allow_risky_operation: true` overrides a single call); `install` is MEDIUM.
|
|
138
|
+
Every call is recorded in the security audit log, and none of them archives the
|
|
139
|
+
timeline — they write plugin folders, not the project. The `probe_*_lifecycle`
|
|
140
|
+
actions route their installs and cleanup deletes through the same gate.
|
|
141
|
+
|
|
130
142
|
Extension Authoring kernel actions (v2.16.0+) are exposed through
|
|
131
143
|
`script_plugin`:
|
|
132
144
|
|
|
133
145
|
- `extension_capabilities`
|
|
134
146
|
- `probe_fuse_lifecycle(name?, kind?, install?, cleanup?)`
|
|
135
147
|
- `probe_dctl_lifecycle(name?, kind?, category?, install?, refresh_luts?, cleanup?)`
|
|
136
|
-
- `probe_script_lifecycle(name?, language?, category?, install?,
|
|
148
|
+
- `probe_script_lifecycle(name?, language?, category?, install?, cleanup?)`
|
|
137
149
|
- `safe_install_extension(extension_type, name, source?|kind?, dry_run?)`
|
|
138
150
|
- `safe_remove_extension(extension_type, name, dry_run?)`
|
|
139
151
|
- `refresh_or_restart_required(extension_type, category?)`
|
|
140
152
|
- `extension_boundary_report(include_template_matrix?)`
|
|
141
153
|
|
|
142
154
|
Key behavioral notes for `script_plugin`:
|
|
143
|
-
- `run_inline
|
|
144
|
-
|
|
145
|
-
Resolve
|
|
155
|
+
- **No script execution.** `run_inline` and `execute` were removed in v3.0.0:
|
|
156
|
+
this server does not run caller-supplied code, in any form. `install` puts a
|
|
157
|
+
script in Resolve's Workspace › Scripts menu; running it is the user's action
|
|
158
|
+
inside Resolve. For conversational queries against the Resolve API, use the
|
|
159
|
+
typed tools rather than a script.
|
|
146
160
|
- `language` accepts `lua`, `py`, or the human-facing aliases `python` and
|
|
147
161
|
`python3`.
|
|
148
|
-
- `execute(name, category, language)` runs an installed script; Python stdout
|
|
149
|
-
and stderr are captured, while installed Lua execution can return false from
|
|
150
|
-
the Python bridge even when install/read/list/remove worked.
|
|
151
|
-
- Lua scripts: `fusion.Execute()` from the Python bridge is a no-op in
|
|
152
|
-
Resolve 20.x — `_run_inline_lua` works around this with `RunScript` against
|
|
153
|
-
a temp file plus completion-sentinel polling on `app:SetData/GetData`.
|
|
154
162
|
- Fuse install path on macOS is `…/DaVinci Resolve/Fusion/Fuses/` (NOT
|
|
155
163
|
`Support/Fusion/Fuses/` as the SDK doc lists). The MCP path helpers handle
|
|
156
164
|
this; if you're staging files manually, use the path the implementation
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# Script Plugin Authoring
|
|
1
|
+
# Script Plugin Authoring
|
|
2
2
|
|
|
3
|
-
The `script_plugin` compound tool (introduced in v2.5.0) generates,
|
|
4
|
-
and
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
The `script_plugin` compound tool (introduced in v2.5.0) generates, validates
|
|
4
|
+
and installs Resolve-page Lua/Python scripts. **It does not run them.** Script
|
|
5
|
+
execution — `run_inline` and `execute` — was removed in v3.0.0, because this
|
|
6
|
+
server does not execute caller-supplied code. An installed script appears as a
|
|
7
|
+
Resolve menu item, and running it is the user's action, inside Resolve.
|
|
8
8
|
|
|
9
9
|
Unlike `fuse_plugin` (which authors Fusion image-processing tools) and `dctl`
|
|
10
10
|
(which authors color-page shaders), `script_plugin` targets the
|
|
@@ -15,11 +15,11 @@ automation.
|
|
|
15
15
|
|
|
16
16
|
| Goal | Use |
|
|
17
17
|
|---|---|
|
|
18
|
-
| One-off
|
|
19
|
-
| Custom workflow you want as a permanent menu item | `script_plugin('install', ...)
|
|
18
|
+
| One-off query or change against Resolve | The typed Resolve API tools — no script, no execution |
|
|
19
|
+
| Custom workflow you want as a permanent menu item | `script_plugin('install', ...)`, then the user runs it from Workspace → Scripts |
|
|
20
20
|
| Image-processing node for the Fusion page | `fuse_plugin` |
|
|
21
21
|
| Color-page programmable transform | `dctl` |
|
|
22
|
-
| Anything the
|
|
22
|
+
| Anything the wrapped Resolve API tools already cover | The wrapped tool — no scripting needed |
|
|
23
23
|
|
|
24
24
|
## Two template kinds
|
|
25
25
|
|
|
@@ -93,43 +93,24 @@ Real-world example: a script supervisor's CSV with Filename, Scene, Take,
|
|
|
93
93
|
Camera, Lens columns. Single rule maps each clip to its row and populates
|
|
94
94
|
all metadata fields plus organizes into Scene bins. Six lines of RULES.
|
|
95
95
|
|
|
96
|
-
##
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
**Lua**: wraps source so `print()` is intercepted into a buffer, runs via
|
|
108
|
-
`fusion.RunScript()`, polls a completion sentinel, reads stdout + return
|
|
109
|
-
value back via `app:SetData()`/`fusion.GetData()`. (Note: `fusion.Execute()`
|
|
110
|
-
from the Python bridge is a no-op in Resolve 20.x — `RunScript()` against a
|
|
111
|
-
file is the only working path. The implementation handles this.)
|
|
112
|
-
|
|
113
|
-
Example:
|
|
114
|
-
```python
|
|
115
|
-
script_plugin('run_inline', {
|
|
116
|
-
'source': '''
|
|
117
|
-
print(f"Project: {project.GetName()}")
|
|
118
|
-
print(f"Bins: {len(mp.GetRootFolder().GetSubFolderList() or [])}")
|
|
119
|
-
''',
|
|
120
|
-
'language': 'py',
|
|
121
|
-
})
|
|
122
|
-
# → {success: True, stdout: "Project: My Show\nBins: 12\n", exit_code: 0}
|
|
123
|
-
```
|
|
96
|
+
## Running an installed script
|
|
97
|
+
|
|
98
|
+
`script_plugin` installs scripts; it does not run them. v3.0.0 removed the two
|
|
99
|
+
actions that did:
|
|
100
|
+
|
|
101
|
+
- `run_inline` ran a caller's source directly — Python as a subprocess on the
|
|
102
|
+
host, with a live Resolve handle, or Lua inside Resolve's Fusion engine with
|
|
103
|
+
`os` and `io` in scope.
|
|
104
|
+
- `execute` ran an installed script, which `install` could have just written
|
|
105
|
+
from caller-supplied source.
|
|
124
106
|
|
|
125
|
-
|
|
126
|
-
|
|
107
|
+
Neither passed any of the server's safety gates, and the maintainer policy is
|
|
108
|
+
that the server never executes caller-supplied code. Calling either now returns
|
|
109
|
+
an error that says so and points here.
|
|
127
110
|
|
|
128
|
-
**
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
to `app:SetData()` and the caller reads via the existing `fusion_comp`
|
|
132
|
-
tooling.
|
|
111
|
+
After `install`, the user runs the script from **Workspace → Scripts →
|
|
112
|
+
\<category\>** inside Resolve; Python output appears in Resolve's Console. For
|
|
113
|
+
queries and edits in conversation, use the typed tools.
|
|
133
114
|
|
|
134
115
|
## Install paths
|
|
135
116
|
|
|
@@ -158,32 +139,19 @@ familiar for data-heavy workflows. The same RULES table syntax works in both
|
|
|
158
139
|
Verified on DaVinci Resolve Studio 20.3.2.9, macOS:
|
|
159
140
|
|
|
160
141
|
- ✅ Scripts appear in Workspace → Scripts → \<category\> after install (no restart needed)
|
|
161
|
-
- ⚠️ Installed Lua script execution via `fusion.RunScript(path)` can return
|
|
162
|
-
`False` from the Python bridge even when install/read/list/remove work. Use
|
|
163
|
-
`run_inline(language="lua")` when captured output or return values matter.
|
|
164
|
-
- ✅ Python scripts execute via subprocess with full stdout/stderr capture
|
|
165
|
-
- ✅ `run_inline` Lua: stdout captured (with tabs), return value captured, errors trapped with line numbers
|
|
166
|
-
- ✅ `run_inline` Python: full Resolve API access, project + media-pool + timeline pre-bound
|
|
167
142
|
- ✅ Both engines (Lua and Python) compile without errors
|
|
168
143
|
- ✅ DSL coverage tests confirm every documented source/action/target/transform/strategy is present in both engines
|
|
169
144
|
|
|
170
|
-
##
|
|
171
|
-
|
|
172
|
-
Two non-obvious behaviors of Resolve's Lua bridge surfaced during live
|
|
173
|
-
testing and are encoded in the implementation:
|
|
174
|
-
|
|
175
|
-
1. **`fusion.Execute(luaSource)` is a no-op** when called from the Python
|
|
176
|
-
`DaVinciResolveScript` bridge in Resolve 20.x. It returns `None` and has
|
|
177
|
-
no observable side effects. Don't use it. Use `fusion.RunScript(filepath)`
|
|
178
|
-
against a temp file instead.
|
|
145
|
+
## Resolve's Lua bridge — reference
|
|
179
146
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
implementation polls a completion-sentinel slot (`__mcp_done__`) until
|
|
183
|
-
the wrapped Lua sets it to `"1"`, then reads results.
|
|
147
|
+
Two measured facts about Resolve itself, found while building the now-removed
|
|
148
|
+
`run_inline`, stay true of Resolve and are kept here for reference:
|
|
184
149
|
|
|
185
|
-
|
|
186
|
-
|
|
150
|
+
1. **`fusion.Execute(luaSource)` is a no-op** from the Python
|
|
151
|
+
`DaVinciResolveScript` bridge in Resolve 20.x: it returns `None` with no
|
|
152
|
+
observable side effects.
|
|
153
|
+
2. **`fusion.RunScript(filepath)` is asynchronous**: it returns before the
|
|
154
|
+
script finishes, so an immediate `fusion.GetData()` reads stale values.
|
|
187
155
|
|
|
188
156
|
## Source media integrity
|
|
189
157
|
|
|
@@ -33,7 +33,7 @@ All kernel actions are exposed through `script_plugin`.
|
|
|
33
33
|
| `extension_capabilities` | Report Fuse, DCTL, script paths, template kinds, MCP markers, lifecycle rules, and safety guards. |
|
|
34
34
|
| `probe_fuse_lifecycle` | Generate, validate, optionally install/read/list/remove a Fuse template. |
|
|
35
35
|
| `probe_dctl_lifecycle` | Generate, validate, optionally install/read/list/remove a LUT or ACES DCTL template. |
|
|
36
|
-
| `probe_script_lifecycle` | Generate, validate, optionally install/read/list/
|
|
36
|
+
| `probe_script_lifecycle` | Generate, validate, optionally install/read/list/remove a Resolve-page script. Refuses `execute` (removed in v3.0.0). |
|
|
37
37
|
| `safe_install_extension` | Install Fuse, DCTL, or script source/templates with `_mcp_` name and marker guards. |
|
|
38
38
|
| `safe_remove_extension` | Remove Fuse, DCTL, or script files only when the file is MCP-marked by default. |
|
|
39
39
|
| `refresh_or_restart_required` | Classify whether an extension needs LUT refresh, menu refresh, UI reload, or Resolve restart. |
|
|
@@ -47,8 +47,6 @@ All kernel actions are exposed through `script_plugin`.
|
|
|
47
47
|
| Regular DCTL | LUT directory | `project_settings.refresh_luts` picks it up. | Not required for LUT-category DCTLs. |
|
|
48
48
|
| ACES IDT/ODT DCTL | ACES Transforms IDT/ODT | Not picked up by LUT refresh. | Required. |
|
|
49
49
|
| Resolve-page script | Fusion/Scripts category directory | Workspace Scripts menu refreshes when opened. | Not required. |
|
|
50
|
-
| Inline Python script | Temp file subprocess | Captured synchronously. | Not required. |
|
|
51
|
-
| Inline Lua script | Temp Lua file via `fusion.RunScript` | Captured through Fusion app data bridge. | Not required. |
|
|
52
50
|
|
|
53
51
|
## Supported Findings
|
|
54
52
|
|
|
@@ -59,20 +57,17 @@ All kernel actions are exposed through `script_plugin`.
|
|
|
59
57
|
- ACES IDT DCTL template generation, install into `ACES Transforms/IDT/MCP`,
|
|
60
58
|
read, list, and safe remove worked. It remains restart-required before Resolve
|
|
61
59
|
can use the transform.
|
|
62
|
-
- Python Resolve-page script template generation, install, read, list,
|
|
63
|
-
|
|
64
|
-
- `script_plugin.run_inline` worked for Python with stdout capture.
|
|
65
|
-
- `script_plugin.run_inline` worked for Lua with stdout and return-value capture.
|
|
60
|
+
- Python Resolve-page script template generation, install, read, list, and safe
|
|
61
|
+
remove worked.
|
|
66
62
|
- The template matrix generated and validated every Fuse, DCTL, and script
|
|
67
63
|
template kind.
|
|
68
64
|
- Safe install rejected unmarked provided source by default.
|
|
69
65
|
|
|
70
66
|
## Boundaries
|
|
71
67
|
|
|
72
|
-
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
captured output/return values matter.
|
|
68
|
+
- **No script execution.** `run_inline` and `execute` were removed in v3.0.0:
|
|
69
|
+
the server does not execute caller-supplied code, and the probes no longer run
|
|
70
|
+
scripts. Installed scripts are run by the user from Workspace → Scripts.
|
|
76
71
|
- New Fuses still require a Resolve restart to appear as registered Fusion
|
|
77
72
|
tools. The MCP can install/remove files but cannot force Fusion to register a
|
|
78
73
|
new Fuse in-process.
|
|
@@ -93,8 +88,8 @@ python3.11 tests/live_extension_authoring_validation.py --output-dir /tmp/extens
|
|
|
93
88
|
```
|
|
94
89
|
|
|
95
90
|
The harness creates a disposable `_mcp_` project, installs and removes a
|
|
96
|
-
generated Fuse, regular DCTL, ACES DCTL, Python script, and Lua script,
|
|
97
|
-
|
|
91
|
+
generated Fuse, regular DCTL, ACES DCTL, Python script, and Lua script, writes
|
|
92
|
+
JSON and Markdown reports, deletes the
|
|
98
93
|
project, and removes its temp work directory.
|
|
99
94
|
|
|
100
95
|
Use `--keep-open` only when you intentionally want to inspect the disposable
|
package/install.py
CHANGED
|
@@ -37,7 +37,7 @@ from src.utils.update_check import (
|
|
|
37
37
|
|
|
38
38
|
# ─── Version ──────────────────────────────────────────────────────────────────
|
|
39
39
|
|
|
40
|
-
VERSION = "
|
|
40
|
+
VERSION = "3.0.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
|
@@ -288,7 +288,9 @@ _CANARY_NAME = "resolve_bridge_canary.lua"
|
|
|
288
288
|
|
|
289
289
|
_LUA_CANARY = """-- Installed by davinci-resolve-mcp as an enumeration canary.
|
|
290
290
|
-- If THIS appears under Workspace > Scripts but resolve_bridge_probe does not,
|
|
291
|
-
-- Resolve is listing Lua and silently skipping Python
|
|
291
|
+
-- Resolve is listing Lua and silently skipping Python. On Resolve 21.1+ FREE
|
|
292
|
+
-- that is expected: Python scripting moved to Studio (issue #203), and nothing
|
|
293
|
+
-- below will change it. On Studio, or 21.0.x and earlier, it cannot find a Python 3.
|
|
292
294
|
-- It looks at PYTHON3HOME, then /usr/local/bin/python3 -- and nowhere else, which
|
|
293
295
|
-- is why Homebrew, pyenv, uv and conda interpreters go unseen. Either point it at
|
|
294
296
|
-- the one you have (no sudo, but does NOT survive a reboot):
|
|
@@ -299,9 +301,12 @@ _LUA_CANARY = """-- Installed by davinci-resolve-mcp as an enumeration canary.
|
|
|
299
301
|
-- already looks, which persists:
|
|
300
302
|
-- sudo ln -s "$(command -v python3)" /usr/local/bin/python3
|
|
301
303
|
-- A python.org build creates that symlink for you. Restart Resolve after.
|
|
302
|
-
print("Resolve is enumerating scripts. If the Python probe is missing
|
|
303
|
-
print("
|
|
304
|
-
print("
|
|
304
|
+
print("Resolve is enumerating scripts. If the Python probe is missing:")
|
|
305
|
+
print("- Resolve 21.1+ FREE: Python scripting moved to Studio, so .py scripts")
|
|
306
|
+
print(" no longer list at all (issue #203). No Python setting changes that.")
|
|
307
|
+
print("- Studio, or 21.0.x and earlier: Resolve cannot find a Python 3. Set")
|
|
308
|
+
print(" PYTHON3HOME with launchctl setenv, or install a python.org build.")
|
|
309
|
+
print(" Homebrew/pyenv/uv/conda are not looked at directly.")
|
|
305
310
|
"""
|
|
306
311
|
|
|
307
312
|
|
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 = "
|
|
90
|
+
VERSION = "3.0.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()}")
|