davinci-resolve-mcp 2.99.0 → 2.99.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 +40 -0
- package/README.md +1 -1
- package/README.zh-CN.md +2 -2
- package/docs/SKILL.md +5 -1
- package/install.py +1 -1
- package/package.json +1 -1
- package/src/granular/common.py +1 -1
- package/src/server.py +47 -16
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,46 @@
|
|
|
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.99.1
|
|
6
|
+
|
|
7
|
+
**`bulk_set_item_properties` could not set a clip colour on its own.** Reported
|
|
8
|
+
and fixed in [#157](https://github.com/samuelgursky/davinci-resolve-mcp/pull/157)
|
|
9
|
+
by @matoberuc-afk.
|
|
10
|
+
|
|
11
|
+
The action documents `clip_color` and `enabled` as per-op keys and has code to
|
|
12
|
+
apply both — but that code was unreachable for the op shape that needs it most.
|
|
13
|
+
The payload is built by `_merge_property_groups`, which merges only
|
|
14
|
+
`properties`/`transform`/`crop`/`composite`/`audio` and the duplicate-keyframe
|
|
15
|
+
keys. `clip_color` and `enabled` are not `SetProperty` keys, so they never landed
|
|
16
|
+
in that dict, and the `if not properties: continue` guard above returned early —
|
|
17
|
+
leaving the `clip_color` branch twenty-five lines below dead on exactly the ops
|
|
18
|
+
that carry no transform. Colour triage in one round trip is the main reason to
|
|
19
|
+
call a *bulk* setter, and it was the one shape that could not work.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **A colour-only or enabled-only op is now accepted** and applied.
|
|
24
|
+
- **A colour-only op could not fail.** Per-op success was
|
|
25
|
+
`all(row.get("success") for row in ...properties.values())`, and `all([])` is
|
|
26
|
+
`True` — with no property rows the op passed regardless of what `SetClipColor`
|
|
27
|
+
returned. Every branch that runs now votes.
|
|
28
|
+
- **The bulk path trusted the bare bool.** It called `item.SetClipColor` directly,
|
|
29
|
+
bypassing `_set_clip_color_checked` — the helper added for
|
|
30
|
+
[#124](https://github.com/samuelgursky/davinci-resolve-mcp/issues/124) that the
|
|
31
|
+
single-item path already used, because that bool lies twice: a name outside the
|
|
32
|
+
16-name Edit-page palette is refused with a bare `False`, and a generator or
|
|
33
|
+
title takes the call, returns `True`, and drops the colour. A failure now
|
|
34
|
+
carries `clip_color_detail`.
|
|
35
|
+
- `dry_run` reports `would_set_clip_color` / `would_set_enabled`, and the
|
|
36
|
+
`action_help` example shows the triage shape instead of a `properties`
|
|
37
|
+
dict with a `ClipColor` key that was never a valid `SetProperty` target.
|
|
38
|
+
|
|
39
|
+
### Live validation
|
|
40
|
+
|
|
41
|
+
Studio 19.1.3.7: three colour-only ops in one call, live readback
|
|
42
|
+
`['Apricot', 'Chocolate', 'Purple']` on the timeline items; a refused colour
|
|
43
|
+
returns `success: false` instead of passing on the empty-list vote.
|
|
44
|
+
|
|
5
45
|
## What's New in v2.99.0
|
|
6
46
|
|
|
7
47
|
**`timeline.ripple_insert`, and a verified-delete gate on `move_clips`.**
|
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
|
-
> 本翻译对应 v2.99.
|
|
15
|
+
> 本翻译对应 v2.99.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
|
@@ -1410,7 +1410,11 @@ Key actions:
|
|
|
1410
1410
|
`track_index?` (default 1), so multicam angles can be rebuilt onto V2/V3
|
|
1411
1411
|
rather than collapsing onto V1; missing tracks are added
|
|
1412
1412
|
- `bulk_set_item_properties(ops, dry_run?, readback?)` — apply transforms,
|
|
1413
|
-
crop/composite/audio/property groups to many timeline items in one call
|
|
1413
|
+
crop/composite/audio/property groups to many timeline items in one call. An op
|
|
1414
|
+
may carry `clip_color` and/or `enabled` with nothing else, which is the triage
|
|
1415
|
+
shape: paint a whole selection in one round trip. A colour is verified by
|
|
1416
|
+
readback, so a name outside the Edit-page palette and the generator/title case
|
|
1417
|
+
that returns True and drops the colour both fail the op instead of passing
|
|
1414
1418
|
- `apply_look_to_items(target_ids, cdl?|copy_from_item_id?, dry_run?)` — apply a
|
|
1415
1419
|
normalized CDL and/or copy a source grade to multiple video items
|
|
1416
1420
|
- `thumbnail_contact_sheet` / `marker_thumbnail_review` — sample Resolve
|
package/install.py
CHANGED
|
@@ -37,7 +37,7 @@ from src.utils.update_check import (
|
|
|
37
37
|
|
|
38
38
|
# ─── Version ──────────────────────────────────────────────────────────────────
|
|
39
39
|
|
|
40
|
-
VERSION = "2.99.
|
|
40
|
+
VERSION = "2.99.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
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 = "2.99.
|
|
90
|
+
VERSION = "2.99.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()}")
|
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.99.
|
|
14
|
+
VERSION = "2.99.1"
|
|
15
15
|
|
|
16
16
|
import base64
|
|
17
17
|
import os
|
|
@@ -6039,8 +6039,15 @@ def _timeline_bulk_set_item_properties(tl, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
6039
6039
|
results.append({"index": index, "success": False, "error": f"timeline item not found: {item_id}"})
|
|
6040
6040
|
continue
|
|
6041
6041
|
properties = _merge_property_groups(op)
|
|
6042
|
-
|
|
6043
|
-
|
|
6042
|
+
# clip_color and enabled are not SetProperty keys, so they never reach
|
|
6043
|
+
# `properties`. Requiring a non-empty `properties` made a colour-only op
|
|
6044
|
+
# — the shape triage actually sends, `{"timeline_item_id": id,
|
|
6045
|
+
# "clip_color": "Apricot"}` — bail here, which left the clip_color and
|
|
6046
|
+
# enabled branches below unreachable on exactly the ops that need them.
|
|
6047
|
+
wants_color = "clip_color" in op
|
|
6048
|
+
wants_enabled = "enabled" in op
|
|
6049
|
+
if not properties and not wants_color and not wants_enabled:
|
|
6050
|
+
results.append({"index": index, "success": False, "error": "op requires properties, transform, crop, composite, audio, clip_color, enabled, or direct property keys"})
|
|
6044
6051
|
continue
|
|
6045
6052
|
item_result = {
|
|
6046
6053
|
"index": index,
|
|
@@ -6050,6 +6057,10 @@ def _timeline_bulk_set_item_properties(tl, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
6050
6057
|
}
|
|
6051
6058
|
if dry_run:
|
|
6052
6059
|
item_result.update({"success": True, "would_set": properties})
|
|
6060
|
+
if wants_color:
|
|
6061
|
+
item_result["would_set_clip_color"] = op["clip_color"]
|
|
6062
|
+
if wants_enabled:
|
|
6063
|
+
item_result["would_set_enabled"] = bool(op["enabled"])
|
|
6053
6064
|
results.append(item_result)
|
|
6054
6065
|
continue
|
|
6055
6066
|
for key, value in properties.items():
|
|
@@ -6065,17 +6076,33 @@ def _timeline_bulk_set_item_properties(tl, p: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
6065
6076
|
except Exception as exc:
|
|
6066
6077
|
row["readback_error"] = str(exc)
|
|
6067
6078
|
item_result["properties"][key] = row
|
|
6068
|
-
|
|
6079
|
+
outcomes = [bool(row.get("success")) for row in item_result["properties"].values()]
|
|
6080
|
+
if wants_color:
|
|
6081
|
+
# Routed through the checked helper rather than the bare bool: a name
|
|
6082
|
+
# outside the 16-name Edit-page palette is refused with False and no
|
|
6083
|
+
# reason, and a generator or title takes the call, returns True and
|
|
6084
|
+
# drops the colour (issue #124). The detail only appears on failure,
|
|
6085
|
+
# so the success shape stays the plain bool callers already read.
|
|
6069
6086
|
try:
|
|
6070
|
-
|
|
6087
|
+
outcome = _set_clip_color_checked(item, op["clip_color"], kind="timeline item")
|
|
6071
6088
|
except Exception as exc:
|
|
6072
|
-
|
|
6073
|
-
|
|
6089
|
+
outcome = {"success": False, "error": str(exc)}
|
|
6090
|
+
applied = bool(outcome.get("success"))
|
|
6091
|
+
item_result["clip_color"] = applied
|
|
6092
|
+
if not applied:
|
|
6093
|
+
item_result["clip_color_detail"] = outcome
|
|
6094
|
+
outcomes.append(applied)
|
|
6095
|
+
if wants_enabled:
|
|
6074
6096
|
try:
|
|
6075
|
-
|
|
6097
|
+
enabled_ok = bool(item.SetClipEnabled(bool(op["enabled"])))
|
|
6098
|
+
item_result["enabled"] = enabled_ok
|
|
6076
6099
|
except Exception as exc:
|
|
6100
|
+
enabled_ok = False
|
|
6077
6101
|
item_result["enabled"] = {"success": False, "error": str(exc)}
|
|
6078
|
-
|
|
6102
|
+
outcomes.append(enabled_ok)
|
|
6103
|
+
# all([]) is True, so a colour-only op used to be able to report success
|
|
6104
|
+
# no matter what SetClipColor did. Every branch that ran now votes.
|
|
6105
|
+
item_result["success"] = all(outcomes)
|
|
6079
6106
|
results.append(item_result)
|
|
6080
6107
|
return {"success": all(row.get("success") for row in results), "results": results, "op_count": len(ops)}
|
|
6081
6108
|
|
|
@@ -22853,6 +22880,8 @@ def timeline(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str,
|
|
|
22853
22880
|
track_type, default 1); missing tracks are added, so V2/V3 multicam angles survive.
|
|
22854
22881
|
# example: action_help(name='<action_name>')
|
|
22855
22882
|
bulk_set_item_properties(ops, dry_run?, readback?) -> {results, op_count}
|
|
22883
|
+
an op may carry clip_color and/or enabled with no other payload -- that is
|
|
22884
|
+
the triage shape (paint N clips in one call).
|
|
22856
22885
|
# example: action_help(name='<action_name>')
|
|
22857
22886
|
apply_look_to_items(target_ids, cdl?|copy_from_item_id?, dry_run?) -> {success}
|
|
22858
22887
|
# example: action_help(name='<action_name>')
|
|
@@ -24966,16 +24995,18 @@ _ACTION_HELP: Dict[str, Dict[str, Dict[str, Any]]] = {
|
|
|
24966
24995
|
),
|
|
24967
24996
|
},
|
|
24968
24997
|
"bulk_set_item_properties": {
|
|
24969
|
-
"summary": "Batch SetProperty/clip_color/enabled across many items.",
|
|
24970
|
-
"params": "ops: [{timeline_item_id|clip_id, properties
|
|
24971
|
-
"returns": "{success, results, op_count}",
|
|
24998
|
+
"summary": "Batch SetProperty/clip_color/enabled across many items. clip_color or enabled may be the only key in an op.",
|
|
24999
|
+
"params": "ops: [{timeline_item_id|clip_id, properties?|transform?|crop?|composite?|audio?, clip_color?, enabled?}], dry_run?, readback?",
|
|
25000
|
+
"returns": "{success, results, op_count}; a failed clip_color adds results[].clip_color_detail",
|
|
24972
25001
|
"example": (
|
|
24973
25002
|
'timeline(action="bulk_set_item_properties", params={\n'
|
|
24974
25003
|
' "ops": [\n'
|
|
24975
|
-
'
|
|
24976
|
-
'
|
|
24977
|
-
' {"timeline_item_id": "TimelineItem-def",\n'
|
|
24978
|
-
'
|
|
25004
|
+
' # triage: colour only, no other payload needed\n'
|
|
25005
|
+
' {"timeline_item_id": "TimelineItem-abc", "clip_color": "Apricot"},\n'
|
|
25006
|
+
' {"timeline_item_id": "TimelineItem-def", "clip_color": "Chocolate"},\n'
|
|
25007
|
+
' # or colour plus a transform in the same op\n'
|
|
25008
|
+
' {"timeline_item_id": "TimelineItem-ghi",\n'
|
|
25009
|
+
' "clip_color": "Purple", "transform": {"ZoomX": 1.05}}\n'
|
|
24979
25010
|
' ],\n'
|
|
24980
25011
|
' "dry_run": True, "readback": True\n'
|
|
24981
25012
|
'})'
|