davinci-resolve-mcp 4.6.2 → 4.6.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 +38 -0
- package/README.md +1 -1
- package/README.zh-CN.md +2 -2
- package/docs/process/release-process.md +10 -1
- package/install.py +1 -1
- package/package.json +1 -1
- package/src/granular/common.py +1 -1
- package/src/server.py +6 -6
- package/src/utils/destructive_hook.py +1 -1
- package/src/utils/execution_lifecycle.py +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,44 @@
|
|
|
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.6.4 — `ripple="false"` no longer ripples
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **`timeline` `delete_clips`, `lift_range`, and the delete step of `move_clips` read
|
|
10
|
+
`ripple` with a bare `bool()`, so a client sending `ripple="false"` got a ripple
|
|
11
|
+
delete.** ([#236](https://github.com/samuelgursky/davinci-resolve-mcp/pull/236), @Dev-next-gen)
|
|
12
|
+
The gap closed and everything downstream shifted, which is exactly what the caller
|
|
13
|
+
declined. The guards around it read the flag the same way — the pending-confirm
|
|
14
|
+
check, `destructive_hook.is_strict_required`, and the blast-radius branch of the
|
|
15
|
+
risk classifier — so with confirm tokens on the user was asked to confirm a ripple
|
|
16
|
+
delete they never requested, and once confirmed it ran as one. All six readings now
|
|
17
|
+
go through `coerce_bool` (the helper #218 added for `dry_run`). Real booleans and
|
|
18
|
+
`"true"`/`"false"`/`"1"`/`"0"` behave exactly as before; a string `coerce_bool`
|
|
19
|
+
does not recognise now falls to non-ripple instead of ripple, the safe direction
|
|
20
|
+
for a destructive flag. Guard test: `tests/test_delete_clips_ripple_string.py`,
|
|
21
|
+
which fails three of four on the previous code.
|
|
22
|
+
|
|
23
|
+
## What's New in v4.6.3 — the publish workflow keeps npm `latest` on the highest version
|
|
24
|
+
|
|
25
|
+
Release-process hardening only. No tool, action, or Resolve behaviour changed.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- **Three release tags pushed in one command left npm `latest` on the oldest of
|
|
30
|
+
them.** The `Publish npm package` runs for v4.6.0, v4.6.1 and v4.6.2 executed in
|
|
31
|
+
parallel and finished in the order 4.6.1, 4.6.2, 4.6.0; npm points `latest` at
|
|
32
|
+
whichever publish lands last, so `npm install davinci-resolve-mcp` resolved to
|
|
33
|
+
4.6.0 while v4.6.2 was the GitHub Release marked latest. The workflow now ends with
|
|
34
|
+
a step that compares `dist-tags.latest` against the highest published version
|
|
35
|
+
(always counting the version the run itself carries, because `npm view` can serve
|
|
36
|
+
a document minutes stale right after a publish) and re-points `latest` when it
|
|
37
|
+
lags. It never fails the job; if the trusted-publishing token cannot edit
|
|
38
|
+
dist-tags it logs the `npm dist-tag add` command for a maintainer. This release's
|
|
39
|
+
own publish is what puts `latest` back on the newest version.
|
|
40
|
+
- `docs/process/release-process.md`: push one release tag at a time and wait for its
|
|
41
|
+
publish run before pushing the next; the workflow step is a backstop, not the plan.
|
|
42
|
+
|
|
5
43
|
## What's New in v4.6.2 — the granular server's macOS temp-path redirect matches the compound server's
|
|
6
44
|
|
|
7
45
|
### Fixed
|
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
|
-
> 本翻译对应 v4.6.
|
|
15
|
+
> 本翻译对应 v4.6.4 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
|
|
16
16
|
|
|
17
17
|
一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
|
|
18
18
|
|
|
@@ -204,7 +204,16 @@ should say that no behavior changed.
|
|
|
204
204
|
```
|
|
205
205
|
|
|
206
206
|
6. Push `main`.
|
|
207
|
-
7. Create and push the annotated tag
|
|
207
|
+
7. Create and push the annotated tag. **Push one release tag at a time and wait
|
|
208
|
+
for its `Publish npm package` run to finish before pushing the next.** Runs for
|
|
209
|
+
tags pushed together execute in parallel, and npm points `latest` at whichever
|
|
210
|
+
publish finishes *last*: on 2026-09-15 v4.6.0–v4.6.2 pushed in one command
|
|
211
|
+
finished in the order 4.6.1, 4.6.2, 4.6.0, and `latest` landed on 4.6.0. The
|
|
212
|
+
workflow's final step now re-points `latest` at the highest published version
|
|
213
|
+
when it lags, but it is a backstop, not the plan — the trusted-publishing token
|
|
214
|
+
may not be allowed to edit dist-tags, in which case the fix is
|
|
215
|
+
`npm dist-tag add davinci-resolve-mcp@X.Y.Z latest` by a logged-in maintainer,
|
|
216
|
+
or simply the next release's publish, which sets `latest` itself.
|
|
208
217
|
|
|
209
218
|
```bash
|
|
210
219
|
git tag -a v2.4.1 -m "v2.4.1"
|
package/install.py
CHANGED
|
@@ -37,7 +37,7 @@ from src.utils.update_check import (
|
|
|
37
37
|
|
|
38
38
|
# ─── Version ──────────────────────────────────────────────────────────────────
|
|
39
39
|
|
|
40
|
-
VERSION = "4.6.
|
|
40
|
+
VERSION = "4.6.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
package/src/granular/common.py
CHANGED
|
@@ -93,7 +93,7 @@ if not logging.getLogger().handlers:
|
|
|
93
93
|
handlers=[logging.StreamHandler()],
|
|
94
94
|
)
|
|
95
95
|
|
|
96
|
-
VERSION = "4.6.
|
|
96
|
+
VERSION = "4.6.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.6.
|
|
14
|
+
VERSION = "4.6.4"
|
|
15
15
|
|
|
16
16
|
import base64
|
|
17
17
|
import os
|
|
@@ -73,7 +73,7 @@ from src.utils.proc import safe_run
|
|
|
73
73
|
from src.utils.readback import verify_by_readback, verification_stats as _verification_stats
|
|
74
74
|
from src.utils import operation_result as _operation_result
|
|
75
75
|
from src.utils import operation_log as _operation_log
|
|
76
|
-
from src.utils.bool_params import explicit_bool_param as _explicit_bool_param
|
|
76
|
+
from src.utils.bool_params import coerce_bool as _coerce_bool, explicit_bool_param as _explicit_bool_param
|
|
77
77
|
from src.utils.confirm_tokens import (
|
|
78
78
|
ConfirmTokenStore as _ConfirmTokenStore,
|
|
79
79
|
gate_required_from as _gate_required_from,
|
|
@@ -1365,7 +1365,7 @@ def _action_will_gate_pending_confirm(
|
|
|
1365
1365
|
tool_name == "timeline"
|
|
1366
1366
|
and action == "delete_clips"
|
|
1367
1367
|
and isinstance(params, dict)
|
|
1368
|
-
and
|
|
1368
|
+
and _coerce_bool(params.get("ripple"))
|
|
1369
1369
|
):
|
|
1370
1370
|
return True
|
|
1371
1371
|
return False
|
|
@@ -4982,7 +4982,7 @@ def _timeline_duplicate_clips_impl(proj, tl, p: Dict[str, Any], *, delete_source
|
|
|
4982
4982
|
seen_delete_ids.add(item_id)
|
|
4983
4983
|
if delete_items:
|
|
4984
4984
|
try:
|
|
4985
|
-
out["deleted_sources"] = _timeline_delete_clips_verified(tl, delete_items,
|
|
4985
|
+
out["deleted_sources"] = _timeline_delete_clips_verified(tl, delete_items, _coerce_bool(p.get("ripple")), resolve=resolve)
|
|
4986
4986
|
out["deleted_source_ids"] = _timeline_item_ids(delete_items)
|
|
4987
4987
|
except Exception as exc:
|
|
4988
4988
|
out["deleted_sources"] = False
|
|
@@ -5614,7 +5614,7 @@ def _timeline_lift_range_impl(tl, p: Dict[str, Any], *, resolve=None):
|
|
|
5614
5614
|
return {"success": True, "deleted": 0, "range": {"start": start, "end": end}}
|
|
5615
5615
|
deleted_ids = _timeline_item_ids(delete_items)
|
|
5616
5616
|
return {
|
|
5617
|
-
"success": _timeline_delete_clips_verified(tl, delete_items,
|
|
5617
|
+
"success": _timeline_delete_clips_verified(tl, delete_items, _coerce_bool(p.get("ripple")), resolve=resolve),
|
|
5618
5618
|
"deleted": len(delete_items),
|
|
5619
5619
|
"deleted_ids": deleted_ids,
|
|
5620
5620
|
"range": {"start": start, "end": end},
|
|
@@ -25619,7 +25619,7 @@ def timeline(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str,
|
|
|
25619
25619
|
if it.GetUniqueId() in ids_set:
|
|
25620
25620
|
found.append(it)
|
|
25621
25621
|
# B2 — ripple=True is catastrophic; require confirmation.
|
|
25622
|
-
ripple =
|
|
25622
|
+
ripple = _coerce_bool(p.get("ripple"))
|
|
25623
25623
|
if ripple:
|
|
25624
25624
|
if "confirm_token" not in p and "confirmToken" not in p and _confirm_token_required():
|
|
25625
25625
|
return _issue_confirm_token(
|
|
@@ -493,7 +493,7 @@ def is_strict_required(tool_name: str, action: str, params: Optional[Dict[str, A
|
|
|
493
493
|
tool_name == "timeline"
|
|
494
494
|
and action == "delete_clips"
|
|
495
495
|
and isinstance(params, dict)
|
|
496
|
-
and
|
|
496
|
+
and coerce_bool(params.get("ripple"))
|
|
497
497
|
):
|
|
498
498
|
return True
|
|
499
499
|
return False
|
|
@@ -25,6 +25,7 @@ from dataclasses import dataclass, field
|
|
|
25
25
|
from typing import Any, Callable, Dict, List, Optional, Set, Tuple
|
|
26
26
|
|
|
27
27
|
from src.utils import operation_log
|
|
28
|
+
from src.utils.bool_params import coerce_bool
|
|
28
29
|
|
|
29
30
|
logger = logging.getLogger("resolve-mcp.execution-lifecycle")
|
|
30
31
|
|
|
@@ -407,7 +408,7 @@ class RiskClassificationHook(LifecycleHook):
|
|
|
407
408
|
):
|
|
408
409
|
level = RiskLevel.HIGH
|
|
409
410
|
destructive = True
|
|
410
|
-
if params.get("ripple"
|
|
411
|
+
if coerce_bool(params.get("ripple")):
|
|
411
412
|
radius = BlastRadius.TIMELINE
|
|
412
413
|
reasons.append("Ripple mode alters downstream timeline synchronization")
|
|
413
414
|
elif tool_name == "graph":
|