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 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
- [![Version](https://img.shields.io/badge/version-2.224.3-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-3.0.1-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-36%20(377%20full)-blue.svg)](#server-modes)
package/README.zh-CN.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [English](README.md) | 简体中文
4
4
 
5
- [![Version](https://img.shields.io/badge/version-2.224.3-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-3.0.1-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-36%20(377%20full)-blue.svg)](#服务器模式)
@@ -12,7 +12,7 @@
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.224.3 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
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, Python installed-script execution, and
122
- Python/Lua `run_inline`. Use `docs/kernels/extension-authoring-kernel.md` for the
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
- conversational-execution model. For hand-authoring `.setting` template files
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?, execute?, cleanup?)`
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(source, language)` runs ad-hoc Lua/Python in Resolve and returns
144
- stdout + result use this for one-off conversational queries against the
145
- Resolve API instead of building+installing a script.
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 & Conversational Resolve Scripting
1
+ # Script Plugin Authoring
2
2
 
3
- The `script_plugin` compound tool (introduced in v2.5.0) generates, installs,
4
- and **executes** Resolve-page Lua/Python scripts. It closes the conversational
5
- loop: an LLM with access to the MCP can describe a workflow, generate the
6
- script, install it as a Resolve menu item, and execute it — all in one turn —
7
- with the script's stdout streamed back into the conversation.
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 conversational query against Resolve | `script_plugin('run_inline', ...)` |
19
- | Custom workflow you want as a permanent menu item | `script_plugin('install', ...)` then `('execute', ...)` |
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 existing 28 wrapped Resolve API tools already cover | The wrapped tool — no scripting needed |
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
- ## Conversational execution: `run_inline` and `execute`
97
-
98
- The two actions that close the loop:
99
-
100
- ### `run_inline(source, language, timeout?)`
101
- Run an ad-hoc Lua or Python snippet inside Resolve, get stdout + return
102
- value back. No file persistence.
103
-
104
- **Python**: writes source to a temp file with `resolve`/`project`/`mp`/
105
- `timeline` pre-bound, runs as subprocess, captures stdout/stderr.
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
- ### `execute(name, category, language, args?, timeout?)`
126
- Run an installed script. Same return shape as `run_inline`.
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
- **Python**: subprocess captures full stdout/stderr.
129
- **Lua**: `fusion.RunScript()`; print() output goes to Resolve Console
130
- (can't capture). For Lua scripts that need to return data, have them write
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
- ## Implementation notes (for maintainers)
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
- 2. **`fusion.RunScript()` is asynchronous.** It returns before the script
181
- finishes. Reading `fusion.GetData()` immediately gives stale values. The
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
- These constraints are unique to the Lua side; Python's subprocess approach
186
- is straightforwardly synchronous.
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/execute/remove a Resolve-page script. |
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, execute,
63
- stdout/stderr capture, and safe remove worked.
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
- - Installed Lua script execution through `fusion.RunScript(path)` returned
73
- `success=False` in the release probe, even though install/read/list/remove
74
- worked and inline Lua execution worked. Use `run_inline(language="lua")` when
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, probes
97
- inline Python/Lua execution, writes JSON and Markdown reports, deletes the
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 = "2.224.3"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "davinci-resolve-mcp",
3
- "version": "2.224.3",
3
+ "version": "3.0.1",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -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: it cannot find a Python 3.
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, Resolve")
303
- print("cannot find a Python 3: set PYTHON3HOME with launchctl setenv, or install")
304
- print("a python.org build. Homebrew/pyenv/uv/conda are not looked at directly.")
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
 
@@ -87,7 +87,7 @@ if not logging.getLogger().handlers:
87
87
  handlers=[logging.StreamHandler()],
88
88
  )
89
89
 
90
- VERSION = "2.224.3"
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()}")