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 +14 -0
- package/README.md +1 -1
- package/docs/SKILL.md +2 -0
- package/install.py +1 -1
- package/package.json +1 -1
- package/src/granular/common.py +1 -1
- package/src/server.py +10 -2
- package/src/utils/api_truth.py +132 -0
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
|
-
[](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
|
|
4
4
|
[](https://www.npmjs.com/package/davinci-resolve-mcp)
|
|
5
5
|
[](docs/reference/api-coverage.md)
|
|
6
6
|
[-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.
|
|
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
package/src/granular/common.py
CHANGED
|
@@ -80,7 +80,7 @@ if not logging.getLogger().handlers:
|
|
|
80
80
|
handlers=[logging.StreamHandler()],
|
|
81
81
|
)
|
|
82
82
|
|
|
83
|
-
VERSION = "2.33.
|
|
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.
|
|
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
|