davinci-resolve-mcp 4.8.3 → 4.8.4

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,39 @@
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 v4.8.4 — a Fusion nest control is refused with the controls it folds named
6
+
7
+ ### Fixed
8
+
9
+ - **`fusion_comp add_keyframe` on a nest control (`Softness1`, the Follower's
10
+ `TransformSize`, `Size1`, …) answered a generic `FUSION_ADD_MODIFIER_FAILED` with no
11
+ way forward.** ([#253](https://github.com/samuelgursky/davinci-resolve-mcp/issues/253), reported by @artpavelalex-ux as a follow-up to #250)
12
+ **Measured on Studio 19.1.3.7:** some entries `GetInputList()` returns are not
13
+ values at all. Inputs whose `INPID_InputControl` is `NestControl` (`INPB_Passive`
14
+ true) are the fold-down group headers the Fusion UI draws — `TextPlus Softness1`,
15
+ and on the text Follower `TransformSize` (display name "Size"), `Softness1` and
16
+ `Size1`. `Tool.AddModifier` returns False for them on every modifier type
17
+ (BezierSpline, Path, TextScramble all measured), so nothing could ever keyframe
18
+ them; this is not Follower-specific. The controls a header folds are the next
19
+ `INPI_LabelControl_NumInputs` entries in `GetInputList()` order —
20
+ `Softness1` → `SoftnessX1`, `SoftnessY1`, `SoftnessOnFillColorToo1`, `SoftnessGlow1`,
21
+ `SoftnessBlend1`; `TransformSize` → `LineSizeX/Y`, `WordSizeX/Y`, `CharacterSizeX/Y`;
22
+ `Size1` → `SizeX1`, `SizeY1` — and those take a spline normally.
23
+ - `add_keyframe` and `add_modifier` now detect a nest control before touching Fusion
24
+ and refuse it with **`FUSION_INPUT_IS_NEST_CONTROL`**, naming the folded controls
25
+ in the remediation and in `error.state.nest_members` (`_fusion_nest_members`).
26
+ - New `api_truth` entry `Tool.AddModifier (NestControl inputs)`, mapped on
27
+ `add_keyframe` and `add_modifier` results as a `known_limitation`.
28
+ - **Live-validated on landing through the real actions** on a disposable timeline:
29
+ the refusal named exactly those members on the Follower and on TextPlus;
30
+ `add_modifier` on `TransformSize` refused the same way; `SoftnessX1`,
31
+ `CharacterSizeX` and `Delay` keyframed and read back. Unit tests in
32
+ `tests/test_fusion_nest_control.py` against fakes whose input list is handed back
33
+ unsorted, so the member order is proven to come from the list order, not luck.
34
+ - The report itself arrived as an empty template with only its title; the
35
+ measurement was made from the title. Not measured: nests on tools other than
36
+ TextPlus and the Follower, and builds other than 19.1.3.7.
37
+
5
38
  ## What's New in v4.8.3 — nested folder ids resolve for delete and move
6
39
 
7
40
  ### Fixed
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-4.8.3-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-4.8.4-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-37%20(389%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-4.8.3-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-4.8.4-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-37%20(389%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
- > 本翻译对应 v4.8.3 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
15
+ > 本翻译对应 v4.8.4 版 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
@@ -2047,7 +2047,10 @@ Key actions:
2047
2047
  - `get_inputs(tool_name)` / `get_outputs(tool_name)`
2048
2048
  - `set_attrs(tool_name, attrs)` / `get_attrs(tool_name)`
2049
2049
  - `add_keyframe(tool_name, input_name, time, value, modifier?)` — attaches a
2050
- BezierSpline (or `modifier`, e.g. `Path` for Point inputs) on first use
2050
+ BezierSpline (or `modifier`, e.g. `Path` for Point inputs) on first use. A nest
2051
+ control (a fold-down group header like `Softness1` or the Follower's
2052
+ `TransformSize`) is refused with `FUSION_INPUT_IS_NEST_CONTROL` naming the
2053
+ controls it folds (`SoftnessX1`/`SoftnessY1`, `CharacterSizeX`/`Y`, ...); keyframe those
2051
2054
  - `add_modifier(tool_name, input_name, modifier)` → `{modifier_tool, modifier_type}`
2052
2055
  — attach any modifier and get back the tool Fusion created, so a text modifier
2053
2056
  (`Follower` on a TextPlus `StyledText`) can be driven with `set_input` /
package/install.py CHANGED
@@ -37,7 +37,7 @@ from src.utils.update_check import (
37
37
 
38
38
  # ─── Version ──────────────────────────────────────────────────────────────────
39
39
 
40
- VERSION = "4.8.3"
40
+ VERSION = "4.8.4"
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": "4.8.3",
3
+ "version": "4.8.4",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -93,7 +93,7 @@ if not logging.getLogger().handlers:
93
93
  handlers=[logging.StreamHandler()],
94
94
  )
95
95
 
96
- VERSION = "4.8.3"
96
+ VERSION = "4.8.4"
97
97
  logger = logging.getLogger("davinci-resolve-mcp")
98
98
  logger.info(f"Starting DaVinci Resolve MCP Server v{VERSION}")
99
99
  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 377-tool granular server instead
12
12
  """
13
13
 
14
- VERSION = "4.8.3"
14
+ VERSION = "4.8.4"
15
15
 
16
16
  import base64
17
17
  import os
@@ -30530,6 +30530,61 @@ def _fusion_modifier_id(name: Any) -> str:
30530
30530
  return _FUSION_MODIFIER_IDS.get(text.lower(), text)
30531
30531
 
30532
30532
 
30533
+ def _fusion_nest_members(tool, input_name: str):
30534
+ """(is_nest, member_ids) for an input that is a Fusion NestControl header.
30535
+
30536
+ Measured on Studio 19.1.3.7 (issue #253): inputs whose `INPID_InputControl`
30537
+ is "NestControl" (`INPB_Passive` true) are the fold-down group headers the
30538
+ Fusion UI draws — TextPlus `Softness1`, Follower `TransformSize`, `Softness1`,
30539
+ `Size1` — not animatable values. `Tool.AddModifier` returns False for them on
30540
+ every modifier type, and so did every attempt to keyframe them. The controls
30541
+ the header folds are the next `INPI_LabelControl_NumInputs` entries in
30542
+ `GetInputList()` order (Softness1 -> SoftnessX1, SoftnessY1,
30543
+ SoftnessOnFillColorToo1, SoftnessGlow1, SoftnessBlend1; TransformSize ->
30544
+ Line/Word/CharacterSize X and Y), and those take a spline normally.
30545
+ """
30546
+ try:
30547
+ attrs = tool[input_name].GetAttrs() or {}
30548
+ except Exception:
30549
+ return False, []
30550
+ if attrs.get("INPID_InputControl") != "NestControl":
30551
+ return False, []
30552
+ try:
30553
+ count = int(attrs.get("INPI_LabelControl_NumInputs") or 0)
30554
+ except (TypeError, ValueError):
30555
+ count = 0
30556
+ members: List[str] = []
30557
+ try:
30558
+ input_list = tool.GetInputList() or {}
30559
+ keys = list(input_list.keys())
30560
+ try:
30561
+ keys.sort(key=float)
30562
+ except (TypeError, ValueError):
30563
+ pass
30564
+ ids = [((input_list[k].GetAttrs() or {}).get("INPS_ID") or "") for k in keys]
30565
+ if input_name in ids:
30566
+ start = ids.index(input_name) + 1
30567
+ members = [i for i in ids[start:start + count] if i]
30568
+ except Exception:
30569
+ members = []
30570
+ return True, members
30571
+
30572
+
30573
+ def _fusion_nest_control_error(tool_name: str, input_name: str, members: List[str], verb: str):
30574
+ listed = ", ".join(members) if members else "see get_inputs(tool_name)"
30575
+ return _err(
30576
+ f"'{input_name}' on '{tool_name}' is a nest control (a group header), "
30577
+ f"not an animatable input; it cannot be {verb}.",
30578
+ code="FUSION_INPUT_IS_NEST_CONTROL", category="invalid_input", retryable=False,
30579
+ reason="Fusion NestControl inputs are passive headers that fold a group of "
30580
+ "controls. Tool.AddModifier returns False for them on every modifier "
30581
+ "type (measured on Studio 19.1.3.7: TextPlus Softness1, Follower "
30582
+ "TransformSize / Softness1 / Size1).",
30583
+ remediation=f"Target one of the controls the nest folds instead: {listed}.",
30584
+ state={"nest_control": input_name, "nest_members": members},
30585
+ )
30586
+
30587
+
30533
30588
  def _fusion_input_spline(inp):
30534
30589
  """The modifier/spline tool driving `inp`, or None when it is not animated.
30535
30590
 
@@ -30686,7 +30741,10 @@ def fusion_comp(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[st
30686
30741
  add_keyframe(tool_name, input_name, time, value, modifier?) -> {success}
30687
30742
  Attaches a BezierSpline (or `modifier`, e.g. 'Path' for Point inputs)
30688
30743
  the first time an input is animated. Modifier names are mapped to
30689
- their registry ID ('Follower' -> 'StyledTextFollower').
30744
+ their registry ID ('Follower' -> 'StyledTextFollower'). A nest control
30745
+ (a fold-down group header such as Softness1 or TransformSize) is refused
30746
+ with FUSION_INPUT_IS_NEST_CONTROL naming the controls it folds
30747
+ (SoftnessX1/SoftnessY1, ...): keyframe those.
30690
30748
  add_modifier(tool_name, input_name, modifier) -> {success, modifier_tool, modifier_type}
30691
30749
  Attach any modifier and return the tool Fusion created for it, so a
30692
30750
  TEXT modifier (Follower on a TextPlus StyledText) can then be driven
@@ -30966,6 +31024,10 @@ def fusion_comp(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[st
30966
31024
  except Exception:
30967
31025
  _already_animated = False
30968
31026
  if not _already_animated:
31027
+ _is_nest, _members = _fusion_nest_members(tool, p["input_name"])
31028
+ if _is_nest:
31029
+ return _fusion_nest_control_error(
31030
+ p["tool_name"], p["input_name"], _members, "keyframed")
30969
31031
  # AddModifier reports through the Lua bridge, which resolves an
30970
31032
  # unknown attribute to None rather than raising, so the return is
30971
31033
  # not reliable evidence on its own. The readback below is: if the
@@ -31012,6 +31074,10 @@ def fusion_comp(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[st
31012
31074
  if not inp:
31013
31075
  return _err(f"Input '{input_name}' not found on tool '{p['tool_name']}'")
31014
31076
  modifier_id = _fusion_modifier_id(requested)
31077
+ is_nest, members = _fusion_nest_members(tool, input_name)
31078
+ if is_nest:
31079
+ return _fusion_nest_control_error(p["tool_name"], input_name, members,
31080
+ "given a modifier")
31015
31081
  try:
31016
31082
  existing = inp.GetConnectedOutput()
31017
31083
  except Exception:
@@ -3440,6 +3440,34 @@ API_TRUTH: List[Dict[str, Any]] = [
3440
3440
  "only StyledTextFollower attached (new tool Follower1).",
3441
3441
  "mitigation": ["fusion_comp.add_modifier", "fusion_comp.add_keyframe"],
3442
3442
  },
3443
+ {
3444
+ "symbol": "Tool.AddModifier (NestControl inputs)",
3445
+ "object": "Fusion Tool",
3446
+ "signature": "(inputName, modifierRegID) -> bool",
3447
+ "reality": "Some inputs GetInputList() returns are not values at all: those whose "
3448
+ "INPID_InputControl attribute is 'NestControl' (INPB_Passive true) are "
3449
+ "the fold-down group headers the Fusion UI draws. AddModifier returns "
3450
+ "False for them on every modifier type, and assigning at a time sets "
3451
+ "nothing. Measured on TextPlus Softness1 and on the text Follower's "
3452
+ "TransformSize, Softness1 and Size1. The controls a header folds are "
3453
+ "the next INPI_LabelControl_NumInputs entries in GetInputList() order "
3454
+ "(Softness1 -> SoftnessX1, SoftnessY1, SoftnessOnFillColorToo1, "
3455
+ "SoftnessGlow1, SoftnessBlend1; TransformSize -> Line/Word/Character "
3456
+ "Size X and Y), and those take a BezierSpline normally.",
3457
+ "recommended": "Keyframe the folded controls, never the header. fusion_comp "
3458
+ "add_keyframe and add_modifier refuse a nest control with "
3459
+ "FUSION_INPUT_IS_NEST_CONTROL and list its members.",
3460
+ "tags": ["fusion", "silent-failure", "naming"],
3461
+ "verified_on": "DaVinci Resolve Studio 19.1.3.7",
3462
+ "measured": "2026-09-19 on a disposable timeline: Follower via add_modifier, then "
3463
+ "add_keyframe on TransformSize / Softness1 (FUSION_ADD_MODIFIER_FAILED, "
3464
+ "raw AddModifier False for BezierSpline, Path, TextScramble) versus "
3465
+ "Size / Opacity1 / Delay / SoftnessX1 / SoftnessY1 / SizeX1 / "
3466
+ "CharacterSizeX (BezierSpline attached); TextPlus Softness1 refused too. "
3467
+ "GetAttrs diff: INPID_InputControl NestControl vs SliderControl, "
3468
+ "INPB_Passive true, INPI_LabelControl_NumInputs 6 / 5 / 2.",
3469
+ "mitigation": ["fusion_comp.add_keyframe", "fusion_comp.add_modifier"],
3470
+ },
3443
3471
 
3444
3472
  ]
3445
3473
 
@@ -3500,7 +3528,8 @@ ACTION_SYMBOLS: Dict[Tuple[str, str], List[str]] = {
3500
3528
  ("timeline_item_color", "safe_export_lut"): ["TimelineItem.ExportLUT"],
3501
3529
  ("timeline", "duplicate"): ["Timeline.DuplicateTimeline"],
3502
3530
  ("project_manager", "archive"): ["ProjectManager.ArchiveProject"],
3503
- ("fusion_comp", "add_modifier"): ["Tool.AddModifier"],
3531
+ ("fusion_comp", "add_modifier"): ["Tool.AddModifier", "Tool.AddModifier (NestControl inputs)"],
3532
+ ("fusion_comp", "add_keyframe"): ["Tool.AddModifier (NestControl inputs)"],
3504
3533
  ("project_manager", "safe_project_archive"): ["ProjectManager.ArchiveProject"],
3505
3534
  }
3506
3535