davinci-resolve-mcp 2.104.8 → 2.104.10

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,59 @@
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.104.10
6
+
7
+ Stones turned on the live-validation backlog, on Studio 19.1.3.7.
8
+
9
+ **AAF live import, validated at last** (marked "NOT live-validated" since
10
+ 2026-07-06): an EXPORT_AAF/EXPORT_AAF_NEW round trip imports cleanly with
11
+ importSourceClips=false, lands fully offline (the documented turnover shape),
12
+ and preserves the start timecode. The naming matrix across import formats is
13
+ now complete and in api_truth — FCP7 XML ignores timelineName (internal name
14
+ wins, the #171 trap); AAF honours timelineName when given, else its internal
15
+ name; OTIO honours timelineName; .drt names the timeline after the file. Only
16
+ FCP7 exhibits the returned-existing trap. The AAF post-import relink leg was
17
+ exercised too: under importSourceClips=false it correctly reports "no Media
18
+ Pool Items to relink" — the API relinks pool items, and none exist on that
19
+ path — so its precondition is now stated instead of assumed.
20
+
21
+ **safe_quick_export verifies its output.** RenderWithQuickExport's status
22
+ dict was the last render surface trusted without a file check: a success
23
+ status that wrote nothing read as an export. The files that actually landed
24
+ in TargetDir are now listed with size and ffprobe duration, and a success
25
+ status with no new file flips to an error.
26
+
27
+ **Housekeeping:** the delete-locked scratch project from the v2.104.7 session
28
+ is gone — the pre-restart DeleteProject had returned False while actually
29
+ succeeding, the documented DeleteProject lie caught in the wild. A clean
30
+ Resolve quit/relaunch verified the wedge entry's other half: Quit() works
31
+ when no orphaned render holds the pipeline.
32
+
33
+ ## What's New in v2.104.9
34
+
35
+ **The NTSC coverage gap, closed.** The conform fixtures were integer-rate
36
+ only — which is how parseEDL ran exact-rate timecode math against a
37
+ nominal-rate writer for years (fixed in v2.104.6, convention measured against
38
+ Resolve's own GetStartFrame). New fixtures now exercise the pipeline at
39
+ 29.97: a broadcast-start EDL parses to nominal frames (an NDF minute is 1800
40
+ frames, butt cuts stay gapless), the EDL write→parse round trip is
41
+ frame-identical, media-inventory's tc↔frames round trip is the identity at
42
+ all three NTSC rates, and drop-frame pins to the canonical values
43
+ (01:00:00;00 → 107892 — the number that haunted the #168 saga, now living
44
+ where it belongs). Cross-language pin tests assert the Python converters
45
+ (_timecode_to_frame_id, multicam) and the Node converters agree on the same
46
+ canonical values, so a change that moves one side fails the other side's
47
+ suite.
48
+
49
+ **Python dependency stack: audited to zero.** pip-audit over the dev venv
50
+ found and cleared advisories in urllib3, requests, python-multipart,
51
+ setuptools, starlette (0.52 → 1.6 — the MCP SDK tolerated the major, full
52
+ suite green), pyjwt, pydantic-settings, pygments, pillow, idna, msgpack,
53
+ cryptography, pip itself, and torch 2.13 (with the matching torchvision).
54
+ Also found: the venv's mcp SDK was at 1.27.0, BELOW the repo's own >=1.29
55
+ floor, and carrying a CVE — now 1.29.1. Both stacks (npm and pip) report
56
+ zero known vulnerabilities, with clean resolver constraints.
57
+
5
58
  ## What's New in v2.104.8
6
59
 
7
60
  More laps: the aggregation class swept to completion, and the dependency
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-2.104.8-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-2.104.10-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-36%20(353%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-2.104.8-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-2.104.10-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-36%20(353%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
- > 本翻译对应 v2.104.8 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
15
+ > 本翻译对应 v2.104.10 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
16
16
 
17
17
  一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
18
18
 
@@ -521,7 +521,7 @@ values, or automation-hostile modal prompts.
521
521
 
522
522
  - **Object:** `MediaPool`
523
523
  - **Signature:** `(filePath, {timelineName, importSourceClips, ...}) -> Timeline`
524
- - **Behavior:** For FCP7 XML, the sequence name INSIDE the file wins over the timelineName import option. When the internal name matches an existing timeline, the call returns that EXISTING timeline — no error, no new timeline — so an export→edit→re-import loop keying uniqueness on the option 'succeeds' while operating on one timeline forever (issue #171, Studio 21.0.4.5; wrapper behavior verified on 19.1.3.7). Distinct from the documented repeated-timelineName None return: here the option is fresh and the file's name is stale.
524
+ - **Behavior:** For FCP7 XML, the sequence name INSIDE the file wins over the timelineName import option. When the internal name matches an existing timeline, the call returns that EXISTING timeline — no error, no new timeline — so an export→edit→re-import loop keying uniqueness on the option 'succeeds' while operating on one timeline forever (issue #171, Studio 21.0.4.5; wrapper behavior verified on 19.1.3.7). Distinct from the documented repeated-timelineName None return: here the option is fresh and the file's name is stale. THE NAMING AUTHORITY DIFFERS PER FORMAT (all measured on 19.1.3.7): FCP7 XML ignores timelineName entirely (internal <name> wins); AAF honours timelineName when given and falls back to its internal name; OTIO honours timelineName; and .drt names the timeline after the FILE (see the .drt entry below). Only FCP7 exhibits the returned-existing trap.
525
525
  - **Workaround / current handling:** Rewrite the <sequence><name> inside the file to the intended name before importing — timeline.import_timeline_checked does this automatically for FCP7 XML and errors when a non-rewritable format still returns an existing timeline. Never treat a truthy return as proof of creation; check the returned timeline's id against the pre-import set.
526
526
  - **Reference:** [issue #171](https://github.com/samuelgursky/davinci-resolve-mcp/issues/171)
527
527
  - **Tags:** timeline, import, silent-failure, unreliable-return
package/install.py CHANGED
@@ -37,7 +37,7 @@ from src.utils.update_check import (
37
37
 
38
38
  # ─── Version ──────────────────────────────────────────────────────────────────
39
39
 
40
- VERSION = "2.104.8"
40
+ VERSION = "2.104.10"
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": "2.104.8",
3
+ "version": "2.104.10",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -87,7 +87,7 @@ if not logging.getLogger().handlers:
87
87
  handlers=[logging.StreamHandler()],
88
88
  )
89
89
 
90
- VERSION = "2.104.8"
90
+ VERSION = "2.104.10"
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
@@ -11,7 +11,7 @@ Usage:
11
11
  python src/server.py --full # Start the 353-tool granular server instead
12
12
  """
13
13
 
14
- VERSION = "2.104.8"
14
+ VERSION = "2.104.10"
15
15
 
16
16
  import base64
17
17
  import os
@@ -18975,8 +18975,40 @@ def _safe_quick_export(proj, p: Dict[str, Any]):
18975
18975
  return {"success": False, "validation": validation}
18976
18976
  if p.get("dry_run") or not p.get("allow_render", False):
18977
18977
  return _ok(would_render=False, preset=preset, params=params, validation=validation)
18978
+ before = set()
18979
+ if target_dir and os.path.isdir(target_dir):
18980
+ before = set(os.listdir(target_dir))
18978
18981
  status = _ser(proj.RenderWithQuickExport(preset, params))
18979
- return {"success": not (isinstance(status, dict) and status.get("error")), "status": status, "params": params}
18982
+ out: Dict[str, Any] = {
18983
+ "success": not (isinstance(status, dict) and status.get("error")),
18984
+ "status": status,
18985
+ "params": params,
18986
+ }
18987
+ # RenderWithQuickExport's status dict is trusted nowhere else in this
18988
+ # repo's render surface anymore (issue #164 taught that lesson) — check
18989
+ # what actually landed on disk. Best-effort: TargetDir is known, so a
18990
+ # successful export must have produced at least one new file there.
18991
+ if out["success"] and target_dir and os.path.isdir(target_dir):
18992
+ new_files = sorted(set(os.listdir(target_dir)) - before)
18993
+ outputs = []
18994
+ for name in new_files:
18995
+ path = os.path.join(target_dir, name)
18996
+ if not os.path.isfile(path):
18997
+ continue
18998
+ rec: Dict[str, Any] = {"path": path, "size_bytes": os.path.getsize(path)}
18999
+ probe = _ffprobe_media_summary(path)
19000
+ if probe:
19001
+ rec["duration_seconds"] = probe.get("duration_seconds")
19002
+ outputs.append(rec)
19003
+ out["outputs"] = outputs
19004
+ if not outputs:
19005
+ out["success"] = False
19006
+ out["error"] = (
19007
+ "RenderWithQuickExport reported success but wrote no file to "
19008
+ f"{target_dir} — treat the status dict as unreliable and check "
19009
+ "the Deliver page's render queue."
19010
+ )
19011
+ return out
18980
19012
 
18981
19013
 
18982
19014
  def _export_render_boundary_report(proj, p: Dict[str, Any]):
@@ -19049,7 +19081,10 @@ def render(action: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, An
19049
19081
  way to read back — see the SetRenderSettings api_truth entry.
19050
19082
  render_job_lifecycle_probe(target_dir, settings?, format?, codec?, custom_name?) -> {success, job_id, status_before_delete}
19051
19083
  quick_export_capabilities() -> {presets, safe_params, guards}
19052
- safe_quick_export(preset, target_dir?|params?, custom_name?, dry_run?, allow_render?) -> {success, status}
19084
+ safe_quick_export(preset, target_dir?|params?, custom_name?, dry_run?, allow_render?) -> {success, status, outputs}
19085
+ After a live export, the files that actually landed in TargetDir are
19086
+ listed with size and ffprobe duration; a success status that wrote no
19087
+ file flips success to false (the issue #164 trust lesson).
19053
19088
  export_render_boundary_report(include_matrix?, max_pairs?, include_quick_export?) -> {capabilities, settings, matrix?}
19054
19089
  list_delivery_targets(tier?, check_availability?) -> {targets, tiers, schema_version}
19055
19090
  resolve_delivery_target(target, overrides?) -> {format_id, codec_id, settings, qc_spec}
@@ -1866,7 +1866,13 @@ API_TRUTH: List[Dict[str, Any]] = [
1866
1866
  "(issue #171, Studio 21.0.4.5; wrapper behavior verified on "
1867
1867
  "19.1.3.7). Distinct from the documented repeated-"
1868
1868
  "timelineName None return: here the option is fresh and the "
1869
- "file's name is stale.",
1869
+ "file's name is stale. THE NAMING AUTHORITY DIFFERS PER "
1870
+ "FORMAT (all measured on 19.1.3.7): FCP7 XML ignores "
1871
+ "timelineName entirely (internal <name> wins); AAF honours "
1872
+ "timelineName when given and falls back to its internal "
1873
+ "name; OTIO honours timelineName; and .drt names the "
1874
+ "timeline after the FILE (see the .drt entry below). Only "
1875
+ "FCP7 exhibits the returned-existing trap.",
1870
1876
  "recommended": "Rewrite the <sequence><name> inside the file to the "
1871
1877
  "intended name before importing — "
1872
1878
  "timeline.import_timeline_checked does this "