davinci-resolve-mcp 2.32.0 → 2.32.2

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,42 @@
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.32.2
6
+
7
+ Fixes `fusion_comp(action="get_keyframes")` serialization.
8
+
9
+ The handler iterated `Input.GetKeyFrames()` as if it returned `{time: value}`,
10
+ but Fusion returns `{1-based index: frame_position}`. The result put the
11
+ keyframe **index** in `time` and the **frame position** in `value` — the actual
12
+ keyframed values were never reported.
13
+
14
+ The handler now treats the dict values as frame positions and reads each
15
+ keyframed value back via `GetInput(input_name, frame)`.
16
+
17
+ - **Fixed** `get_keyframes` now returns `[{"time": <frame>, "value": <value>}, ...]`
18
+ in frame order (live-validated on DaVinci Resolve Studio 21.0.0:
19
+ `Size` keyed `1.0@f0` / `1.4@f75` → `[{0.0: 1.0}, {75.0: 1.4}]`).
20
+ - Follow-up to the `add_keyframe` fix in v2.32.1; flagged by @sandypoli-boop in #56.
21
+
22
+ ## What's New in v2.32.1
23
+
24
+ Fixes `fusion_comp(action="add_keyframe")` so it actually **animates** the input.
25
+
26
+ Previously the handler did `inp[time] = value` on the input directly. On an input
27
+ with no animation spline, that only assigns a **static** value (last write wins) —
28
+ no keyframe is created. Symptoms: `get_keyframes` returned `[]`, `get_input` at
29
+ different times returned the same value, and the clip never animated.
30
+
31
+ The handler now attaches a `BezierSpline` modifier the first time an input is
32
+ animated, then sets the keyframe. A new optional `modifier` param lets callers
33
+ pass e.g. `"Path"` for Point inputs such as `Center`. Behavior is unchanged for
34
+ inputs that are already animated or otherwise connected.
35
+
36
+ - **Fixed** `add_keyframe` now creates real, editable keyframes (live-validated on
37
+ DaVinci Resolve Studio 21.0.0).
38
+ - **Added** optional `modifier` param to `add_keyframe`.
39
+ - Thanks to @sandypoli-boop for the diagnosis and fix ([#56](https://github.com/samuelgursky/davinci-resolve-mcp/pull/56)).
40
+
5
41
  ## What's New in v2.32.0
6
42
 
7
43
  Adds **governance tiers** for the media-creating Resolve 21 AI ops (Phase 3, the
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # DaVinci Resolve MCP Server
2
2
 
3
- [![Version](https://img.shields.io/badge/version-2.32.0-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
3
+ [![Version](https://img.shields.io/badge/version-2.32.2-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
4
4
  [![npm](https://img.shields.io/npm/v/davinci-resolve-mcp.svg?label=npm&color=CB3837)](https://www.npmjs.com/package/davinci-resolve-mcp)
5
5
  [![API Coverage](https://img.shields.io/badge/API%20Coverage-100%25-brightgreen.svg)](docs/reference/api-coverage.md)
6
6
  [![Tools](https://img.shields.io/badge/MCP%20Tools-32%20(341%20full)-blue.svg)](#server-modes)
package/install.py CHANGED
@@ -35,7 +35,7 @@ from src.utils.update_check import (
35
35
 
36
36
  # ─── Version ──────────────────────────────────────────────────────────────────
37
37
 
38
- VERSION = "2.32.0"
38
+ VERSION = "2.32.2"
39
39
  # Only hard floor: mcp[cli] requires Python 3.10+. There is no upper bound —
40
40
  # Resolve's scripting bridge loads into newer interpreters on recent builds
41
41
  # (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.32.0",
3
+ "version": "2.32.2",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -80,7 +80,7 @@ if not logging.getLogger().handlers:
80
80
  handlers=[logging.StreamHandler()],
81
81
  )
82
82
 
83
- VERSION = "2.32.0"
83
+ VERSION = "2.32.2"
84
84
  logger = logging.getLogger("davinci-resolve-mcp")
85
85
  logger.info(f"Starting DaVinci Resolve MCP Server v{VERSION}")
86
86
  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 341-tool granular server instead
12
12
  """
13
13
 
14
- VERSION = "2.32.0"
14
+ VERSION = "2.32.2"
15
15
 
16
16
  import base64
17
17
  import os
@@ -18663,7 +18663,17 @@ def fusion_comp(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[st
18663
18663
  inp = tool[p["input_name"]]
18664
18664
  if not inp:
18665
18665
  return _err(f"Input '{p['input_name']}' not found on tool '{p['tool_name']}'")
18666
- inp[p["time"]] = p["value"]
18666
+ # Attach a BezierSpline modifier the first time this input is animated.
18667
+ # Without a spline, `inp[time] = value` only sets a STATIC value (the last
18668
+ # write wins) and never creates a keyframe. See the Fusion Scripting Guide.
18669
+ # Optional `modifier` lets callers pass e.g. "Path" for Point inputs.
18670
+ try:
18671
+ _already_animated = inp.GetConnectedOutput() is not None
18672
+ except Exception:
18673
+ _already_animated = False
18674
+ if not _already_animated:
18675
+ tool.AddModifier(p["input_name"], p.get("modifier", "BezierSpline"))
18676
+ tool[p["input_name"]][p["time"]] = p["value"]
18667
18677
  return _ok()
18668
18678
  finally:
18669
18679
  comp.Unlock()
@@ -18675,11 +18685,17 @@ def fusion_comp(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[st
18675
18685
  inp = tool[p["input_name"]]
18676
18686
  if not inp:
18677
18687
  return _err(f"Input '{p['input_name']}' not found on tool '{p['tool_name']}'")
18688
+ # GetKeyFrames() returns {1-based index: frame_position}, NOT
18689
+ # {time: value}. Iterating it directly puts the index in `time` and the
18690
+ # frame position in `value`. Read the actual keyframed value back from
18691
+ # the input at each frame position via GetInput.
18678
18692
  keyframes = []
18679
18693
  kfs = inp.GetKeyFrames()
18680
18694
  if kfs:
18681
- for t in kfs:
18682
- keyframes.append({"time": t, "value": _ser(kfs[t])})
18695
+ for idx in sorted(kfs):
18696
+ frame = kfs[idx]
18697
+ value = tool.GetInput(p["input_name"], frame)
18698
+ keyframes.append({"time": frame, "value": _ser(value)})
18683
18699
  return {"keyframes": keyframes}
18684
18700
 
18685
18701
  elif action == "delete_keyframe":