davinci-resolve-mcp 2.218.0 → 2.218.1
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 +65 -0
- package/README.md +1 -1
- package/README.zh-CN.md +2 -2
- package/install.py +1 -1
- package/package.json +1 -1
- package/src/granular/common.py +1 -1
- package/src/server.py +1 -1
- package/src/utils/resolve_runtime.py +101 -15
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,71 @@
|
|
|
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.218.1 — Windows 11 process detection survives the removal of WMIC
|
|
6
|
+
|
|
7
|
+
Reported by @Nikibakht (#210), verified on Windows 11 Pro build 26200.
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Every tool refused with `RESOLVE_NOT_RUNNING` on Windows 11 build 26200+,
|
|
12
|
+
while Resolve was running in front of the user.** Process detection read the
|
|
13
|
+
running Resolve's command line through `wmic`, which **Microsoft removed in
|
|
14
|
+
build 26200** — it is neither on `PATH` nor at `C:\Windows\System32\wbem`.
|
|
15
|
+
Spawning it raised `FileNotFoundError`, the read returned `None`, and `None`
|
|
16
|
+
correctly means "cannot determine whether Resolve is running", so the server
|
|
17
|
+
refused to act and declined to launch. The detection logic was right; the
|
|
18
|
+
reader it depended on had ceased to exist.
|
|
19
|
+
- Windows now tries a chain of readers — `wmic`, then Windows PowerShell's
|
|
20
|
+
`Get-CimInstance Win32_Process`, then `pwsh` — and uses the first that
|
|
21
|
+
answers. `None` is returned only when **no** reader ran; a reader that ran
|
|
22
|
+
and found nothing still returns an empty list, which is a different answer.
|
|
23
|
+
Machines that still have WMIC are unaffected, and keeping it first costs
|
|
24
|
+
nothing, because a missing binary fails instantly rather than burning the
|
|
25
|
+
ten-second timeout.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- The PowerShell reader returns **`ProcessId`, `Name`, `ExecutablePath` and
|
|
30
|
+
`CommandLine`**, not the command line alone, so Windows now fills the same
|
|
31
|
+
two-column process table as macOS and Linux. The columns fail independently,
|
|
32
|
+
and @Nikibakht measured how: querying as an unelevated user on build 26200, a
|
|
33
|
+
process the caller cannot fully read still returns its row with `ProcessId`
|
|
34
|
+
and `Name` populated and `CommandLine` NULL — the *column* is
|
|
35
|
+
access-restricted, not the row. Reading only the command line would turn
|
|
36
|
+
such an instance into no row at all: an empty list, which does not mean
|
|
37
|
+
"cannot tell", it means "nothing is running", and that is the answer that
|
|
38
|
+
launches a second Resolve on top of a live one.
|
|
39
|
+
- `Name` is in that query because of the same measurement. It showed `Name`
|
|
40
|
+
surviving the access restriction; it did **not** show `ExecutablePath`
|
|
41
|
+
surviving it, and for a protected process that field is commonly empty too,
|
|
42
|
+
so the executable column falls back to the bare process name — which the
|
|
43
|
+
existing match patterns already accept. An instance is counted on either
|
|
44
|
+
column, and the mode is reported as unknown rather than guessed when the
|
|
45
|
+
argument vector is unreadable, since `-nogui` is only ever visible there.
|
|
46
|
+
Windows rows also carry real pids instead of the synthetic negative ones the
|
|
47
|
+
WMIC branch invents.
|
|
48
|
+
|
|
49
|
+
### Validation
|
|
50
|
+
|
|
51
|
+
- Full suite green: 3,446 passed, 1 skipped. Ten new tests cover the reader chain: a machine with no WMIC, `-nogui`
|
|
52
|
+
surviving the new reader, an unreadable command line still counting as an
|
|
53
|
+
instance, a row where only the process name survives, WMIC still winning
|
|
54
|
+
where it exists, an empty answer ending the chain rather than falling
|
|
55
|
+
through, a broken reader falling through, no reader at all staying
|
|
56
|
+
undeterminable, and the two parsing edges (a command line containing tabs, a
|
|
57
|
+
non-numeric pid).
|
|
58
|
+
- **Not verified on Windows hardware by this project — there is none here.**
|
|
59
|
+
The WMIC absence, the `FileNotFoundError` it raises inside the server's own
|
|
60
|
+
venv, and the access-restricted row shape were all measured by @Nikibakht on
|
|
61
|
+
Windows 11 Pro build 26200. The local half is the unit coverage above, run
|
|
62
|
+
against a faked process spawn.
|
|
63
|
+
- One thing remains **untested by anyone**: an actual Resolve running elevated
|
|
64
|
+
or under a different Windows account. The reporter runs it as the same
|
|
65
|
+
unelevated user and said so rather than guessing; the access-restricted row
|
|
66
|
+
shape above is a proxy measured on other processes in that same access
|
|
67
|
+
class. The fallback is written so that it costs nothing if that case never
|
|
68
|
+
arises.
|
|
69
|
+
|
|
5
70
|
## What's New in v2.218.0 — native Resolve 21.1 transition creation
|
|
6
71
|
|
|
7
72
|
Contributed by @legionsound (#209), live-validated on Studio 21.1.0.14.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
English | [简体中文](README.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
[](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
|
|
6
6
|
[](https://www.npmjs.com/package/davinci-resolve-mcp)
|
|
7
7
|
[](docs/reference/api-coverage.md)
|
|
8
8
|
[-blue.svg)](#server-modes)
|
package/README.zh-CN.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[English](README.md) | 简体中文
|
|
4
4
|
|
|
5
|
-
[](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
|
|
6
6
|
[](https://www.npmjs.com/package/davinci-resolve-mcp)
|
|
7
7
|
[](docs/reference/api-coverage.md)
|
|
8
8
|
[-blue.svg)](#服务器模式)
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
[](https://www.python.org/downloads/)
|
|
13
13
|
[](https://opensource.org/licenses/MIT)
|
|
14
14
|
|
|
15
|
-
> 本翻译对应 v2.218.
|
|
15
|
+
> 本翻译对应 v2.218.1 版 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 = "2.218.
|
|
40
|
+
VERSION = "2.218.1"
|
|
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
package/src/granular/common.py
CHANGED
|
@@ -87,7 +87,7 @@ if not logging.getLogger().handlers:
|
|
|
87
87
|
handlers=[logging.StreamHandler()],
|
|
88
88
|
)
|
|
89
89
|
|
|
90
|
-
VERSION = "2.218.
|
|
90
|
+
VERSION = "2.218.1"
|
|
91
91
|
logger = logging.getLogger("davinci-resolve-mcp")
|
|
92
92
|
logger.info(f"Starting DaVinci Resolve MCP Server v{VERSION}")
|
|
93
93
|
logger.info(f"Detected platform: {get_platform()}")
|
package/src/server.py
CHANGED
|
@@ -96,6 +96,91 @@ def _split_pid(line: str, index: int):
|
|
|
96
96
|
return -(index + 1), line
|
|
97
97
|
|
|
98
98
|
|
|
99
|
+
def _windows_wmic_rows(stdout: str) -> List[Dict[str, Optional[str]]]:
|
|
100
|
+
"""WMIC prints one command line per row, under a `CommandLine` header.
|
|
101
|
+
|
|
102
|
+
The header row and WMIC's blank padding rows are left in rather than
|
|
103
|
+
filtered: they are not Resolve command lines, so the executable match
|
|
104
|
+
drops them, and a second filter here would be a second place for that
|
|
105
|
+
decision to drift. There is no pid column to read, so pids are synthetic
|
|
106
|
+
and negative — they exist only to key the row, never to name a process.
|
|
107
|
+
"""
|
|
108
|
+
return [{"pid": -(index + 1), "comm": None, "args": line}
|
|
109
|
+
for index, line in enumerate(stdout.splitlines())]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _windows_cim_rows(stdout: str) -> List[Dict[str, Optional[str]]]:
|
|
113
|
+
"""`ProcessId`, `ExecutablePath` and `CommandLine`, tab-separated per row.
|
|
114
|
+
|
|
115
|
+
Four columns rather than the command line alone, because they fail
|
|
116
|
+
independently exactly as they do on POSIX. Measured on build 26200 by the
|
|
117
|
+
reporter of #210, querying as an unelevated user: for a process the caller
|
|
118
|
+
cannot fully read, CIM still returns the row with `ProcessId` and `Name`
|
|
119
|
+
populated and `CommandLine` NULL — the *column* is access-restricted, not
|
|
120
|
+
the row. Reading only the command line would turn such an instance into no
|
|
121
|
+
row at all: an empty list, which does not mean "undeterminable", it means
|
|
122
|
+
"nothing is running", and that is the answer that launches a second
|
|
123
|
+
Resolve on top of a live one.
|
|
124
|
+
|
|
125
|
+
`Name` rather than `ExecutablePath` alone is the reason this holds. That
|
|
126
|
+
measurement showed `Name` surviving the access restriction; it did not
|
|
127
|
+
show `ExecutablePath` surviving it, and for a protected process that field
|
|
128
|
+
is commonly empty too. So the executable column falls back to the bare
|
|
129
|
+
process name, which `RESOLVE_PROCESS_PATTERNS` already matches — enough to
|
|
130
|
+
prove an instance is up, while the mode stays honestly unknown, since
|
|
131
|
+
`-nogui` is only ever visible in the command line.
|
|
132
|
+
|
|
133
|
+
Split at most three times: a command line may itself contain tabs, and it
|
|
134
|
+
is the last field, so everything after the third separator belongs to it.
|
|
135
|
+
"""
|
|
136
|
+
rows: List[Dict[str, Optional[str]]] = []
|
|
137
|
+
for index, line in enumerate(stdout.splitlines()):
|
|
138
|
+
if not line.strip():
|
|
139
|
+
continue
|
|
140
|
+
fields = line.split("\t", 3)
|
|
141
|
+
fields += [""] * (4 - len(fields))
|
|
142
|
+
try:
|
|
143
|
+
pid = int(fields[0].strip())
|
|
144
|
+
except ValueError:
|
|
145
|
+
pid = -(index + 1)
|
|
146
|
+
rows.append({"pid": pid,
|
|
147
|
+
"comm": fields[2].strip() or fields[1].strip() or None,
|
|
148
|
+
"args": fields[3].strip() or None})
|
|
149
|
+
return rows
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
#: PowerShell equivalent of the WMIC query, emitting the four columns above.
|
|
153
|
+
#: The output encoding is forced because the default console codepage mangles
|
|
154
|
+
#: a non-ASCII install path before Python ever sees it.
|
|
155
|
+
_CIM_COMMAND = (
|
|
156
|
+
"[Console]::OutputEncoding=[Text.Encoding]::UTF8; "
|
|
157
|
+
"Get-CimInstance Win32_Process -Filter \"name='Resolve.exe'\" | "
|
|
158
|
+
"ForEach-Object { \"$($_.ProcessId)`t$($_.Name)`t$($_.ExecutablePath)`t$($_.CommandLine)\" }"
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
#: Readers for the Windows process table, tried in order until one answers.
|
|
162
|
+
#:
|
|
163
|
+
#: WMIC first, so a machine that still has it behaves exactly as it did before
|
|
164
|
+
#: — but **WMIC was removed in Windows 11 build 26200** and is neither on PATH
|
|
165
|
+
#: nor at its old System32\wbem location, so on current Windows it raises
|
|
166
|
+
#: FileNotFoundError and every tool refused with "Resolve is not running"
|
|
167
|
+
#: while Resolve sat in front of the user (#210). Keeping the old reader first
|
|
168
|
+
#: costs nothing precisely because absence fails instantly rather than burning
|
|
169
|
+
#: the timeout. Windows PowerShell 5.1 ships with Windows; `pwsh` is the
|
|
170
|
+
#: cross-platform 7.x binary, tried last for a machine that has only that one.
|
|
171
|
+
#:
|
|
172
|
+
#: `None` is returned only when NO reader ran. A reader that ran and found
|
|
173
|
+
#: nothing returns an empty list, which is a different answer.
|
|
174
|
+
WINDOWS_PROCESS_READERS = (
|
|
175
|
+
(["wmic", "process", "where", "name='Resolve.exe'", "get", "CommandLine"],
|
|
176
|
+
_windows_wmic_rows),
|
|
177
|
+
(["powershell", "-NoProfile", "-NonInteractive", "-Command", _CIM_COMMAND],
|
|
178
|
+
_windows_cim_rows),
|
|
179
|
+
(["pwsh", "-NoProfile", "-NonInteractive", "-Command", _CIM_COMMAND],
|
|
180
|
+
_windows_cim_rows),
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
99
184
|
def _process_table() -> Optional[List[Dict[str, Optional[str]]]]:
|
|
100
185
|
"""One row per process: `{pid, comm, args}`, or None when undeterminable.
|
|
101
186
|
|
|
@@ -113,27 +198,28 @@ def _process_table() -> Optional[List[Dict[str, Optional[str]]]]:
|
|
|
113
198
|
"""
|
|
114
199
|
if platform.system().lower() == "windows":
|
|
115
200
|
# `tasklist` prints no command line, so the flag is invisible there.
|
|
116
|
-
#
|
|
201
|
+
# The readers below do print it, which is what makes headless
|
|
202
|
+
# detection possible on Windows at all.
|
|
117
203
|
#
|
|
118
204
|
# Decoded explicitly: `text=True` alone decodes with the locale
|
|
119
205
|
# codepage, which raises UnicodeDecodeError on a byte cp1252 has no
|
|
120
206
|
# mapping for — and this read is the input to the second-instance
|
|
121
207
|
# guard, so it must fail to "cannot tell", never to an exception.
|
|
122
208
|
# ASCII is byte-identical under both codecs, so the matching this
|
|
123
|
-
# feeds is unchanged; what
|
|
124
|
-
# on a non-English Windows is not something we can verify here.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
209
|
+
# feeds is unchanged; what these readers emit for a non-ASCII install
|
|
210
|
+
# path on a non-English Windows is not something we can verify here.
|
|
211
|
+
for reader, parse in WINDOWS_PROCESS_READERS:
|
|
212
|
+
try:
|
|
213
|
+
out = subprocess.run(
|
|
214
|
+
reader, capture_output=True, text=True, encoding="utf-8",
|
|
215
|
+
errors="replace", timeout=10, check=False,
|
|
216
|
+
)
|
|
217
|
+
except Exception:
|
|
218
|
+
continue # this reader is unusable here; try the next one
|
|
219
|
+
if out.returncode != 0 and not (out.stdout or "").strip():
|
|
220
|
+
continue
|
|
221
|
+
return parse(out.stdout or "")
|
|
222
|
+
return None
|
|
137
223
|
|
|
138
224
|
comm_lines = _run_ps("pid=,comm=")
|
|
139
225
|
args_lines = _run_ps("pid=,args=")
|