davinci-resolve-mcp 4.8.24 → 4.8.25

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,47 @@
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.8.25 — open_settings and open_app_preferences say what Resolve cannot do
6
+
7
+ ### Fixed
8
+
9
+ - **The granular `open_settings` and `open_app_preferences` tools could never
10
+ work, and reported that as an ordinary failure.** Both went through
11
+ `Resolve.GetUIManager()`, which does not exist. Measured on Studio 19.1.3.7:
12
+ `dir(resolve)` lists 23 methods and `GetUIManager` is not one of them;
13
+ `Fusion().UIManager` is real but has neither `OpenProjectSettings` nor
14
+ `OpenPreferences`; and none of the three names appears in the 21.1 typed API.
15
+ The call raised `'NoneType' object is not callable`, a broad `except`
16
+ swallowed it, an ERROR was logged, and the tool answered
17
+ `Failed to open Project Settings dialog` with no reason.
18
+ Both tools now answer `Not supported:` and name the call that is missing;
19
+ `open_settings` also names the tools that read and write project settings.
20
+ Nothing is logged as an error, because nothing went wrong.
21
+ - The route is now probed with `has_method` rather than `hasattr`, which is
22
+ true for every name on a Resolve object. If a future build does provide these
23
+ calls they are used, and their result is reported: the old code discarded the
24
+ return and answered success regardless, so a refusal would have read as a
25
+ dialog that opened.
26
+
27
+ ### Documentation
28
+
29
+ - `api_truth` records the absence, with what `Fusion().UIManager` does expose.
30
+ Whether `UIManager.DoAction` or `QueueAction` can open these dialogs was not
31
+ tried: both dialogs are modal, and a modal dialog blocks the scripting API
32
+ until a person closes it.
33
+ - `scripts/audit_api_parity.py` no longer describes `GetUIManager` as a
34
+ documented API.
35
+
36
+ ### Tests
37
+
38
+ - `tests/test_app_control_dialogs.py`: a fake that fabricates attributes the way
39
+ a Resolve object does (every `hasattr` true, a missing `getattr` is `None`).
40
+ Covers the measured build, a manager without the method, a build that has
41
+ the call, a refusal, an exception, and both granular tools. Eight of its
42
+ twelve tests fail against v4.8.24.
43
+ - `tests/test_discarded_resolve_returns.py` now covers every `Open*` call, not
44
+ only `OpenPage`.
45
+
5
46
  ## What's New in v4.8.24 — a frame capture leaves the render output folder and file name alone
6
47
 
7
48
  ### 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.8.24-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-4.8.25-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(389%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.8.24-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-4.8.25-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(389%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.8.24 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
15
+ > 本翻译对应 v4.8.25 版 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.8.24"
40
+ VERSION = "4.8.25"
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.8.24",
3
+ "version": "4.8.25",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -145,10 +145,13 @@ ALLOWLIST_UNDOCUMENTED: Set[str] = {
145
145
  "AddKeyframe", "DeleteKeyframe", "ModifyKeyframe", "RemoveKeyFrame",
146
146
  "GetKeyframeAtIndex", "GetKeyframeCount", "GetPropertyAtKeyframeIndex",
147
147
  "SetKeyframeInterpolation", "Render", "StartUndo",
148
- # UIManager / Resolve app-control API (documented under UIManager, not
149
- # the main Resolve scripting README)
150
- "GetUIManager", "OpenPreferences", "SetHighPriority",
151
- "OpenProjectSettings", "LoadUILayout", "SaveUILayout",
148
+ # Resolve app-control method, not in the scripting README this audit parses
149
+ "SetHighPriority",
150
+ # NOT a documented API: Resolve has no GetUIManager on any build measured
151
+ # (Studio 19.1.3.7; api_truth 'Resolve.GetUIManager ...'). It is called
152
+ # only behind has_method in src/utils/app_control.py, and from the unused
153
+ # helpers in src/utils/layout_presets.py along with the other two.
154
+ "GetUIManager", "LoadUILayout", "SaveUILayout",
152
155
  # Lua-table iteration helper used as a fallback in object_inspection.py
153
156
  "GetKeyList",
154
157
  # Project metadata accessor used defensively (hasattr-guarded)
@@ -93,7 +93,7 @@ if not logging.getLogger().handlers:
93
93
  handlers=[logging.StreamHandler()],
94
94
  )
95
95
 
96
- VERSION = "4.8.24"
96
+ VERSION = "4.8.25"
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()}")
@@ -371,32 +371,45 @@ def restart_app(wait_seconds: int = 5) -> str:
371
371
 
372
372
  @mcp.tool()
373
373
  def open_settings() -> str:
374
- """Open the Project Settings dialog in DaVinci Resolve."""
374
+ """Open the Project Settings dialog in DaVinci Resolve.
375
+
376
+ Not available on any Resolve build measured so far: the scripting API has
377
+ no call that opens this dialog (Studio 19.1.3.7 measured; absent from the
378
+ 21.1 typed API). The reply says so by name instead of a bare failure. To
379
+ read or change project settings use get_project_settings,
380
+ get_project_setting and set_project_setting.
381
+ """
375
382
  resolve = get_resolve()
376
383
  if resolve is None:
377
384
  return "Error: Not connected to DaVinci Resolve"
378
-
385
+
379
386
  result = open_project_settings(resolve)
380
-
381
- if result:
387
+ if result["success"]:
382
388
  return "Project Settings dialog opened successfully"
383
- else:
384
- return "Failed to open Project Settings dialog"
389
+ if not result["supported"]:
390
+ return (
391
+ result["message"]
392
+ + " Use get_project_settings, get_project_setting and set_project_setting instead."
393
+ )
394
+ return result["message"]
385
395
 
386
396
 
387
397
  @mcp.tool()
388
398
  def open_app_preferences() -> str:
389
- """Open the Preferences dialog in DaVinci Resolve."""
399
+ """Open the Preferences dialog in DaVinci Resolve.
400
+
401
+ Not available on any Resolve build measured so far: the scripting API has
402
+ no call that opens this dialog (Studio 19.1.3.7 measured; absent from the
403
+ 21.1 typed API). The reply says so by name instead of a bare failure.
404
+ """
390
405
  resolve = get_resolve()
391
406
  if resolve is None:
392
407
  return "Error: Not connected to DaVinci Resolve"
393
-
408
+
394
409
  result = open_preferences(resolve)
395
-
396
- if result:
410
+ if result["success"]:
397
411
  return "Preferences dialog opened successfully"
398
- else:
399
- return "Failed to open Preferences dialog"
412
+ return result["message"]
400
413
 
401
414
 
402
415
  @mcp.tool()
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.8.24"
14
+ VERSION = "4.8.25"
15
15
 
16
16
  import base64
17
17
  import os
@@ -2749,6 +2749,34 @@ API_TRUTH: List[Dict[str, Any]] = [
2749
2749
  "verified_on": "DaVinci Resolve Studio 19.1.3.7",
2750
2750
  "mitigation": ["_render_target_dir", "_playhead_frame_render"],
2751
2751
  },
2752
+ {
2753
+ "symbol": "Resolve.GetUIManager / UIManager.OpenProjectSettings / OpenPreferences (do not exist)",
2754
+ "object": "Resolve",
2755
+ "reality": "No scripting call opens the Project Settings or Preferences "
2756
+ "dialog. Measured 2026-09-30 on Studio 19.1.3.7, direct "
2757
+ "connection: dir(resolve) lists 23 methods and GetUIManager "
2758
+ "is not among them (getattr returns None; hasattr says True, "
2759
+ "as it does for every name). Fusion().UIManager is a real "
2760
+ "object with 15 names — AddNotify, Comp, Composition, "
2761
+ "DoAction, FindWindow, FindWindows, GetData, GetEvent, GetID, "
2762
+ "GetReg, QueueAction, QueueEvent, RemoveNotify, SetData, "
2763
+ "TriggerEvent — and none of OpenProjectSettings, "
2764
+ "OpenPreferences, SaveUILayout or LoadUILayout. None of those "
2765
+ "names, nor GetUIManager, appears in the 21.1 typed API "
2766
+ "either. Code written against them calls None and raises "
2767
+ "\"'NoneType' object is not callable\"; wrapped in a broad "
2768
+ "except, that reads as an ordinary failure. Whether "
2769
+ "UIManager.DoAction or QueueAction can open these dialogs was "
2770
+ "not tried: both dialogs are modal, and a modal dialog blocks "
2771
+ "the scripting API until a person closes it.",
2772
+ "recommended": "Do not offer to open these dialogs. Read and write "
2773
+ "project settings through Project.GetSetting/SetSetting. "
2774
+ "UI layouts go through Resolve.SaveLayoutPreset / "
2775
+ "LoadLayoutPreset, which do exist. Probe with "
2776
+ "resolve_probe.has_method, never hasattr.",
2777
+ "tags": ["ui", "unsupported", "dialog"],
2778
+ "verified_on": "DaVinci Resolve Studio 19.1.3.7",
2779
+ },
2752
2780
  {
2753
2781
  "symbol": "ProjectManager.SaveProject",
2754
2782
  "object": "ProjectManager",
@@ -16,6 +16,8 @@ import platform
16
16
  import subprocess
17
17
  from typing import Dict, Any, Optional, Union, List
18
18
 
19
+ from src.utils.resolve_probe import has_method
20
+
19
21
  # Configure logging
20
22
  logger = logging.getLogger("davinci-resolve-mcp.app_control")
21
23
  APP_CONTROL_TIMEOUT_SECONDS = 10
@@ -267,65 +269,68 @@ def restart_resolve_app(resolve_obj, wait_seconds: int = 5) -> bool:
267
269
  logger.error(f"Error restarting DaVinci Resolve: {str(e)}")
268
270
  return False
269
271
 
270
- def open_project_settings(resolve_obj) -> bool:
271
- """
272
- Open the Project Settings dialog in DaVinci Resolve.
273
-
274
- Args:
275
- resolve_obj: DaVinci Resolve API object
276
-
277
- Returns:
278
- True if successful, False otherwise
279
- """
280
- try:
281
- # Check if UI Manager is available
282
- ui_manager = resolve_obj.GetUIManager()
283
- if not ui_manager:
284
- logger.error("Failed to get UI Manager")
285
- return False
286
-
287
- # Open Project Settings dialog
288
- if hasattr(ui_manager, 'OpenProjectSettings') and callable(getattr(ui_manager, 'OpenProjectSettings')):
289
- ui_manager.OpenProjectSettings()
290
- return True
291
-
292
- # Alternative method - send keyboard shortcut based on platform
293
- current_page = resolve_obj.GetCurrentPage()
294
-
295
- # Ensure we're on a page that supports project settings
296
- if current_page not in ['media', 'cut', 'edit', 'fusion', 'color', 'fairlight', 'deliver']:
297
- logger.error(f"Can't open settings from page: {current_page}")
298
- return False
299
-
300
- return False # Keyboard shortcuts not implemented yet
301
- except Exception as e:
302
- logger.error(f"Error opening project settings: {str(e)}")
303
- return False
272
+ def _open_dialog(resolve_obj, method_name: str, label: str) -> Dict[str, Any]:
273
+ """Ask Resolve to open a dialog, and say what actually happened.
304
274
 
305
- def open_preferences(resolve_obj) -> bool:
306
- """
307
- Open the Preferences dialog in DaVinci Resolve.
308
-
309
- Args:
310
- resolve_obj: DaVinci Resolve API object
311
-
312
- Returns:
313
- True if successful, False otherwise
275
+ Returns {"success", "supported", "message"}.
276
+
277
+ The route this module has always used is Resolve.GetUIManager() and then a
278
+ method on the manager. Neither half exists on any build measured so far.
279
+ On Studio 19.1.3.7 (2026-09-30): dir(resolve) lists 23 methods and
280
+ GetUIManager is not one of them; Fusion().UIManager is real but offers
281
+ neither OpenProjectSettings nor OpenPreferences; and none of the three
282
+ names appears in the 21.1 typed API. So the old code raised "'NoneType'
283
+ object is not callable" on its first line, caught it, logged an error and
284
+ returned a bare False — a tool that could never work, reporting that as an
285
+ ordinary failure with no reason.
286
+
287
+ `hasattr` cannot make this distinction: it is True for every name on a
288
+ Resolve object (see src/utils/resolve_probe.py). `has_method` can, so the
289
+ route is probed with it, and a build that does grow these calls is used
290
+ and its answer reported instead of assumed.
314
291
  """
292
+ if not has_method(resolve_obj, "GetUIManager"):
293
+ return {
294
+ "success": False,
295
+ "supported": False,
296
+ "message": (
297
+ f"Not supported: DaVinci Resolve's scripting API has no call that opens the "
298
+ f"{label} dialog. Resolve.GetUIManager does not exist on this build."
299
+ ),
300
+ }
301
+ ui_manager = resolve_obj.GetUIManager()
302
+ if not has_method(ui_manager, method_name):
303
+ return {
304
+ "success": False,
305
+ "supported": False,
306
+ "message": (
307
+ f"Not supported: DaVinci Resolve's scripting API has no call that opens the "
308
+ f"{label} dialog. UIManager.{method_name} does not exist on this build."
309
+ ),
310
+ }
315
311
  try:
316
- # Check if UI Manager is available
317
- ui_manager = resolve_obj.GetUIManager()
318
- if not ui_manager:
319
- logger.error("Failed to get UI Manager")
320
- return False
321
-
322
- # Open Preferences dialog
323
- if hasattr(ui_manager, 'OpenPreferences') and callable(getattr(ui_manager, 'OpenPreferences')):
324
- ui_manager.OpenPreferences()
325
- return True
326
-
327
- # Alternative method - send keyboard shortcut based on platform
328
- return False # Keyboard shortcuts not implemented yet
329
- except Exception as e:
330
- logger.error(f"Error opening preferences: {str(e)}")
331
- return False
312
+ opened = getattr(ui_manager, method_name)()
313
+ except Exception as exc:
314
+ logger.error("UIManager.%s raised: %s", method_name, exc)
315
+ return {
316
+ "success": False,
317
+ "supported": True,
318
+ "message": f"Failed to open the {label} dialog: UIManager.{method_name} raised {exc}",
319
+ }
320
+ if opened is False:
321
+ return {
322
+ "success": False,
323
+ "supported": True,
324
+ "message": f"Failed to open the {label} dialog: UIManager.{method_name} returned False",
325
+ }
326
+ return {"success": True, "supported": True, "message": f"{label} dialog opened"}
327
+
328
+
329
+ def open_project_settings(resolve_obj) -> Dict[str, Any]:
330
+ """Open the Project Settings dialog. See `_open_dialog` for the result shape."""
331
+ return _open_dialog(resolve_obj, "OpenProjectSettings", "Project Settings")
332
+
333
+
334
+ def open_preferences(resolve_obj) -> Dict[str, Any]:
335
+ """Open the Preferences dialog. See `_open_dialog` for the result shape."""
336
+ return _open_dialog(resolve_obj, "OpenPreferences", "Preferences")