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 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
- [![Version](https://img.shields.io/badge/version-4.6.2-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-4.6.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(387%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.6.2-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-4.6.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(387%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.6.2 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
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.2"
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "davinci-resolve-mcp",
3
- "version": "4.6.2",
3
+ "version": "4.6.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.6.2"
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.2"
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 bool(params.get("ripple"))
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, bool(p.get("ripple", False)), resolve=resolve)
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, bool(p.get("ripple", False)), resolve=resolve),
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 = bool(p.get("ripple", False))
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 bool(params.get("ripple"))
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", False):
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":