davinci-resolve-mcp 2.33.4 → 2.33.5

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,20 @@
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.33.5
6
+
7
+ A queryable ledger of verified Resolve API behavior.
8
+
9
+ - **Added** `resolve_control(action="api_truth", query?)` returns
10
+ behaviorally-verified facts about quirky or unreliable Resolve scripting-API
11
+ behavior — methods that live on unexpected objects, return values that lie,
12
+ silently-rejected string keys, and calls that don't exist. Each fact records
13
+ the reality, a recommended approach, and the Resolve build it was verified on.
14
+ No connection required. Backed by `src/utils/api_truth.py`, seeded from
15
+ hard-won findings (AutoSyncAudio, Fusion Paste, FlowView positions,
16
+ GetTimelineByName, project render methods, transcription truncation, the
17
+ CreateProject modal, stdio subprocess hygiene) and meant to grow over time.
18
+
5
19
  ## What's New in v2.33.4
6
20
 
7
21
  Internal reliability framework.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # DaVinci Resolve MCP Server
2
2
 
3
- [![Version](https://img.shields.io/badge/version-2.33.4-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
3
+ [![Version](https://img.shields.io/badge/version-2.33.5-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
4
4
  [![npm](https://img.shields.io/npm/v/davinci-resolve-mcp.svg?label=npm&color=CB3837)](https://www.npmjs.com/package/davinci-resolve-mcp)
5
5
  [![API Coverage](https://img.shields.io/badge/API%20Coverage-100%25-brightgreen.svg)](docs/reference/api-coverage.md)
6
6
  [![Tools](https://img.shields.io/badge/MCP%20Tools-32%20(341%20full)-blue.svg)](#server-modes)
package/docs/SKILL.md CHANGED
@@ -318,6 +318,8 @@ Key actions:
318
318
  - `launch` — connect to or start Resolve; call this first if any tool returns a
319
319
  "Not connected" error
320
320
  - `get_version` — returns `{product, version, version_string}`
321
+ - `api_truth(query?)` — look up behaviorally-verified facts about quirky/unreliable
322
+ Resolve API behavior (no connection needed); filter by substring
321
323
  - `get_page` / `open_page(page)` — read or switch the active page
322
324
  - `get_keyframe_mode` / `set_keyframe_mode(mode)`
323
325
  - `get_fairlight_presets` — Resolve 20.2.2+; returns available Fairlight
package/install.py CHANGED
@@ -35,7 +35,7 @@ from src.utils.update_check import (
35
35
 
36
36
  # ─── Version ──────────────────────────────────────────────────────────────────
37
37
 
38
- VERSION = "2.33.4"
38
+ VERSION = "2.33.5"
39
39
  # Only hard floor: mcp[cli] requires Python 3.10+. There is no upper bound —
40
40
  # Resolve's scripting bridge loads into newer interpreters on recent builds
41
41
  # (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.33.4",
3
+ "version": "2.33.5",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -80,7 +80,7 @@ if not logging.getLogger().handlers:
80
80
  handlers=[logging.StreamHandler()],
81
81
  )
82
82
 
83
- VERSION = "2.33.4"
83
+ VERSION = "2.33.5"
84
84
  logger = logging.getLogger("davinci-resolve-mcp")
85
85
  logger.info(f"Starting DaVinci Resolve MCP Server v{VERSION}")
86
86
  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.33.4"
14
+ VERSION = "2.33.5"
15
15
 
16
16
  import base64
17
17
  import os
@@ -43,6 +43,7 @@ for p in [current_dir, project_dir]:
43
43
  # Platform-specific Resolve paths
44
44
  from src.utils.cdl import normalize_cdl_payload
45
45
  from src.utils.mcp_stdio import run_fastmcp_stdio
46
+ from src.utils.api_truth import lookup_api_truth, VERIFIED_ON as _API_TRUTH_VERIFIED_ON
46
47
  from src.utils.readback import verify_by_readback
47
48
  from src.utils.update_check import (
48
49
  check_for_updates,
@@ -10575,6 +10576,8 @@ def resolve_control(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
10575
10576
  ignore_mcp_update() -> {success, version, update, decision}
10576
10577
  snooze_mcp_update(hours?) -> {success, version, update, decision}
10577
10578
  clear_mcp_update_preferences() -> {success, version, update, decision}
10579
+ api_truth(query?) -> {verified_on, count, facts} — look up behaviorally-verified
10580
+ facts about quirky/unreliable Resolve API behavior (no connection needed).
10578
10581
  get_page() -> {page}
10579
10582
  open_page(page) -> {success} — page: edit, cut, color, fusion, fairlight, deliver
10580
10583
  get_keyframe_mode() -> {mode}
@@ -10595,6 +10598,11 @@ def resolve_control(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
10595
10598
  """
10596
10599
  p = params or {}
10597
10600
 
10601
+ # api_truth is a static knowledge lookup — no Resolve connection needed.
10602
+ if action == "api_truth":
10603
+ facts = lookup_api_truth(p.get("query"))
10604
+ return {"verified_on": _API_TRUTH_VERIFIED_ON, "count": len(facts), "facts": facts}
10605
+
10598
10606
  # Control-panel actions don't require Resolve to be running.
10599
10607
  if action == "open_control_panel":
10600
10608
  return _open_control_panel(p)
@@ -10682,7 +10690,7 @@ def resolve_control(action: str, params: Optional[Dict[str, Any]] = None) -> Dic
10682
10690
  return missing
10683
10691
  r.DisableBackgroundTasksForCurrentResolveSession()
10684
10692
  return _ok()
10685
- return _unknown(action, ["launch","get_version","mcp_update_status","set_mcp_update_policy","ignore_mcp_update","snooze_mcp_update","clear_mcp_update_preferences","get_page","open_page","get_keyframe_mode","set_keyframe_mode","quit","get_fairlight_presets","set_high_priority","disable_background_tasks_for_current_session","open_control_panel","control_panel_status","close_control_panel","save_state","restore_state"])
10693
+ return _unknown(action, ["launch","get_version","api_truth","mcp_update_status","set_mcp_update_policy","ignore_mcp_update","snooze_mcp_update","clear_mcp_update_preferences","get_page","open_page","get_keyframe_mode","set_keyframe_mode","quit","get_fairlight_presets","set_high_priority","disable_background_tasks_for_current_session","open_control_panel","control_panel_status","close_control_panel","save_state","restore_state"])
10686
10694
 
10687
10695
 
10688
10696
  # ─── V2 C4: Per-field corrections with provenance + changelog ────────────────
@@ -0,0 +1,132 @@
1
+ """Behaviorally-verified facts about the DaVinci Resolve scripting API.
2
+
3
+ The Resolve scripting API is under-documented and frequently behaves differently
4
+ from its apparent signature: methods live on objects you wouldn't expect, return
5
+ values lie, string keys are silently rejected, and some documented-looking calls
6
+ don't exist. This module records facts we have *verified against live Resolve* so
7
+ agents and code can look the reality up instead of rediscovering it the hard way.
8
+
9
+ Each entry is a small dict. Grow it opportunistically — every readback recipe and
10
+ every fix that uncovers a surprising behavior should add an entry. Facts are
11
+ stamped with the Resolve build they were verified on so drift is visible when a
12
+ new version ships.
13
+ """
14
+ from typing import Any, Dict, List, Optional
15
+
16
+ VERIFIED_ON = "DaVinci Resolve Studio 21.0.0"
17
+
18
+ # Each entry: symbol, object, reality, recommended, tags. `signature` optional.
19
+ API_TRUTH: List[Dict[str, Any]] = [
20
+ {
21
+ "symbol": "MediaPool.AutoSyncAudio",
22
+ "object": "MediaPool",
23
+ "signature": "(clips, settings) -> bool",
24
+ "reality": "The boolean return does not reflect whether clips actually "
25
+ "linked, and string enum keys in `settings` are silently "
26
+ "rejected (the call returns False).",
27
+ "recommended": "Resolve the AUDIO_SYNC_* enum constants via the live "
28
+ "resolve handle, and verify by reading each clip's "
29
+ "'Synced Audio' property (see verify_by_readback).",
30
+ "tags": ["unreliable-return", "silent-failure", "audio", "enum"],
31
+ },
32
+ {
33
+ "symbol": "Composition.Paste",
34
+ "object": "Fusion Composition",
35
+ "reality": "Passing tool.SaveSettings()'s in-memory table to Paste() / "
36
+ "LoadSettings() fails across the Python bridge with an "
37
+ "OrderedDict/null-argument error and creates no node, while "
38
+ "reporting nothing useful.",
39
+ "recommended": "Duplicate via AddTool(RegID) + SaveSettings(path)/"
40
+ "LoadSettings(path) through a temp .setting FILE, which "
41
+ "round-trips reliably. Identify the new node by name diff.",
42
+ "tags": ["fusion", "bridge", "silent-failure"],
43
+ },
44
+ {
45
+ "symbol": "FlowView.SetPos / FlowView.GetPosTable",
46
+ "object": "Fusion FlowView (comp.CurrentFrame.FlowView)",
47
+ "reality": "Node positions are read/written through the FlowView, not the "
48
+ "tool. SetPos returns nothing reliable; GetPosTable returns a "
49
+ "1-indexed table (or dict/tuple depending on bridge).",
50
+ "recommended": "Use comp.CurrentFrame.FlowView.SetPos(tool, x, y); confirm "
51
+ "with GetPosTable and a liberal position parser.",
52
+ "tags": ["fusion", "unreliable-return"],
53
+ },
54
+ {
55
+ "symbol": "Timeline.GetTimelineByName",
56
+ "object": "Project",
57
+ "reality": "Does not exist. Timelines are looked up by index.",
58
+ "recommended": "Iterate GetTimelineByIndex(1..GetTimelineCount()).",
59
+ "tags": ["missing-method", "timeline"],
60
+ },
61
+ {
62
+ "symbol": "Project render methods (AddRenderJob, SetRenderSettings, ...)",
63
+ "object": "Project",
64
+ "reality": "Render methods live on the Project object, not on a separate "
65
+ "render-settings interface.",
66
+ "recommended": "Call proj.AddRenderJob(), proj.SetRenderSettings(), "
67
+ "proj.LoadRenderPreset() directly on the project.",
68
+ "tags": ["render"],
69
+ },
70
+ {
71
+ "symbol": "MediaPoolItem.GetClipProperty('Transcription')",
72
+ "object": "MediaPoolItem",
73
+ "reality": "Returns a PREVIEW of the transcription that ends in an "
74
+ "ellipsis when the full transcript is longer than the property "
75
+ "exposes.",
76
+ "recommended": "Treat a trailing ellipsis as truncation (see "
77
+ "media_pool_item get_transcription's `truncated` flag).",
78
+ "tags": ["transcription", "truncation"],
79
+ },
80
+ {
81
+ "symbol": "ProjectManager.CreateProject (with a dirty Untitled project)",
82
+ "object": "ProjectManager",
83
+ "reality": "Returns None and pops a modal 'Save Current Project' dialog "
84
+ "when the current unsaved/Untitled project blocks the switch. "
85
+ "SaveProject() on an Untitled project re-triggers the same modal.",
86
+ "recommended": "CloseProject(current) to discard the untitled project "
87
+ "without a prompt, then CreateProject; restore with "
88
+ "LoadProject afterward.",
89
+ "tags": ["project", "modal", "silent-failure"],
90
+ },
91
+ {
92
+ "symbol": "Timeline.InsertFusionCompositionIntoTimeline",
93
+ "object": "Timeline",
94
+ "reality": "Reliable way to obtain a Fusion comp on an otherwise empty "
95
+ "timeline: it inserts a Fusion composition clip whose comp is "
96
+ "then reachable via GetFusionCompByIndex(1).",
97
+ "recommended": "Use it (rather than InsertGeneratorIntoTimeline) when you "
98
+ "need a comp to operate on.",
99
+ "tags": ["fusion", "timeline"],
100
+ },
101
+ {
102
+ "symbol": "subprocess inheriting stdin under the MCP stdio server",
103
+ "object": "(server runtime)",
104
+ "reality": "A child process that inherits stdin can race-read bytes off "
105
+ "the JSON-RPC protocol stream and corrupt it; capture_output "
106
+ "redirects only stdout/stderr.",
107
+ "recommended": "Pass stdin=subprocess.DEVNULL on every subprocess that can "
108
+ "run while serving over stdio.",
109
+ "tags": ["runtime", "stdio", "subprocess"],
110
+ },
111
+ ]
112
+
113
+
114
+ def lookup_api_truth(query: Optional[str] = None) -> List[Dict[str, Any]]:
115
+ """Return verified facts matching `query`, or all facts if no query.
116
+
117
+ Matches a case-insensitive substring against the symbol, tags, and reality.
118
+ """
119
+ if not query:
120
+ return list(API_TRUTH)
121
+ q = query.lower()
122
+ out = []
123
+ for e in API_TRUTH:
124
+ hay = " ".join([
125
+ e.get("symbol", ""),
126
+ e.get("object", ""),
127
+ e.get("reality", ""),
128
+ " ".join(e.get("tags", [])),
129
+ ]).lower()
130
+ if q in hay:
131
+ out.append(e)
132
+ return out