davinci-resolve-mcp 2.43.0 → 2.45.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 +67 -0
- package/README.md +3 -3
- package/docs/SKILL.md +56 -1
- package/docs/guides/control-panel.md +4 -1
- package/install.py +1 -1
- package/package.json +1 -1
- package/src/analysis_dashboard.py +49 -0
- package/src/granular/common.py +1 -1
- package/src/server.py +549 -1
- package/src/utils/analysis_store.py +20 -2
- package/src/utils/destructive_hook.py +5 -0
- package/src/utils/edit_engine.py +646 -0
- package/src/utils/entities.py +579 -0
- package/src/utils/timeline_brain_db.py +48 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,73 @@
|
|
|
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.45.0
|
|
6
|
+
|
|
7
|
+
The edit engine — Phase E, the final phase of the analysis + edit-engine
|
|
8
|
+
program. Three evidence-driven loops on one shared skeleton: evidence query
|
|
9
|
+
→ dry-run plan with per-decision rationale → confirm token → versioned
|
|
10
|
+
timeline ops → metric readback → brain_edits rows.
|
|
11
|
+
|
|
12
|
+
- **Added** a new `edit_engine` MCP tool (compound tool count: 34):
|
|
13
|
+
- `plan_selects` / `execute_selects` — ranks shots by deep-tier
|
|
14
|
+
`editorial.select_potential` and best moments (clip-level fallback for
|
|
15
|
+
standard-analyzed clips), story-spine order, duration budget; execution
|
|
16
|
+
builds a NEW selects timeline from per-shot source ranges. Additive.
|
|
17
|
+
- `plan_tighten` / `execute_tighten` — dead-air lifts from transcript-gap
|
|
18
|
+
evidence per timeline item (no transcript → reported in `skipped`,
|
|
19
|
+
never silently trimmed); execution assembles a tightened VARIANT
|
|
20
|
+
timeline from keep ranges (true partial trims via the range-copy
|
|
21
|
+
kernel) — the original timeline is never mutated.
|
|
22
|
+
- `plan_swap` / `execute_swap` — alternates for a timeline item via the
|
|
23
|
+
visual-similarity index, filtered to shots that can fill the slot
|
|
24
|
+
exactly; execution replaces the item in place (lift + positioned
|
|
25
|
+
append) on the version-archived timeline.
|
|
26
|
+
- `list_plans` / `get_plan` — plans persist under `memory/edit_plans/`
|
|
27
|
+
with content fingerprints; a stale or tampered plan refuses to execute.
|
|
28
|
+
- **Added** `src/utils/edit_engine.py` (DB-only planning/evidence layer) and
|
|
29
|
+
`tests/live_edit_engine_validation.py` (disposable-project live harness).
|
|
30
|
+
execute_* actions are confirm-token gated and registered with the
|
|
31
|
+
version-on-mutate hook (archive + brain_edits come from the same
|
|
32
|
+
machinery as every other destructive op).
|
|
33
|
+
- **Fixed** frame→shot mapping in the analysis store: frames now fall back
|
|
34
|
+
to time-containment when a report's shots don't record
|
|
35
|
+
`frame_indices_used` (commit paths that omit it previously produced no
|
|
36
|
+
shot-level visual vectors).
|
|
37
|
+
- **Validation**: full offline suite (1118 tests; 13 new). Live pilot on a
|
|
38
|
+
disposable synthetic-media Resolve project (ffmpeg + spoken audio):
|
|
39
|
+
20/20 checks — selects timeline assembled (2 decisions), tighten variant
|
|
40
|
+
removed exactly the 15.2s of transcript dead air while keeping the
|
|
41
|
+
spoken 4.8s (original untouched), swap replaced the item with a 0.92
|
|
42
|
+
cosine alternate, brain_edits rationale rows present for all three
|
|
43
|
+
loops, timeline versions archived.
|
|
44
|
+
|
|
45
|
+
## What's New in v2.44.0
|
|
46
|
+
|
|
47
|
+
Cross-clip entities + bin briefing v2 — Phase D of the analysis +
|
|
48
|
+
edit-engine program. Recurring people/places/props found by clustering the
|
|
49
|
+
visual embeddings, confirmed with one vision call per cluster.
|
|
50
|
+
|
|
51
|
+
- **Added** schema v11 (`entities` + `entity_appearances`) and
|
|
52
|
+
`src/utils/entities.py`: union-find clustering over the v10 CLIP frame
|
|
53
|
+
vectors (cosine threshold, no new deps), representative-frame selection,
|
|
54
|
+
ghost pruning across re-runs (labeled entities persist), and a
|
|
55
|
+
detection-state stash so `entity_index` always resolves against the exact
|
|
56
|
+
ordering the payload was issued with.
|
|
57
|
+
- **Added** `media_analysis` actions: `detect_entities` (clusters + deferred
|
|
58
|
+
one-frame-per-cluster confirmation payload, caps pre-checked),
|
|
59
|
+
`commit_entities` (kind/label/description with conservative-label rules;
|
|
60
|
+
`merge_with` collapses duplicate clusters), `list_entities`,
|
|
61
|
+
`prepare_bin_briefing` (entities + per-clip summaries, text-only), and
|
|
62
|
+
`commit_bin_summary` (host-synthesized briefing written above the v2.0
|
|
63
|
+
aggregate in `memory/bin_summary.md`).
|
|
64
|
+
- **Added** a "Recurring across this bin" card on the panel's Review page
|
|
65
|
+
(`/api/entities`), shown once labeled entities exist.
|
|
66
|
+
- **Validation**: full offline suite (1105 tests; 10 new). Live on the real
|
|
67
|
+
sample root: 3 clusters detected from 16 CLIP vectors; host-chat
|
|
68
|
+
confirmation labeled the shattered-windshield POV and the white rental
|
|
69
|
+
sedan and merged the sedan's two clusters; bin briefing synthesized and
|
|
70
|
+
committed; panel card verified and screenshots regenerated.
|
|
71
|
+
|
|
5
72
|
## What's New in v2.43.0
|
|
6
73
|
|
|
7
74
|
Embeddings + similarity search — Phase C of the analysis + edit-engine
|
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
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
|
-
[-blue.svg)](#server-modes)
|
|
7
7
|
[](docs/reference/api-coverage.md#test-results)
|
|
8
8
|
[](https://www.blackmagicdesign.com/products/davinciresolve)
|
|
9
9
|
[](https://www.python.org/downloads/)
|
|
@@ -49,7 +49,7 @@ The command starts a localhost server and opens the control panel in your browse
|
|
|
49
49
|
|
|
50
50
|
| Mode | Entry point | Tools | Best for |
|
|
51
51
|
|------|-------------|-------|----------|
|
|
52
|
-
| Compound | `src/server.py` |
|
|
52
|
+
| Compound | `src/server.py` | 34 | Default mode for most assistants. Related Resolve operations are grouped behind action parameters to keep context usage low. |
|
|
53
53
|
| Full / granular | `src/server.py --full` or `src/resolve_mcp_server.py` | 341 | Power users who want one MCP tool per Resolve API method. |
|
|
54
54
|
|
|
55
55
|
The compound server is recommended unless you specifically need the granular one-tool-per-method surface.
|
package/docs/SKILL.md
CHANGED
|
@@ -496,7 +496,27 @@ Key actions: `capabilities`, `install_guidance`, `resolve_output_root`, `plan`,
|
|
|
496
496
|
`set_panel_state`, `session_start_context`, `update_clip_field`,
|
|
497
497
|
`update_shot_field`, `get_field_history`, `revert_field`,
|
|
498
498
|
`list_corrections`, `deepen`, `commit_shot_vision`, `vision_pending_sweep`,
|
|
499
|
-
`build_embeddings`,
|
|
499
|
+
`build_embeddings`, `find_similar`, `detect_entities`, `commit_entities`,
|
|
500
|
+
`list_entities`, `prepare_bin_briefing`, and `commit_bin_summary`.
|
|
501
|
+
|
|
502
|
+
**Cross-clip entities + bin briefing v2 (v2.44.0+).** Recurring
|
|
503
|
+
people/places/props across a project's media, found cheaply and confirmed
|
|
504
|
+
with ONE vision call per cluster:
|
|
505
|
+
- `detect_entities(threshold?, min_cluster_size?)` clusters the v10 CLIP
|
|
506
|
+
frame vectors (build visual embeddings first), writes provisional entity
|
|
507
|
+
rows + appearances, and returns a deferred payload with one
|
|
508
|
+
representative frame per cluster (caps pre-checked, estimate inlined).
|
|
509
|
+
The host chat reads those frames and calls
|
|
510
|
+
`commit_entities(entities=[{entity_index, kind, label, description,
|
|
511
|
+
confidence, merge_with?}], vision_token)` — conservative labels only
|
|
512
|
+
(describe what's visible; never guess names). `merge_with` collapses
|
|
513
|
+
clusters that show the same entity.
|
|
514
|
+
- `list_entities` returns labeled entities with per-clip/shot appearances;
|
|
515
|
+
the panel's Review page shows a "Recurring across this bin" card.
|
|
516
|
+
- `prepare_bin_briefing` returns entities + per-clip summaries (text-only,
|
|
517
|
+
no vision cost); the host writes a colleague-style markdown briefing and
|
|
518
|
+
calls `commit_bin_summary(briefing, briefing_token)`, which lands in
|
|
519
|
+
`memory/bin_summary.md` above the v2.0 aggregate.
|
|
500
520
|
|
|
501
521
|
**Embeddings + similarity (v2.43.0+).** Local-compute semantic search; no
|
|
502
522
|
vendor tokens, so nothing here touches the caps ledger. Backends are
|
|
@@ -651,6 +671,41 @@ only when that mutation is intentional.
|
|
|
651
671
|
|
|
652
672
|
### Timelines
|
|
653
673
|
|
|
674
|
+
**`edit_engine`** — Evidence-driven edit loops (v2.45.0+): selects assembly,
|
|
675
|
+
tighten, swap.
|
|
676
|
+
|
|
677
|
+
Every loop is plan → confirm → execute. plan_* actions are dry-run by
|
|
678
|
+
construction: they query the DB-canonical analysis store and return a
|
|
679
|
+
per-decision rationale plus a stored `plan_id` (plans persist under
|
|
680
|
+
`memory/edit_plans/` with a content fingerprint, so a stale plan cannot run
|
|
681
|
+
against a changed project). execute_* actions require a `confirm_token`, run
|
|
682
|
+
under the version-on-mutate hook, and return before/after duration and
|
|
683
|
+
clip-count readback plus `brain_edits` rationale rows.
|
|
684
|
+
|
|
685
|
+
- `plan_selects(min_select_potential?, max_duration_seconds?, max_shots?,
|
|
686
|
+
timeline_name?, analysis_root?)` — ranks shots by deep-tier
|
|
687
|
+
`editorial.select_potential` / best moments (clip-level fallback for
|
|
688
|
+
standard-analyzed clips), story-spine order.
|
|
689
|
+
`execute_selects(plan_id)` creates a NEW selects timeline from the plan's
|
|
690
|
+
per-shot source ranges — additive; nothing existing is touched.
|
|
691
|
+
- `plan_tighten(timeline_name?, target_ratio?, min_pause_seconds?,
|
|
692
|
+
handle_seconds?)` — dead-air lifts from transcript-gap evidence for each
|
|
693
|
+
timeline item (items without transcripts are reported in `skipped`, never
|
|
694
|
+
silently trimmed). `execute_tighten(plan_id)` assembles a tightened
|
|
695
|
+
VARIANT timeline from the plan's keep ranges — true partial trims; the
|
|
696
|
+
original timeline is never mutated.
|
|
697
|
+
- `plan_swap(timeline_start_frame | item_name, kind="visual"|"text",
|
|
698
|
+
limit?)` — alternates for one timeline item via the similarity index,
|
|
699
|
+
filtered to shots long enough to fill the slot exactly.
|
|
700
|
+
`execute_swap(plan_id, alternate_index)` replaces the item in place
|
|
701
|
+
(lift + positioned append at the same record frame) on the
|
|
702
|
+
version-archived timeline.
|
|
703
|
+
- `list_plans(limit?)` / `get_plan(plan_id)`.
|
|
704
|
+
|
|
705
|
+
The engine needs the analysis substrate: analyzed clips in the DB (run
|
|
706
|
+
`db_ingest` on older roots), transcripts for tighten, and visual embeddings
|
|
707
|
+
(`build_embeddings(kinds=['visual'])`) for swap.
|
|
708
|
+
|
|
654
709
|
**`timeline_versioning`** — Version-on-mutate, archive, rollback, brain-edit history (C6).
|
|
655
710
|
|
|
656
711
|
Every destructive timeline op (compound, captions, ripple delete, gap close,
|
|
@@ -82,7 +82,10 @@ transcripts once the search index is built. When a local text-embedding
|
|
|
82
82
|
backend is detected (ollama with `nomic-embed-text`, or
|
|
83
83
|
sentence-transformers), a `Semantic` toggle appears next to the search box —
|
|
84
84
|
it searches by meaning instead of exact words, ranking clips, shots, and
|
|
85
|
-
transcript lines by similarity to your query.
|
|
85
|
+
transcript lines by similarity to your query. Once cross-clip entity
|
|
86
|
+
detection has run (`detect_entities` + a one-frame-per-cluster confirmation
|
|
87
|
+
in chat), a `Recurring across this bin` card lists the labeled people,
|
|
88
|
+
places, and objects with their shot counts.
|
|
86
89
|
|
|
87
90
|
### Clip detail
|
|
88
91
|
|
package/install.py
CHANGED
|
@@ -35,7 +35,7 @@ from src.utils.update_check import (
|
|
|
35
35
|
|
|
36
36
|
# ─── Version ──────────────────────────────────────────────────────────────────
|
|
37
37
|
|
|
38
|
-
VERSION = "2.
|
|
38
|
+
VERSION = "2.45.0"
|
|
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
|
@@ -2114,6 +2114,30 @@ HTML = r"""<!doctype html>
|
|
|
2114
2114
|
margin-bottom: var(--space-3);
|
|
2115
2115
|
flex-wrap: wrap;
|
|
2116
2116
|
}
|
|
2117
|
+
.review-semantic-toggle {
|
|
2118
|
+
display: inline-flex;
|
|
2119
|
+
gap: 6px;
|
|
2120
|
+
align-items: center;
|
|
2121
|
+
color: var(--text-secondary);
|
|
2122
|
+
font-size: var(--ops-text-label);
|
|
2123
|
+
white-space: nowrap;
|
|
2124
|
+
}
|
|
2125
|
+
.review-entities {
|
|
2126
|
+
margin-bottom: var(--space-3);
|
|
2127
|
+
padding: var(--space-2) var(--space-3);
|
|
2128
|
+
border: 1px solid var(--border-default);
|
|
2129
|
+
border-radius: var(--radius-md);
|
|
2130
|
+
}
|
|
2131
|
+
.review-entities-title {
|
|
2132
|
+
color: var(--text-secondary);
|
|
2133
|
+
font-size: var(--ops-text-label);
|
|
2134
|
+
margin-bottom: 6px;
|
|
2135
|
+
}
|
|
2136
|
+
.review-entities-chips {
|
|
2137
|
+
display: flex;
|
|
2138
|
+
gap: var(--space-2);
|
|
2139
|
+
flex-wrap: wrap;
|
|
2140
|
+
}
|
|
2117
2141
|
.review-bin-filters input[type="search"] {
|
|
2118
2142
|
flex: 1 1 280px;
|
|
2119
2143
|
min-width: 200px;
|
|
@@ -4077,6 +4101,7 @@ HTML = r"""<!doctype html>
|
|
|
4077
4101
|
</div>
|
|
4078
4102
|
</div>
|
|
4079
4103
|
<div id="reviewBinSummary" class="review-bin-summary">Loading analyzed clips…</div>
|
|
4104
|
+
<div id="reviewEntitiesCard" class="review-entities" style="display:none"></div>
|
|
4080
4105
|
<div id="reviewBinGrid" class="review-grid"></div>
|
|
4081
4106
|
<div id="reviewSearchResults" class="review-search-results" style="display:none"></div>
|
|
4082
4107
|
</div>
|
|
@@ -8093,6 +8118,22 @@ HTML = r"""<!doctype html>
|
|
|
8093
8118
|
renderReviewBin();
|
|
8094
8119
|
// Coverage runs in parallel — failures here must not block the clip grid.
|
|
8095
8120
|
refreshReadinessCard().catch(() => {});
|
|
8121
|
+
refreshEntitiesCard().catch(() => {});
|
|
8122
|
+
}
|
|
8123
|
+
|
|
8124
|
+
// Recurring people/places/props detected across the bin (Phase D).
|
|
8125
|
+
// Hidden until at least one labeled entity exists.
|
|
8126
|
+
async function refreshEntitiesCard() {
|
|
8127
|
+
const card = $('reviewEntitiesCard');
|
|
8128
|
+
if (!card) return;
|
|
8129
|
+
const data = await api('/api/entities').catch(() => null);
|
|
8130
|
+
const labeled = (data?.entities || []).filter(e => e.label);
|
|
8131
|
+
if (!labeled.length) { card.style.display = 'none'; return; }
|
|
8132
|
+
card.style.display = '';
|
|
8133
|
+
card.innerHTML = `<div class="review-entities-title">Recurring across this bin</div>
|
|
8134
|
+
<div class="review-entities-chips">${labeled.map(e =>
|
|
8135
|
+
`<span class="review-chip" title="${escapeHtml(e.description || '')}">${escapeHtml(e.label)} · ${e.kind || 'unknown'} · ${e.shot_count || e.cluster_size || 0} shots</span>`
|
|
8136
|
+
).join('')}</div>`;
|
|
8096
8137
|
}
|
|
8097
8138
|
|
|
8098
8139
|
async function refreshReadinessCard() {
|
|
@@ -14386,6 +14427,14 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
14386
14427
|
payload["results"] = _v2_enrich_search_results(self.state.project_root, payload["results"])
|
|
14387
14428
|
self._json(payload)
|
|
14388
14429
|
return
|
|
14430
|
+
if path == "/api/entities":
|
|
14431
|
+
try:
|
|
14432
|
+
from src.utils import entities as _entities
|
|
14433
|
+
|
|
14434
|
+
self._json(_entities.list_entities(self.state.project_root))
|
|
14435
|
+
except Exception as exc: # noqa: BLE001 — panel reads fail soft
|
|
14436
|
+
self._json({"success": False, "error": f"{type(exc).__name__}: {exc}"})
|
|
14437
|
+
return
|
|
14389
14438
|
if path == "/api/search/semantic":
|
|
14390
14439
|
q = (query.get("q") or [""])[0]
|
|
14391
14440
|
try:
|
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.
|
|
83
|
+
VERSION = "2.45.0"
|
|
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()}")
|