davinci-resolve-mcp 4.6.3 → 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,24 @@
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
+
5
23
  ## What's New in v4.6.3 — the publish workflow keeps npm `latest` on the highest version
6
24
 
7
25
  Release-process hardening only. No tool, action, or Resolve behaviour changed.
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.3-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.3-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.3 版 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
 
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.3"
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.3",
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.3"
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.3"
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":