davinci-resolve-mcp 4.5.0 → 4.5.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 +43 -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/destructive_hook.py +35 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,49 @@
|
|
|
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.5.1 — the safe-mode refusal reaches the caller on 27 more tools
|
|
6
|
+
|
|
7
|
+
v4.5.0 gave the granular server a working safe-mode gate. On 27 tools it then threw
|
|
8
|
+
the answer away.
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **A blocked call raised `ToolError` instead of returning the refusal.** FastMCP
|
|
13
|
+
builds an output schema from a tool's return annotation and validates against it,
|
|
14
|
+
so handing the block dict to a tool annotated `-> str` failed validation:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
ToolError: ...Output / result / Input should be a valid string
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The caller received a generic execution error carrying none of the
|
|
21
|
+
`SAFE_MODE_BLOCKED` code, reason or remediation — the gate fired correctly and its
|
|
22
|
+
answer was destroyed on the way out. Measured on the real `--full` entry path
|
|
23
|
+
against shipped v4.5.0: `clear_folder_transcription` raised rather than refusing.
|
|
24
|
+
|
|
25
|
+
It lands hardest exactly where it matters. The HIGH-rated string-returning tools
|
|
26
|
+
are the calls safe mode exists to stop: `clear_folder_transcription`,
|
|
27
|
+
`unlink_proxy_media`, `replace_clip`, `delete_keyframe`, `quit_app`, `restart_app`.
|
|
28
|
+
|
|
29
|
+
A `-> str` tool is now refused with the message and its remediation as text,
|
|
30
|
+
prefixed with the code. That loses the machine-readable field, which is a real
|
|
31
|
+
cost and worth stating plainly — but a refusal the client can read beats a
|
|
32
|
+
`ToolError` that discards it, and it is the only shape that tool's own schema will
|
|
33
|
+
accept. The 105 tools annotated `-> Dict[str, Any]` or `-> dict` keep the
|
|
34
|
+
structured envelope unchanged.
|
|
35
|
+
|
|
36
|
+
### Validation
|
|
37
|
+
|
|
38
|
+
- Full offline suite: **3,684 passed, 1 skipped, 0 failed**, 1,412 subtests.
|
|
39
|
+
- Both new assertions were confirmed to **fail with the fix reverted**, then pass on
|
|
40
|
+
restore — the guard is not vacuous.
|
|
41
|
+
- Verified end-to-end through the real registry with `destructive.safe_mode` on: a
|
|
42
|
+
`-> str` tool returns readable refusal text, a `-> dict` tool returns the full
|
|
43
|
+
envelope, and `allow_risky_operation=true` still lets a permitted call through on
|
|
44
|
+
both paths.
|
|
45
|
+
- A static check now walks every destructive-decorated tool annotated `-> str` and
|
|
46
|
+
asserts the hook would hand it a string, so tool number 28 cannot reintroduce this.
|
|
47
|
+
|
|
5
48
|
## What's New in v4.5.0 — safe mode and the audit log reach the granular server
|
|
6
49
|
|
|
7
50
|
v4.4.1 froze 131 destructive-hinted granular tools in a backlog and said plainly
|
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
|
-
> 本翻译对应 v4.5.
|
|
15
|
+
> 本翻译对应 v4.5.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 = "4.5.
|
|
40
|
+
VERSION = "4.5.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
|
@@ -93,7 +93,7 @@ if not logging.getLogger().handlers:
|
|
|
93
93
|
handlers=[logging.StreamHandler()],
|
|
94
94
|
)
|
|
95
95
|
|
|
96
|
-
VERSION = "4.5.
|
|
96
|
+
VERSION = "4.5.1"
|
|
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()}")
|
package/src/server.py
CHANGED
|
@@ -991,6 +991,39 @@ def _with_override_param(fn: Callable[..., Any]) -> Tuple[inspect.Signature, boo
|
|
|
991
991
|
return sig.replace(parameters=head + [extra] + tail), True
|
|
992
992
|
|
|
993
993
|
|
|
994
|
+
def _returns_plain_string(fn: Callable[..., Any]) -> bool:
|
|
995
|
+
"""Does this tool declare `-> str`?
|
|
996
|
+
|
|
997
|
+
FastMCP builds an output schema from the return annotation and validates
|
|
998
|
+
against it, so handing a dict to a tool annotated `-> str` raises ToolError
|
|
999
|
+
and the caller never sees the refusal — only a generic execution error with
|
|
1000
|
+
none of its code or remediation. 27 granular tools declare `-> str`, and the
|
|
1001
|
+
HIGH-risk ones among them are exactly the calls safe mode exists to stop.
|
|
1002
|
+
"""
|
|
1003
|
+
annotation = getattr(fn, "__annotations__", {}).get("return")
|
|
1004
|
+
return annotation is str or annotation in ("str", "'str'")
|
|
1005
|
+
|
|
1006
|
+
|
|
1007
|
+
def _block_for(fn: Callable[..., Any], block: Dict[str, Any]) -> Any:
|
|
1008
|
+
"""The refusal, shaped to the contract the tool declares.
|
|
1009
|
+
|
|
1010
|
+
A string-returning tool gets the message and its remediation as text. That
|
|
1011
|
+
loses the machine-readable `code`, which is a real cost — but a refusal the
|
|
1012
|
+
client can read beats a ToolError that discards it entirely, and it is the
|
|
1013
|
+
only shape that tool's own schema will accept.
|
|
1014
|
+
"""
|
|
1015
|
+
if not _returns_plain_string(fn):
|
|
1016
|
+
return block
|
|
1017
|
+
error = block.get("error") or {}
|
|
1018
|
+
message = error.get("message") or "Blocked by destructive.safe_mode."
|
|
1019
|
+
remediation = error.get("remediation")
|
|
1020
|
+
code = error.get("code")
|
|
1021
|
+
parts = [f"{code}: {message}" if code else message]
|
|
1022
|
+
if remediation:
|
|
1023
|
+
parts.append(remediation)
|
|
1024
|
+
return " ".join(parts)
|
|
1025
|
+
|
|
1026
|
+
|
|
994
1027
|
def granular_destructive_op(
|
|
995
1028
|
tool_name: Optional[str] = None,
|
|
996
1029
|
*,
|
|
@@ -1043,13 +1076,13 @@ def granular_destructive_op(
|
|
|
1043
1076
|
params=params,
|
|
1044
1077
|
reason="safe_mode",
|
|
1045
1078
|
)
|
|
1046
|
-
return _security_block_response(
|
|
1079
|
+
return _block_for(fn, _security_block_response(
|
|
1047
1080
|
operation_id=operation_id,
|
|
1048
1081
|
tool_name="granular",
|
|
1049
1082
|
action=action,
|
|
1050
1083
|
risk_level=level,
|
|
1051
1084
|
override_hint=f"{GRANULAR_OVERRIDE_PARAM}=true",
|
|
1052
|
-
)
|
|
1085
|
+
))
|
|
1053
1086
|
|
|
1054
1087
|
result = fn(*args, **kwargs)
|
|
1055
1088
|
_audit_security_event(
|