davinci-resolve-mcp 4.14.3 → 4.15.0

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,155 @@
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.15.0 — Conform-session fixes, Python doctor, restart hint
6
+
7
+ Six gaps a real conform session hit on Resolve Studio 19.1.3.7, each measured
8
+ live with rendered frames; `scripts/doctor.py` now checks the Python the
9
+ server runs on; and a refused scripting connection with the preference already
10
+ Local now says to restart Resolve instead of changing a setting that is
11
+ already correct.
12
+
13
+ ### Fixed
14
+
15
+ - **`timeline_frame capture` works on projects where rendering a frame fails.**
16
+ The default `quality="frame"` now exports the viewer's frame with
17
+ `Project.ExportCurrentFrameAsStill`: seek, read the playhead back by frame,
18
+ export, read the playhead again, restore it. It switches no page and writes
19
+ no render settings. On the project that surfaced this, the render route
20
+ failed every call with "AddRenderJob returned nothing"; the export worked
21
+ every call (3840×2160 in about 0.2 s). The single-frame render is still
22
+ there as `quality="render"`, used only when asked for by name. `preview` and
23
+ `full` now mean the export.
24
+ - **Keyframe actions no longer crash on 19.x.** `timeline_item`
25
+ `get_keyframes`, `add_keyframe`, `modify_keyframe`, `delete_keyframe` and
26
+ `set_keyframe_interpolation` died with "'NoneType' object is not callable":
27
+ a live 19.1 TimelineItem exposes no keyframe methods. They now return
28
+ `KEYFRAME_API_UNAVAILABLE` with the live version and what to do instead.
29
+ - **Locked tracks are named, not answered with a bare False.**
30
+ `timeline delete_clips` refuses with `TRACK_LOCKED` listing the locked
31
+ tracks before calling `DeleteClips`. A failed `media_pool
32
+ append_to_timeline` names locked target tracks in its error.
33
+ - **Page switches refuse during playback.** A Color-page visit while playback
34
+ ran, followed by `restore_state`, hung Resolve's main thread, and every
35
+ later scripting call hung with it. `resolve_control open_page` and
36
+ `restore_state`, `gallery_stills grab_and_export`, and the page-switching
37
+ capture routes detect a moving playhead and refuse with `PLAYBACK_ACTIVE`.
38
+ `allow_during_playback=true` overrides.
39
+ - **A refused connection with scripting already set to Local now says to
40
+ restart Resolve.** When Resolve was running but refused scripting
41
+ connections, `SCRIPTING_UNAVAILABLE` always said to set Preferences >
42
+ General > External scripting = Local, and implied the free edition was the
43
+ cause. A running Studio can refuse every connection with the preference
44
+ already Local until it is relaunched. The server now reads
45
+ `System.Scripting.Mode` from Resolve's `config.dat`, and when it is Local
46
+ the error leads with "Quit and relaunch DaVinci Resolve", then the existing
47
+ guidance. When the preference is not Local or cannot be read, the message is
48
+ unchanged. The error `state` has a new `scripting_preference_local` field
49
+ (true, false or null). The macOS `config.dat` path was checked on a real
50
+ Mac; the Windows and Linux paths are untested, and a wrong path reads as
51
+ unknown, which keeps the old message.
52
+
53
+ ### Added
54
+
55
+ - **`timeline replace_source_range`: slip a clip to a new source range.**
56
+ 19.x has no slip or trim API. The action rebuilds the item at the same
57
+ record frame through a temporary track: append the new range there,
58
+ CopyGrades and carry transform, crop, composite and scaling onto it, delete
59
+ the original, append the final copy on the original track, copy grade and
60
+ properties again, remove the temporary copy and track, restore the track
61
+ lock. Start, end and duration are verified exactly. A length that lands a
62
+ frame off (mixed source and timeline rates) is corrected and retried. If the
63
+ final placement fails, the temporary copy stays as the recovery and the
64
+ error says where. Confirm-token gated, archived first, `dry_run` supported;
65
+ a locked track needs `unlock_track=true`.
66
+ - **`timeline_item probe_keyframe_override`.** Tells whether a property write
67
+ reaches the render. On a clip with Inspector keyframes, `SetProperty`
68
+ returns True and reads back while the render ignores it. The probe exports
69
+ the frame twice as a null control, nudges the property, exports again,
70
+ restores the property and compares. Verdict: `override`, `writes_render` or
71
+ `inconclusive`.
72
+ - **`set_transform` takes `pan_px` / `tilt_px`.** Pan and Tilt scale with the
73
+ fitted image: Pan moves `Pan × fittedW / timelineW` px. For scale-to-fit the
74
+ action computes the fitted size from the clip and timeline resolutions and
75
+ reports the factors it used. Other scaling modes need `fitted_width` /
76
+ `fitted_height`.
77
+ - **`scripts/doctor.py` checks the interpreter the server runs on.** The checks
78
+ live in `scripts/doctor_python_env.py`. Each problem is reported as WARN or
79
+ FAIL with a `Fix:` line (a `fix` key under `--json`) that gives the exact
80
+ command.
81
+ - *Python bitness.* A 32-bit Python fails, because Resolve's scripting
82
+ library is 64-bit only. So does a Python whose architecture the
83
+ `fusionscript` library was not built for. The library header (ELF, thin
84
+ or universal Mach-O, PE) is read directly. An Intel Python under Rosetta
85
+ against an arm64-only library is named as such.
86
+ - *Python version for Resolve.* Below 3.10 fails. 3.13+ warns unless Resolve
87
+ answered on this interpreter or the build is 20.3.2 or later. The build is
88
+ read from the live probe or, on macOS, the app bundle. 3.13+ fails when
89
+ the scripting module is present but will not import.
90
+ - *Which Python.* It reads the Claude Desktop and Codex configs. It warns
91
+ when a config starts the server as a bare `python3`, which a GUI app
92
+ resolves through a PATH that is not your shell's. It also warns when a
93
+ config runs a different interpreter from the one doctor checked. It lists
94
+ every Python on PATH and marks the server's. A venv and its base
95
+ interpreter count as different Pythons, because they share a binary but
96
+ not `sys.prefix`. It also states where `fusionscript` loads: external
97
+ scripting loads it into the server's interpreter, and Resolve's own
98
+ Scripts menu uses the Python it finds itself.
99
+ - The fix for a wrong interpreter moves the venv aside before re-running the
100
+ installer, because `install.py` reuses an existing venv.
101
+ - **The `SCRIPTING_UNAVAILABLE` remediation points at doctor** for the case
102
+ where Studio is already set to Local and the cause is the server's Python.
103
+
104
+ ### Documentation
105
+
106
+ - New api_truth entries, carried into `docs/reference/api-limitations.md`:
107
+ ExportCurrentFrameAsStill as the frame-capture route, the silent
108
+ keyframe override on SetProperty, DeleteClips on a locked track, Edit
109
+ transform units (reconciling the "Pan = timeline pixels" rule of thumb,
110
+ which holds only when the fitted width equals the timeline's), OpenPage
111
+ during playback, and the missing slip/trim API.
112
+ - `docs/SKILL.md`: capture routes rewritten for the new default; Known Gotchas
113
+ cover playback, keyframe overrides, locked tracks, transform units and the
114
+ slip action.
115
+
116
+ ### Validation
117
+
118
+ - Full Python suite: 4,221 tests, zero failures, 5 skipped. New
119
+ `tests/test_conform_session_gaps.py` (45 tests), an updated
120
+ `tests/test_playhead_frame_capture.py`, `tests/test_doctor_python_env.py`
121
+ (every check against fakes, with a clean 64-bit 3.12 setup as the null
122
+ control), and `ScriptingPreferenceTests` in `tests/test_resolve_bridge.py`.
123
+ No advanced-server (Node) code changed.
124
+ - Live: the conform behaviour these fixes encode was measured in the conform
125
+ session itself. A live pass of the new code paths on a scratch project has
126
+ not run, because Resolve was in use by that session. The restart hint was
127
+ checked by reading this Mac's real `config.dat` (Local) and producing the
128
+ restart message.
129
+
130
+ ## What's New in v4.14.4 — Compatible dependency updates
131
+
132
+ Lands #303, #304, #305, #306, and #308. Resolves #307, #309, and #310.
133
+
134
+ ### Maintenance
135
+
136
+ - Update fast-xml-parser to 5.11.2, JSZip to 3.10.2, Zod to 4.6.7,
137
+ PostgreSQL client pg to 8.23.1, and development formatter Prettier to 3.9.10.
138
+ - Align the published root package with the advanced-server dependency ranges
139
+ and audited lockfile versions, including the MCP SDK and sharp security fixes.
140
+ - Group weekly patch/minor Dependabot updates across both package manifests.
141
+ - Defer js-yaml v5 because its default merge-key and empty-input behavior changes
142
+ existing YAML specs. Defer native better-sqlite3 major upgrades for an explicit
143
+ compatibility review; v13 requires Node 22 while this project supports Node 20.9.
144
+
145
+ ### Validation
146
+
147
+ - Full advanced-server Node suite: 1,141 passed, 60 skipped, zero failed.
148
+ - Root and advanced-server npm audits report zero vulnerabilities.
149
+ - Required release static checks pass. Control-panel documentation reviewed;
150
+ no panel behavior or navigation changed.
151
+ - No Resolve scripting or project authoring behavior changed; live Resolve
152
+ validation is not required for these dependency and maintenance updates.
153
+
5
154
  ## What's New in v4.14.3 — Secure footage indexing
6
155
 
7
156
  ### Security
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-4.14.3-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-4.15.0-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-37%20(390%20full)-blue.svg)](#server-modes)
@@ -449,6 +449,7 @@ Extension authoring references live in [docs/authoring](docs/authoring/). Resolv
449
449
 
450
450
  - DaVinci Resolve 18.5+ on macOS, Windows, or Linux. **Studio** supports external scripting directly. The **free edition** does not — Blackmagic gates external scripting to Studio — but it is still reachable through the [in-app bridge](#free-edition-in-app-bridge), which runs inside Resolve from the ungated **Workspace ▸ Scripts** menu.
451
451
  - Python 3.10+ (3.10-3.12 is the lowest-risk range). Python 3.13/3.14 also work on recent Resolve builds (verified on Studio 20.3.2); older builds may fail to connect on 3.13+, in which case use 3.10-3.12.
452
+ - Not connecting? `python3 scripts/doctor.py` checks the Python the server runs on and prints the exact fix for each problem: 32-bit or wrong-architecture Python, a Python too new for your Resolve build, or several Pythons where the MCP client starts the wrong one. See [Checking the Python environment](docs/install.md#checking-the-python-environment).
452
453
  - Resolve external scripting set to **Local** (Studio). On the free edition this
453
454
  preference has no effect — use the [in-app bridge](#free-edition-in-app-bridge)
454
455
  instead.
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-4.14.3-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-4.15.0-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-37%20(390%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
- > 本翻译对应 v4.14.3 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
15
+ > 本翻译对应 v4.15.0 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
16
16
 
17
17
  一个 Model Context Protocol (MCP) 服务器,让 AI 助手通过官方脚本 API 控制 DaVinci Resolve Studio(达芬奇)。它提供完整的 API 覆盖,外加带护栏的工作流助手,涵盖剪辑、媒体池整理、渲染设置、审阅标记、调色、Fusion、Fairlight、项目生命周期任务、扩展开发,以及不碰源媒体的媒体分析。
18
18
 
package/docs/SKILL.md CHANGED
@@ -1890,9 +1890,16 @@ rendering — grade, Fusion, titles, transitions — rather than inferring it fr
1890
1890
  metadata. (For the raw camera file instead, use
1891
1891
  `media_analysis(action="extract_frames")`.)
1892
1892
 
1893
- - `quality="frame"` (default) renders exactly that frame — the only
1894
- frame-accurate route. Full resolution, well under a second, works headless.
1895
- `preview` is the same render bounded to 1280px.
1893
+ - `quality="frame"` (default) exports exactly that frame with
1894
+ `Project.ExportCurrentFrameAsStill`: frame-accurate, full resolution, about
1895
+ 0.2 s, **no page switch and no render settings**. The seek is read back by
1896
+ frame before the export and the playhead is read again after it; a playhead
1897
+ that moved means playback is running and the capture refuses
1898
+ (`PLAYBACK_ACTIVE`). `preview` is the same export bounded to 1280px.
1899
+ - `quality="render"` renders the frame as a one-frame render job. Use it only
1900
+ when you want that route by name: on a real project on Studio 19.1.3.7 it
1901
+ failed every call with "AddRenderJob returned nothing" while the export
1902
+ worked every call.
1896
1903
  - `quality="thumbnail"` is instant and touches nothing, but returns the **clip's**
1897
1904
  thumbnail — identical for every frame of that clip. Use it to see which clip is
1898
1905
  under the playhead, never to judge a specific frame. Needs the Color page *and*
@@ -1903,22 +1910,23 @@ metadata. (For the raw camera file instead, use
1903
1910
  (default), `png`, or `tif`.
1904
1911
  - `timecode` accepts absolute (`01:00:15:12`) or elapsed (`00:00:15:12`) time;
1905
1912
  `frame` is the absolute timeline frame. Omit both to capture the playhead.
1913
+ - The page-switching routes (`render`, `thumbnail`, `still`) refuse while
1914
+ playback is running; `allow_during_playback=true` overrides.
1906
1915
 
1907
- The playhead, page, current timeline and Gallery are restored. The render route
1916
+ The playhead, page, current timeline and Gallery are restored. The `render` route
1908
1917
  additionally touches project render settings and puts them back: render mode,
1909
1918
  format, codec, mark range and output folder (`TargetDir`, read off a throwaway
1910
1919
  render job because there is no `GetRenderSettings`). The file name
1911
1920
  (`CustomName`) is never written, and the render job is deleted. One gap: a
1912
1921
  project that has never had an output folder has none to put back, and Resolve
1913
1922
  cannot clear one, so it is left on the capture's temporary folder
1914
- (`capabilities` repeats this as `render_settings_caveat`). Reach for
1915
- `quality="thumbnail"` when zero side effects matter more than accuracy.
1923
+ (`capabilities` repeats this as `render_settings_caveat`).
1916
1924
 
1917
- The render calls pull Resolve onto the Deliver page. The capture switches back
1918
- and reads the page to confirm it. If a restore does not take (page, playhead,
1919
- timeline, render mode, format, range or output folder), the image is followed by a
1920
- `{"warnings": [...]}` block naming what was left changed and the call that puts
1921
- it back. No warnings block means every restore was confirmed.
1925
+ The render calls pull Resolve onto the Deliver page. The `render` route switches
1926
+ back and reads the page to confirm it. If a restore does not take (page,
1927
+ playhead, timeline, render mode, format, range or output folder), the image is
1928
+ followed by a `{"warnings": [...]}` block naming what was left changed and the
1929
+ call that puts it back. No warnings block means every restore was confirmed.
1922
1930
 
1923
1931
  ```
1924
1932
  timeline_frame(action="capture", params={"timecode": "01:00:15:12", "max_width": 1280})
@@ -2493,6 +2501,42 @@ Other recorded traps ride along on results as `known_limitation` without blockin
2493
2501
  `RESOLVE_MCP_DISABLE_TRAP_GUARD=1` disables both behaviours.
2494
2502
 
2495
2503
 
2504
+ **Stop playback before switching pages** — On Studio 19.1.3.7 a Color-page visit
2505
+ while playback was running, followed by `restore_state`, timed out, and after
2506
+ that every scripting call hung, even a fresh connection: Resolve's main thread
2507
+ was stuck in a viewer render loop. 19.x has no transport API, so the server
2508
+ cannot stop playback. `resolve_control open_page` / `restore_state`,
2509
+ `gallery_stills grab_and_export` and the page-switching `timeline_frame` routes
2510
+ refuse with `PLAYBACK_ACTIVE` when the playhead is moving; ask the user to stop
2511
+ playback (Space or K). `allow_during_playback=true` overrides.
2512
+
2513
+ **A transform write that reads back can still not render** — On a clip with
2514
+ Inspector keyframes (an AAF import can bring them), `SetProperty('ZoomX', v)`
2515
+ returns True and reads back `v`, and the render does not change. 19.x has no
2516
+ TimelineItem keyframe methods at all, so the `timeline_item` keyframe actions
2517
+ return `KEYFRAME_API_UNAVAILABLE` there. `timeline_item(action=
2518
+ "probe_keyframe_override")` exports the frame twice (null control), nudges the
2519
+ property, exports again and restores it; `verdict: "override"` means keyframes
2520
+ are winning. Prove sizing with frames, never with the readback.
2521
+
2522
+ **Locked tracks fail deletes silently** — `DeleteClips` on a locked track
2523
+ returns a bare False, while `SetProperty` on the same items still works.
2524
+ `timeline delete_clips` refuses with `TRACK_LOCKED` naming the tracks.
2525
+
2526
+ **Edit transform units** — Zoom is relative to the fitted image. Pan moves
2527
+ `Pan × fittedW / timelineW` px and Tilt `Tilt × fittedH / timelineH` px (+up),
2528
+ so Pan equals timeline pixels only when the fitted width equals the timeline's.
2529
+ A 720×1280 clip in a 4096×1716 timeline fits at 965×1716, and Pan −500 moved it
2530
+ 117 px. `set_transform` takes `pan_px` / `tilt_px` and converts for
2531
+ scale-to-fit.
2532
+
2533
+ **Slipping a clip** — there is no slip or trim API.
2534
+ `timeline(action="replace_source_range", params={"timeline_item_id": ...,
2535
+ "source_start": N})` rebuilds the item at the same record frame through a
2536
+ temporary track, carrying grade, transform, crop, composite and scaling, and
2537
+ verifies start, end and duration exactly. It is confirm-token gated, archives
2538
+ the timeline first, and refuses a locked track unless `unlock_track=true`.
2539
+
2496
2540
  **Resolve API object lifetimes** — Objects like timelines, clips, and color groups
2497
2541
  returned by the API are live references that can become stale if the project state
2498
2542
  changes (e.g., the user deletes a timeline). Always re-fetch IDs after any
@@ -2572,8 +2616,9 @@ extracted from a delivered render.
2572
2616
 
2573
2617
  **Start here: `timeline_frame(action="capture")`** — Returns the frame at the
2574
2618
  playhead (or at any `timecode`/`frame` you name) as MCP image content, so a
2575
- multimodal assistant can simply look at it. It renders that one frame, which is
2576
- what makes it frame-accurate; `max_width` bounds the context cost.
2619
+ multimodal assistant can simply look at it. It exports that one frame from the
2620
+ viewer with `ExportCurrentFrameAsStill`, which is frame-accurate and switches no
2621
+ page; `max_width` bounds the context cost.
2577
2622
 
2578
2623
  ```
2579
2624
  timeline_frame(action="capture", params={"timecode": "01:00:15:12", "max_width": 1280})
package/docs/install.md CHANGED
@@ -53,6 +53,32 @@ Model weights carry their own licences, separate from the code that loads them.
53
53
  > (`brew install python@3.12`, `pyenv install 3.12`, or python.org on Windows) and
54
54
  > point the launcher at it with `DAVINCI_RESOLVE_MCP_PYTHON=/path/to/python3.12`.
55
55
 
56
+ ### Checking the Python environment
57
+
58
+ Most connection failures that are not Resolve's preferences are Python. Run the
59
+ read-only doctor from the checkout, with any Python:
60
+
61
+ ```bash
62
+ python3 scripts/doctor.py # add --json for machine-readable output
63
+ ```
64
+
65
+ Besides paths and client configs, it checks the interpreter the server runs on.
66
+ Each problem is reported as `WARN` or `FAIL` with a `Fix:` line giving the exact
67
+ command:
68
+
69
+ | Check | Fails or warns when |
70
+ |---|---|
71
+ | **Python bitness** | The server's Python is 32-bit (Resolve's scripting library is 64-bit only), or its architecture is not one the `fusionscript` library is built for. The common case is an Intel Python running under Rosetta on an Apple silicon Mac. The library header is read directly, so a universal build counts as both. |
72
+ | **Python version for Resolve** | Below 3.10 (`FAIL`, the SDK floor). On 3.13+ it warns unless Resolve answered on this interpreter or the Resolve build is 20.3.2 or later. It fails if the scripting module is present but will not import. |
73
+ | **Client interpreter** | An MCP client config starts the server as a bare `python3` (resolved through the PATH the GUI app sees, not your shell's), or runs a different interpreter from the one doctor checked. |
74
+ | **Pythons on PATH** | Several are installed and the first is not the server's. This is `INFO` unless a bare client command or a 32-bit or too-old Python first on PATH makes it matter. |
75
+ | **Where fusionscript loads** | Never; this is `INFO`. External scripting loads `fusionscript` into the *server's* interpreter. Resolve's own Scripts menu, which the free-edition bridge runs from, uses a Python it finds itself. On macOS that is `PYTHON3HOME`, then `/usr/local/bin/python3`. The two need not match. |
76
+
77
+ `install.py` reuses an existing venv, so the fix for a wrong interpreter moves
78
+ the venv aside before re-running the installer with a 64-bit Python 3.12.
79
+ `npx davinci-resolve-mcp doctor` is the installer's dry run. It does not run
80
+ these checks.
81
+
56
82
  Validated live coverage is based on **DaVinci Resolve 19.1.3 Studio** for the original API surface, plus **DaVinci Resolve 20.3.2 Studio** for the Resolve 20.0-20.2.2 scripting additions. Resolve 21 beta APIs are intentionally deferred until a stable release.
57
83
 
58
84
  ## Quick Start
@@ -12,7 +12,7 @@ that none exists).
12
12
 
13
13
  **Verified on:** DaVinci Resolve Studio 21.0.2
14
14
 
15
- **Totals:** 42 missing capabilities, 58 bugs / unreliable behaviors.
15
+ **Totals:** 43 missing capabilities, 61 bugs / unreliable behaviors.
16
16
 
17
17
  The authoritative source is the runtime-queryable `api_truth` ledger
18
18
  (`resolve_control api_truth "<query>"`); this document is generated from
@@ -361,6 +361,13 @@ equivalent, blocking full automation.
361
361
  - **Workaround / current handling:** Read <in> as the literal source frame (Resolve reads and writes it that way), record-align it by the junction-minus-span-start offset on a -1 start, and walk -1/-1 clips with a record-order cursor. conform.snapshot ingest_xml and editorial.parse_interchange both do; the frame QC then samples each cut CLEAR of its transition windows (inside one the reference is a blend, or black for a fade).
362
362
  - **Tags:** timeline, export, xml, fcp7, transitions, silent-failure
363
363
 
364
+ ### Timeline slip / trim of an existing item (no API on 19.x)
365
+
366
+ - **Object:** `Timeline`
367
+ - **Behavior:** There is no call to slip an item's source range or trim its edges in place: no SetProperty key moves the source in-point and no Timeline method takes a new range. The only route is to delete the item and append the new range at the same record frame, which loses its grade, transform, crop and composite unless they are carried over by hand. With a source rate different from the timeline's, the appended length can come out a frame off (the AppendToTimeline mixed-fps floor) and needed a corrected endFrame and a second append.
368
+ - **Workaround / current handling:** timeline replace_source_range: append the new range on a temporary track at the same record frame, CopyGrades onto it, carry transform/crop/composite/scaling, delete the original, append the final copy on the original track, CopyGrades from the temporary copy, re-apply the properties, delete the temporary copy, restore the track lock. Start, end and duration are verified exactly; an endFrame that lands a frame off is corrected and retried.
369
+ - **Tags:** missing-method, timeline, edit, mixed-fps
370
+
364
371
  ## Bugs / Unreliable Behavior (please fix)
365
372
 
366
373
  Methods that exist but misbehave — silent failures, unreliable return
@@ -830,3 +837,27 @@ values, or automation-hostile modal prompts.
830
837
  - **Behavior:** No scriptable call produces an archive. With source media and proxies off it returns False instantly and writes nothing, for an open or a closed project and with render cache on or off. With isArchiveSrcMedia or isArchiveProxyMedia on it creates an empty directory at the target and Resolve crashes (SIGSEGV) in the same second; the call comes back through the bridge as None and every later handle is dead. Four crashes, one of them in a Blackmagic Cloud library, share identical top stack frames in Fusion script-symbol teardown on the UI thread; a separate crash in the same session during DeleteProject/LoadProject had a different stack, so the signature belongs to the archive calls. Media-flag calls crashed 4 of 4, flags-off calls 0 of 5. A file already at the target survived every case byte for byte, including a crash, so the destination is never overwritten; unsaved work in the open project is what is lost. The native defaults turn source media on, so a default call crashes Resolve. Resolve logs nothing about the False returns.
831
838
  - **Workaround / current handling:** Keep isArchiveSrcMedia and isArchiveProxyMedia off unless you have verified the build, and save every open project first. Treat False as 'nothing archived', not as a path problem: a .dra and a folder-style path fail identically. Archive from the Project Manager UI when you need a real archive.
832
839
  - **Tags:** crash, unreliable-return, silent-failure, project, reported
840
+
841
+ ### TimelineItem.SetProperty (Inspector-keyframed property)
842
+
843
+ - **Object:** `TimelineItem`
844
+ - **Signature:** `(propertyKey, propertyValue) -> Bool`
845
+ - **Behavior:** On a clip that carries Edit-page (Inspector) keyframes for a transform property, as clips from an AAF import can, SetProperty('ZoomX', v) returns True and GetProperty reads v back, but the rendered frame never changes: the keyframes win at render time. Nothing in the return or the readback says so. On 19.x it cannot be detected or fixed from the API either: dir() of a live TimelineItem lists no keyframe methods (GetKeyframeCount, AddKeyframe and the rest are absent, and not in the scripting README), so the attribute is None and a call dies as "'NoneType' object is not callable".
846
+ - **Workaround / current handling:** Prove a transform with frames, never with the return or the readback. timeline_item probe_keyframe_override exports the frame twice (null control), nudges the property, exports again and restores it; verdict 'override' means keyframes are winning. Then remove them in the Inspector, or rebuild the item from the Media Pool (timeline replace_source_range re-appends it; a fresh append should carry no Inspector keyframes, which is not yet verified by render). The timeline_item keyframe actions return KEYFRAME_API_UNAVAILABLE on builds without the methods.
847
+ - **Tags:** silent-failure, unreliable-return, keyframes, transform, timeline, missing-method
848
+
849
+ ### Timeline.DeleteClips (locked track)
850
+
851
+ - **Object:** `Timeline`
852
+ - **Signature:** `([TimelineItem], ripple) -> bool`
853
+ - **Behavior:** On a locked track DeleteClips returns a bare False and deletes nothing, with nothing to say the lock is why. The lock does not stop SetProperty: transform and other property writes on items of the same locked track still succeed, so a property edit that worked is no evidence the track is unlocked. Separate from the Edit-page gate in 'Timeline.DeleteClips (requires the Edit page; flaky first attempt)'.
854
+ - **Workaround / current handling:** Read Timeline.GetIsTrackLocked for every track the targets sit on before deleting. timeline delete_clips refuses with TRACK_LOCKED naming the tracks; replace_source_range unlocks only with unlock_track=true and locks the track again afterwards; media_pool append_to_timeline names locked target tracks in its failure.
855
+ - **Tags:** silent-failure, timeline, edit, track-lock
856
+
857
+ ### Resolve.OpenPage (during playback)
858
+
859
+ - **Object:** `Resolve`
860
+ - **Signature:** `(pageName) -> Bool`
861
+ - **Behavior:** With playback engaged, a visit to the Color page followed by a page/playhead restore (resolve_control restore_state) timed out, and from then on EVERY scripting call hung, including a brand-new scriptapp() connection: Resolve's main thread was stuck in a viewer render loop until playback was dealt with in the GUI. The 19.x scripting API has no transport control (no Play, Stop or IsPlaying), so a script can neither stop playback first nor ask whether it is running.
862
+ - **Workaround / current handling:** Detect playback by reading Timeline.GetCurrentTimecode twice ~0.2 s apart; a playhead that moved means the transport is running. resolve_control open_page / restore_state, gallery_stills grab_and_export and the page-switching timeline_frame routes refuse with PLAYBACK_ACTIVE (allow_during_playback=true overrides). Stop playback in the GUI (Space or K) before retrying.
863
+ - **Tags:** hang, page-dependent, playback, missing-method
package/install.py CHANGED
@@ -37,7 +37,7 @@ from src.utils.update_check import (
37
37
 
38
38
  # ─── Version ──────────────────────────────────────────────────────────────────
39
39
 
40
- VERSION = "4.14.3"
40
+ VERSION = "4.15.0"
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": "4.14.3",
3
+ "version": "4.15.0",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -20,16 +20,16 @@
20
20
  "dependencies": {
21
21
  "@modelcontextprotocol/sdk": "^1.30.0",
22
22
  "adm-zip": "^0.6.0",
23
- "fast-xml-parser": "^5.11.1",
23
+ "fast-xml-parser": "^5.11.2",
24
24
  "fzstd": "^0.1.1",
25
- "jszip": "^3.10.1",
26
- "zod": "^4.5.4",
25
+ "jszip": "^3.10.2",
26
+ "zod": "^4.6.7",
27
27
  "zstd-codec": "^0.1.5"
28
28
  },
29
29
  "optionalDependencies": {
30
30
  "better-sqlite3": "^11.10.0",
31
31
  "js-yaml": "^4.3.2",
32
- "pg": "^8.23.0",
32
+ "pg": "^8.23.1",
33
33
  "sharp": "^0.35.4"
34
34
  },
35
35
  "files": [
@@ -23,16 +23,16 @@
23
23
  "dependencies": {
24
24
  "@modelcontextprotocol/sdk": "^1.30.0",
25
25
  "adm-zip": "^0.6.0",
26
- "fast-xml-parser": "^5.11.1",
26
+ "fast-xml-parser": "^5.11.2",
27
27
  "fzstd": "^0.1.1",
28
- "jszip": "^3.10.1",
29
- "zod": "^4.5.4",
28
+ "jszip": "^3.10.2",
29
+ "zod": "^4.6.7",
30
30
  "zstd-codec": "^0.1.5"
31
31
  },
32
32
  "optionalDependencies": {
33
33
  "better-sqlite3": "^11.10.0",
34
34
  "js-yaml": "^4.3.2",
35
- "pg": "^8.23.0",
35
+ "pg": "^8.23.1",
36
36
  "sharp": "^0.35.4"
37
37
  },
38
38
  "peerDependenciesMeta": {
@@ -47,6 +47,6 @@
47
47
  "node": ">=20.9"
48
48
  },
49
49
  "devDependencies": {
50
- "prettier": "^3.9.4"
50
+ "prettier": "^3.9.10"
51
51
  }
52
52
  }
package/scripts/doctor.py CHANGED
@@ -3,7 +3,10 @@
3
3
 
4
4
  Checks the checkout, Python interpreter, Resolve app and scripting-API paths,
5
5
  MCP client configs, and probes the DaVinciResolveScript bridge without
6
- mutating anything. Prints OK/WARN/FAIL lines (or JSON with --json) and exits
6
+ mutating anything. The Python-environment checks (bitness against the
7
+ fusionscript library, a Python too new for the Resolve build, several Pythons
8
+ on PATH) live in doctor_python_env.py. Prints OK/WARN/FAIL lines, each problem
9
+ with a `Fix:` line (or JSON with --json, where it is a `fix` key), and exits
7
10
  nonzero only when a FAIL is present.
8
11
 
9
12
  Run: `python3 scripts/doctor.py`
@@ -506,6 +509,32 @@ def extras_checks() -> list[dict[str, str]]:
506
509
  return results
507
510
 
508
511
 
512
+ def python_env_checks(probe: dict[str, Any]) -> list[dict[str, str]]:
513
+ """Bitness, version-for-this-Resolve, and which-Python checks (C56).
514
+
515
+ Kept in their own module because they are the ones worth testing against
516
+ fakes: every input is a probe result or a file header, never the machine.
517
+ """
518
+ try:
519
+ sys.path.insert(0, str(REPO / "scripts"))
520
+ import doctor_python_env # type: ignore
521
+ except Exception as exc:
522
+ return [{"status": "WARN", "name": "Python environment", "detail": f"checks unavailable: {exc!r}"}]
523
+ try:
524
+ return doctor_python_env.collect(
525
+ server_python=PYTHON,
526
+ lib_path=RESOLVE_LIB,
527
+ app_path=RESOLVE_APP,
528
+ repo=REPO,
529
+ probe=probe,
530
+ claude_config=CLAUDE_CONFIG,
531
+ codex_config=CODEX_CONFIG,
532
+ )
533
+ except Exception as exc:
534
+ # Never let a diagnostic tool be the thing that crashes.
535
+ return [{"status": "WARN", "name": "Python environment", "detail": f"checks failed: {exc!r}"}]
536
+
537
+
509
538
  def collect(
510
539
  resolve_host: str | None = None,
511
540
  resolve_timeout: float | None = None,
@@ -571,6 +600,8 @@ def collect(
571
600
  "(see 'Free-edition bridge' below).",
572
601
  )
573
602
 
603
+ results.extend(python_env_checks(probe))
604
+
574
605
  results.extend(bridge_checks(probe))
575
606
 
576
607
  results.extend(extras_checks())
@@ -601,6 +632,8 @@ def main() -> int:
601
632
  else:
602
633
  for item in results:
603
634
  print(f"[{item['status']}] {item['name']}: {item['detail']}")
635
+ if item.get("fix"):
636
+ print(f" Fix: {item['fix']}")
604
637
 
605
638
  return 1 if any(item["status"] == "FAIL" for item in results) else 0
606
639