davinci-resolve-mcp 2.87.1 → 2.87.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,40 @@
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.87.2
6
+
7
+ A refused `SetSetting` now says why, when the ledger already knows. Issue #141,
8
+ reported by @jus-kim.
9
+
10
+ ### Fixed
11
+
12
+ - **`project_settings set_setting` returned a bare `{"success": false}` for a
13
+ key that can never be written.** `Project.SetSetting('timelinePlaybackFrameRate')`
14
+ refuses every value form, before and after a timeline exists — measured in
15
+ PR #99, written into `api_truth`, published in `api-limitations.md`, and
16
+ invisible at the one moment it mattered. A bare `false` reads as *your value
17
+ was wrong*, which sends a caller into retrying string, int, and float for a
18
+ key with no writable path at all. A refusal now carries the ledger entry for
19
+ that key: what is really happening, and the UI step that is the way through.
20
+ `timeline set_setting` gets the same treatment.
21
+ - The match is deliberately narrow. It requires the exact quoted key on the
22
+ right object — `Project.SetSetting('x')` will not be handed to a `Timeline`
23
+ refusal, and a substring like `timeline` will not collect the
24
+ `timelinePlaybackFrameRate` entry. An unmeasured refusal stays bare, because
25
+ inventing an explanation for a failure nobody measured is the thing this
26
+ ledger exists to prevent. The write is always attempted first, so a key that
27
+ starts working in a later build reports plain success.
28
+
29
+ ### Documentation
30
+
31
+ - **The `timelinePlaybackFrameRate` ledger entry carries the second report.**
32
+ Issue #141 confirms it independently on **Resolve 20.2**, against a freshly
33
+ created project whose timeline rate already read 60 — so a matching
34
+ `timelineFrameRate` does not unlock the write, which the PR #99 measurement
35
+ alone left open. The reporter's workaround is now recorded too: for repeat
36
+ setups, duplicate a project that already carries the wanted playback rate
37
+ rather than creating one and trying to write it.
38
+
5
39
  ## What's New in v2.87.1
6
40
 
7
41
  Follow-up evidence from @legionsound on PR #139, plus the process fix for the
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-2.87.1-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-2.87.2-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-34%20(341%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-2.87.1-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-2.87.2-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-34%20(341%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
- > 本翻译对应 v2.87.1 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
15
+ > 本翻译对应 v2.87.2 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
16
16
 
17
17
  一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
18
18
 
@@ -44,8 +44,8 @@ equivalent, blocking full automation.
44
44
  ### Project.SetSetting('timelinePlaybackFrameRate')
45
45
 
46
46
  - **Object:** `Project`
47
- - **Behavior:** Returns False for every value form tried (string, int, float), both before and after a timeline exists, so the playback frame rate cannot be set from the API at all. Reported by a community contributor against Resolve Studio while assembling a vertical timeline (PR #99).
48
- - **Workaround / current handling:** Ask the user to set it in Project Settings > Master Settings > Playback frame rate as a SETUP step, before any timeline exists. Read it back to confirm; do not report it as set on the strength of the call alone.
47
+ - **Behavior:** Returns False for every value form tried (string, int, float), both before and after a timeline exists, so the playback frame rate cannot be set from the API at all. Reported by a community contributor against Resolve Studio while assembling a vertical timeline (PR #99), and independently on Resolve 20.2 against a freshly created project whose timeline rate already read 60 (issue #141) — so a matching timelineFrameRate does not unlock the write.
48
+ - **Workaround / current handling:** Ask the user to set it in Project Settings > Master Settings > Playback frame rate as a SETUP step, before any timeline exists. Read it back to confirm; do not report it as set on the strength of the call alone. The issue #141 reporter's workaround is worth passing on for repeat setups: duplicate a project that already carries the wanted playback rate rather than creating one and trying to write it.
49
49
  - **Tags:** project-settings, silent-failure, timeline
50
50
 
51
51
  ### Timeline.GetCurrentClipThumbnailImage (Color page only)
package/install.py CHANGED
@@ -36,7 +36,7 @@ from src.utils.update_check import (
36
36
 
37
37
  # ─── Version ──────────────────────────────────────────────────────────────────
38
38
 
39
- VERSION = "2.87.1"
39
+ VERSION = "2.87.2"
40
40
  # Only hard floor: mcp[cli] requires Python 3.10+. There is no upper bound —
41
41
  # Resolve's scripting bridge loads into newer interpreters on recent builds
42
42
  # (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.87.1",
3
+ "version": "2.87.2",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -85,7 +85,7 @@ if not logging.getLogger().handlers:
85
85
  handlers=[logging.StreamHandler()],
86
86
  )
87
87
 
88
- VERSION = "2.87.1"
88
+ VERSION = "2.87.2"
89
89
  logger = logging.getLogger("davinci-resolve-mcp")
90
90
  logger.info(f"Starting DaVinci Resolve MCP Server v{VERSION}")
91
91
  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.87.1"
14
+ VERSION = "2.87.2"
15
15
 
16
16
  import base64
17
17
  import os
@@ -16017,6 +16017,37 @@ def project_manager_database(action: str, params: Optional[Dict[str, Any]] = Non
16017
16017
  # TOOL 8: project_settings
16018
16018
  # ═══════════════════════════════════════════════════════════════════════════════
16019
16019
 
16020
+ def _setting_limitation(name: Any, obj: str = "Project") -> Optional[Dict[str, Any]]:
16021
+ """The api_truth entry for a settings key on `obj`, when one exists.
16022
+
16023
+ `SetSetting` reports a refusal as a bare `False` with no reason, and for
16024
+ several keys this repo has already measured the reason and written it down —
16025
+ `timelinePlaybackFrameRate` returns False for every value form, before and
16026
+ after a timeline exists (issue #141, PR #99). A caller who gets
16027
+ `{"success": false}` has no way to tell "you passed a bad value" from "this
16028
+ key cannot be written from the API at all", and the second one is a
16029
+ different task: it has to go to the user as a UI step.
16030
+
16031
+ Matched narrowly on purpose. The entry must name this exact key *and* be
16032
+ `obj.SetSetting`, because attaching an unrelated explanation to a failure is
16033
+ worse than attaching none — it reads as a diagnosis. `Project` and
16034
+ `Timeline` both have a `SetSetting` and their keys overlap by name, so the
16035
+ object is part of the match rather than assumed.
16036
+ """
16037
+ if not isinstance(name, str) or not name:
16038
+ return None
16039
+ prefix = f"{obj}.SetSetting"
16040
+ quoted = f"'{name}'"
16041
+ for entry in lookup_api_truth(name):
16042
+ symbol = entry.get("symbol", "")
16043
+ # The quoted form is what makes this an exact key match: `name in
16044
+ # symbol` would hand the timelinePlaybackFrameRate entry to anything
16045
+ # that is a substring of it, "timeline" included.
16046
+ if symbol.startswith(prefix) and quoted in symbol:
16047
+ return entry
16048
+ return None
16049
+
16050
+
16020
16051
  @mcp.tool()
16021
16052
  @_guard_missing_params
16022
16053
  def project_settings(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
@@ -16026,7 +16057,9 @@ def project_settings(action: str, params: Optional[Dict[str, Any]] = None) -> Di
16026
16057
  get_name() -> {name}
16027
16058
  set_name(name) -> {success}
16028
16059
  get_setting(name?) -> {settings} — omit name for all settings
16029
- set_setting(name, value) -> {success}
16060
+ set_setting(name, value) -> {success, known_limitation?}
16061
+ A refusal carries the api_truth entry for that key when one exists —
16062
+ several settings cannot be written from the API at all.
16030
16063
  get_unique_id() -> {id}
16031
16064
  get_presets() -> {presets}
16032
16065
  set_preset(name) -> {success}
@@ -16062,7 +16095,20 @@ def project_settings(action: str, params: Optional[Dict[str, Any]] = None) -> Di
16062
16095
  return _err("set_setting requires name")
16063
16096
  if "value" not in p:
16064
16097
  return _err("set_setting requires value")
16065
- return {"success": bool(proj.SetSetting(p["name"], p["value"]))}
16098
+ if bool(proj.SetSetting(p["name"], p["value"])):
16099
+ return {"success": True}
16100
+ known = _setting_limitation(p["name"])
16101
+ if not known:
16102
+ return {"success": False}
16103
+ return {
16104
+ "success": False,
16105
+ "known_limitation": {
16106
+ "symbol": known.get("symbol"),
16107
+ "reality": known.get("reality"),
16108
+ "recommended": known.get("recommended"),
16109
+ "ledger_verified_on": _API_TRUTH_VERIFIED_ON,
16110
+ },
16111
+ }
16066
16112
  elif action == "get_unique_id":
16067
16113
  return {"id": proj.GetUniqueId()}
16068
16114
  elif action == "get_presets":
@@ -21402,7 +21448,8 @@ def timeline(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str,
21402
21448
  export(path, type, subtype?, background?) -> {success | job_id} — type: AAF, EDL, FCPXML, etc.
21403
21449
  UNSAFE. No path sandboxing. Prefer export_timeline_checked.
21404
21450
  get_setting(name?) -> {settings}
21405
- set_setting(name, value) -> {success}
21451
+ set_setting(name, value) -> {success, known_limitation?}
21452
+ A refusal carries the api_truth entry for that key when one exists.
21406
21453
  insert_generator(name) -> {success}
21407
21454
  insert_fusion_generator(name) -> {success}
21408
21455
  insert_fusion_composition() -> {success}
@@ -21762,7 +21809,20 @@ def timeline(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str,
21762
21809
  elif action == "get_setting":
21763
21810
  return {"settings": _ser(tl.GetSetting(p.get("name", "")))}
21764
21811
  elif action == "set_setting":
21765
- return {"success": bool(tl.SetSetting(p["name"], p["value"]))}
21812
+ if bool(tl.SetSetting(p["name"], p["value"])):
21813
+ return {"success": True}
21814
+ known = _setting_limitation(p["name"], obj="Timeline")
21815
+ if not known:
21816
+ return {"success": False}
21817
+ return {
21818
+ "success": False,
21819
+ "known_limitation": {
21820
+ "symbol": known.get("symbol"),
21821
+ "reality": known.get("reality"),
21822
+ "recommended": known.get("recommended"),
21823
+ "ledger_verified_on": _API_TRUTH_VERIFIED_ON,
21824
+ },
21825
+ }
21766
21826
  elif action == "insert_generator":
21767
21827
  r = tl.InsertGeneratorIntoTimeline(p["name"])
21768
21828
  return _ok() if r else _err("Failed to insert generator")
@@ -124,11 +124,18 @@ API_TRUTH: List[Dict[str, Any]] = [
124
124
  "float), both before and after a timeline exists, so the "
125
125
  "playback frame rate cannot be set from the API at all. "
126
126
  "Reported by a community contributor against Resolve Studio "
127
- "while assembling a vertical timeline (PR #99).",
127
+ "while assembling a vertical timeline (PR #99), and "
128
+ "independently on Resolve 20.2 against a freshly created "
129
+ "project whose timeline rate already read 60 (issue #141) — "
130
+ "so a matching timelineFrameRate does not unlock the write.",
128
131
  "recommended": "Ask the user to set it in Project Settings > Master "
129
132
  "Settings > Playback frame rate as a SETUP step, before "
130
133
  "any timeline exists. Read it back to confirm; do not "
131
- "report it as set on the strength of the call alone.",
134
+ "report it as set on the strength of the call alone. The "
135
+ "issue #141 reporter's workaround is worth passing on "
136
+ "for repeat setups: duplicate a project that already "
137
+ "carries the wanted playback rate rather than creating "
138
+ "one and trying to write it.",
132
139
  "tags": ["project-settings", "silent-failure", "timeline"],
133
140
  "submit": "missing",
134
141
  },