davinci-resolve-mcp 2.98.6 → 2.98.7

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,63 @@
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.98.7
6
+
7
+ **Identifying the mechanism behind the Fusion comp-lock bug — and correcting the
8
+ root cause, again.** v2.98.6 recorded that four of the six locked call paths did
9
+ not reproduce the bug and that the rescuing mechanism was unknown. Isolating it
10
+ turned up something more important: the root cause stated in v2.98.5 and v2.98.6
11
+ was incomplete.
12
+
13
+ ### The precondition nobody had noticed
14
+
15
+ A lock around a value write is **necessary but not sufficient**. The suppression
16
+ only reproduces on a comp whose graph was **built through lock-wrapped
17
+ `AddTool`/`ConnectInput` calls**. The identical locked write against a graph
18
+ wired by plain attribute assignment renders normally.
19
+
20
+ That is why every raw-API attempt to reproduce the bug came back green — a
21
+ control that was supposed to fail, didn't, which is what exposed the gap. Holding
22
+ the write constant and varying only how the graph was built:
23
+
24
+ | graph built via | value written via | render |
25
+ | --- | --- | --- |
26
+ | MCP `add_tool`/`connect` | MCP, locked | **suppressed** |
27
+ | raw attribute assignment | MCP, locked | rendered |
28
+ | MCP `add_tool`/`connect` | raw, locked | **suppressed** |
29
+ | raw attribute assignment | raw, locked | rendered |
30
+
31
+ ### What rescues it
32
+
33
+ Given that precondition, with the locked write in place:
34
+
35
+ | after the locked write | render |
36
+ | --- | --- |
37
+ | nothing | **suppressed** |
38
+ | a subsequent **unlocked** value write | rescued |
39
+ | `StartUndo` / `EndUndo` around the write | rescued |
40
+ | a structural `ConnectInput` inside the lock | **suppressed** |
41
+ | a `GetInput` readback after `Unlock` | **suppressed** |
42
+
43
+ So `bulk_set_inputs` and `bulk_set_expressions` escape the bug because both wrap
44
+ their write in `StartUndo`/`EndUndo`. **That answers the open question from
45
+ v2.98.6 for two of the four.** `add_fusion_mask` and `set_text_plus` still escape
46
+ for reasons not identified — but the two obvious candidates, a structural call in
47
+ the same lock and a readback, were tested and ruled out.
48
+
49
+ ### Nothing changed in the fix
50
+
51
+ The v2.98.5 change stands and remains verified: removing the lock from the value
52
+ write is what makes these paths render, and the live harness still fails without
53
+ it. What changed is the explanation, in `api_truth` and the harness docs.
54
+
55
+ ### Added
56
+
57
+ - `tests/live_fusion_lock_mechanism_probe.py` — the isolation experiment itself,
58
+ kept runnable rather than written up and thrown away, so the result can be
59
+ re-checked on another Resolve build instead of taken on trust. It is not a
60
+ pass/fail test; it prints the five-case matrix above.
61
+
5
62
  ## What's New in v2.98.6
6
63
 
7
64
  **Correcting the scope of the v2.98.5 Fusion fix, and covering all six paths
@@ -67,6 +124,12 @@ one no readback can check.
67
124
 
68
125
  ## What's New in v2.98.5
69
126
 
127
+ > **Scope corrected in v2.98.6.** Six sites had the lock removed, but only
128
+ > `set_input` and `safe_set_inputs` were ever verified to suppress the render.
129
+ > The other four were changed by inference, and mutation-checking each of them
130
+ > afterwards showed the lock does not break them. No code changed back; only the
131
+ > claim did. See the v2.98.6 entry above for the per-site measurement.
132
+
70
133
  **Every Fusion parameter this server wrote was ignored at render.** A value
71
134
  write (`SetInput` / `SetExpression`) wrapped in `Comp.Lock()`/`Unlock()` is
72
135
  stored in the graph and reads back correctly — `GetInput` returns it, and so
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.98.6-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-2.98.7-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-35%20(353%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.98.6-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-2.98.7-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-35%20(353%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.98.6 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
15
+ > 本翻译对应 v2.98.7 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
16
16
 
17
17
  一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
18
18
 
@@ -343,7 +343,7 @@ values, or automation-hostile modal prompts.
343
343
 
344
344
  - **Object:** `Composition (Fusion, via TimelineItem comps)`
345
345
  - **Signature:** `Lock() / Unlock()`
346
- - **Behavior:** A numeric tool.SetInput() performed between Comp.Lock() and Comp.Unlock(), when that write is the only thing the call does, is stored in the graph and reads back correctly from GetInput() but is NOT applied when the timeline is rendered. Measured live on Studio 19.1.3.7 (2026-08-21) with MediaIn -> Blur(XBlurSize 20) -> MediaOut on a media-backed clip: written under the lock the delivered H.264 render is bit-identical to the no-comp baseline (ffmpeg PSNR inf); the identical write with the lock removed renders at PSNR 24.38 dB and the file shrinks 2.0 MB -> 727 KB, as a blur should. The variable was isolated against the comp handle (AddFusionComp, GetFusionCompByIndex and GetFusionCompByName all render), the node name, and the write form (attribute assignment and SetInput both render unlocked). STRUCTURAL edits are unaffected: AddTool and ConnectInput inside a lock render normally, so the lock is not broadly unsafe — it suppresses the parameter-change invalidation that a value write depends on. Lock() is widely recommended for batching Fusion edits, which is how this reaches production code. SCOPE, measured by reintroducing the lock at each site and re-rendering: it reproduces for a bare numeric SetInput (2 of 6 call paths tested) and does NOT reproduce when the same call also wraps the write in StartUndo/EndUndo, performs an AddTool, or writes a string to StyledText (4 of 6). Which of those rescues the write is not established only that they do. Treat the safe cases as unexplained rather than proven safe, and keep value writes outside the lock everywhere.
346
+ - **Behavior:** A numeric tool.SetInput() performed between Comp.Lock() and Comp.Unlock(), when that write is the only thing the call does, is stored in the graph and reads back correctly from GetInput() but is NOT applied when the timeline is rendered. Measured live on Studio 19.1.3.7 (2026-08-21) with MediaIn -> Blur(XBlurSize 20) -> MediaOut on a media-backed clip: written under the lock the delivered H.264 render is bit-identical to the no-comp baseline (ffmpeg PSNR inf); the identical write with the lock removed renders at PSNR 24.38 dB and the file shrinks 2.0 MB -> 727 KB, as a blur should. The variable was isolated against the comp handle (AddFusionComp, GetFusionCompByIndex and GetFusionCompByName all render), the node name, and the write form (attribute assignment and SetInput both render unlocked). STRUCTURAL edits are unaffected: AddTool and ConnectInput inside a lock render normally, so the lock is not broadly unsafe — it suppresses the parameter-change invalidation that a value write depends on. Lock() is widely recommended for batching Fusion edits, which is how this reaches production code. PRECONDITION (2026-08-22): the suppression only reproduces on a graph that was BUILT through lock-wrapped AddTool/ConnectInput calls. The same locked write against a graph wired by plain attribute assignment renders normally, so 'a lock around a value write' is necessary but not sufficient — the comp has to have been through prior lock cycles. RESCUERS, each verified by rendering: Comp.EndUndo() after the locked write restores it, and so does any subsequent UNLOCKED value write. NOT rescuers: a structural ConnectInput inside the same lock, and a GetInput readback after the Unlock both still render the stale frame. This explains why undo-wrapped bulk writes escape the bug; two further call shapes (a mask whose inputs are written right after AddTool, and a StyledText string write) escape it for reasons still unidentified. Keep value writes outside the lock everywhere rather than relying on an escape.
347
347
  - **Workaround / current handling:** Never hold a comp lock across a value write. Lock only structural work (AddTool/ConnectInput) and set inputs outside it. Because every readback the API offers agrees with the value that was written, this failure is invisible without a render — prove Fusion parameter changes with a delivered frame or gallery_stills grab_and_export, never with GetInput.
348
348
  - **Tags:** fusion, silent-failure, render, readback
349
349
 
package/install.py CHANGED
@@ -37,7 +37,7 @@ from src.utils.update_check import (
37
37
 
38
38
  # ─── Version ──────────────────────────────────────────────────────────────────
39
39
 
40
- VERSION = "2.98.6"
40
+ VERSION = "2.98.7"
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.98.6",
3
+ "version": "2.98.7",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -87,7 +87,7 @@ if not logging.getLogger().handlers:
87
87
  handlers=[logging.StreamHandler()],
88
88
  )
89
89
 
90
- VERSION = "2.98.6"
90
+ VERSION = "2.98.7"
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()}")
package/src/server.py CHANGED
@@ -11,7 +11,7 @@ Usage:
11
11
  python src/server.py --full # Start the 353-tool granular server instead
12
12
  """
13
13
 
14
- VERSION = "2.98.6"
14
+ VERSION = "2.98.7"
15
15
 
16
16
  import base64
17
17
  import os
@@ -427,15 +427,23 @@ API_TRUTH: List[Dict[str, Any]] = [
427
427
  "suppresses the parameter-change invalidation that a value "
428
428
  "write depends on. Lock() is widely recommended for "
429
429
  "batching Fusion edits, which is how this reaches "
430
- "production code. SCOPE, measured by reintroducing the lock "
431
- "at each site and re-rendering: it reproduces for a bare "
432
- "numeric SetInput (2 of 6 call paths tested) and does NOT "
433
- "reproduce when the same call also wraps the write in "
434
- "StartUndo/EndUndo, performs an AddTool, or writes a string "
435
- "to StyledText (4 of 6). Which of those rescues the write is "
436
- "not established only that they do. Treat the safe cases as "
437
- "unexplained rather than proven safe, and keep value writes "
438
- "outside the lock everywhere.",
430
+ "production code. PRECONDITION (2026-08-22): the "
431
+ "suppression only reproduces on a graph that was BUILT "
432
+ "through lock-wrapped AddTool/ConnectInput calls. The same "
433
+ "locked write against a graph wired by plain attribute "
434
+ "assignment renders normally, so 'a lock around a value "
435
+ "write' is necessary but not sufficient the comp has to "
436
+ "have been through prior lock cycles. RESCUERS, each "
437
+ "verified by rendering: Comp.EndUndo() after the locked "
438
+ "write restores it, and so does any subsequent UNLOCKED "
439
+ "value write. NOT rescuers: a structural ConnectInput "
440
+ "inside the same lock, and a GetInput readback after the "
441
+ "Unlock — both still render the stale frame. This explains "
442
+ "why undo-wrapped bulk writes escape the bug; two further "
443
+ "call shapes (a mask whose inputs are written right after "
444
+ "AddTool, and a StyledText string write) escape it for "
445
+ "reasons still unidentified. Keep value writes outside the "
446
+ "lock everywhere rather than relying on an escape.",
439
447
  "recommended": "Never hold a comp lock across a value write. Lock only "
440
448
  "structural work (AddTool/ConnectInput) and set inputs "
441
449
  "outside it. Because every readback the API offers "