davinci-resolve-mcp 2.54.1 → 2.54.3
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 +38 -0
- package/README.md +1 -1
- package/install.py +126 -6
- package/package.json +1 -1
- package/src/granular/common.py +1 -1
- package/src/server.py +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,44 @@
|
|
|
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.54.3
|
|
6
|
+
|
|
7
|
+
Follow-up to the v2.54.2 config-merge fix (#71): the JSONC sanitizer's
|
|
8
|
+
trailing-comma step was not string-aware.
|
|
9
|
+
|
|
10
|
+
- **Fixed** `_strip_jsonc` removed trailing commas with a regex applied to the
|
|
11
|
+
whole document, so a comma inside a string value followed by whitespace and a
|
|
12
|
+
closing brace/bracket — e.g. `"greeting": "hello, } world"` — had the comma
|
|
13
|
+
silently stripped *from inside the string* when merging a commented (JSONC)
|
|
14
|
+
client config. The trailing-comma pass is now string-aware (mirroring the
|
|
15
|
+
comment stripper), so string contents are never altered while real trailing
|
|
16
|
+
commas are still removed. Added regression tests covering string values that
|
|
17
|
+
contain `, }` / `, ]`. (Dropped the now-unused `re` import.)
|
|
18
|
+
|
|
19
|
+
## What's New in v2.54.2
|
|
20
|
+
|
|
21
|
+
A destructive-overwrite bug in the installer (issue #71): the MCP client setup
|
|
22
|
+
step could wipe a user's entire editor settings file instead of merging into it.
|
|
23
|
+
|
|
24
|
+
- **Fixed** `install.py` silently destroyed existing client config files whose
|
|
25
|
+
contents weren't strict JSON. `read_json` swallowed `JSONDecodeError` and
|
|
26
|
+
returned `{}`, so the subsequent "merge" wrote a file containing *only* the
|
|
27
|
+
`davinci-resolve` server entry — wiping themes, terminal env vars, LSP
|
|
28
|
+
settings, keybindings, everything else. Zed was the reported victim because
|
|
29
|
+
its `settings.json` ships with `//` comments (JSONC), but the same latent
|
|
30
|
+
risk existed for **every** supported client — VS Code and Continue also accept
|
|
31
|
+
JSONC. The fix is centralized in the single read/merge path so all clients are
|
|
32
|
+
covered:
|
|
33
|
+
- `read_json` now best-effort strips JSONC `//` and `/* */` comments and
|
|
34
|
+
trailing commas (string-aware, so comment markers inside string values are
|
|
35
|
+
preserved), letting commented configs merge cleanly instead of being lost.
|
|
36
|
+
- When a config file exists but still can't be parsed after that, the
|
|
37
|
+
installer **refuses to overwrite it** and tells the user to add the entry
|
|
38
|
+
manually — rather than silently replacing their settings.
|
|
39
|
+
- **Added** regression tests covering JSONC merge, plain-JSON merge,
|
|
40
|
+
refuse-to-overwrite on unparseable files, fresh-file creation, and
|
|
41
|
+
string-aware comment stripping.
|
|
42
|
+
|
|
5
43
|
## What's New in v2.54.1
|
|
6
44
|
|
|
7
45
|
One more instance of the enum-keyed silent-failure class (issue #70), plus a
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
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
6
|
[-blue.svg)](#server-modes)
|
package/install.py
CHANGED
|
@@ -35,7 +35,7 @@ from src.utils.update_check import (
|
|
|
35
35
|
|
|
36
36
|
# ─── Version ──────────────────────────────────────────────────────────────────
|
|
37
37
|
|
|
38
|
-
VERSION = "2.54.
|
|
38
|
+
VERSION = "2.54.3"
|
|
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
|
|
@@ -458,14 +458,124 @@ def build_zed_entry(python_path, server_path, api_path, lib_path, system=SYSTEM,
|
|
|
458
458
|
|
|
459
459
|
# ─── Config File Operations ──────────────────────────────────────────────────
|
|
460
460
|
|
|
461
|
+
class ConfigParseError(Exception):
|
|
462
|
+
"""Existing config file has content but could not be parsed.
|
|
463
|
+
|
|
464
|
+
Callers must NOT overwrite such a file -- doing so destroys the user's
|
|
465
|
+
settings (issue #71).
|
|
466
|
+
"""
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
def _strip_jsonc(text):
|
|
470
|
+
"""Best-effort strip of // and /* */ comments and trailing commas.
|
|
471
|
+
|
|
472
|
+
Zed's ``settings.json`` (and several other clients) accept JSON-with-comments.
|
|
473
|
+
Python's ``json`` module rejects those, so we strip them before parsing.
|
|
474
|
+
The walk is string-aware, so a ``//`` or ``/*`` sequence inside a JSON
|
|
475
|
+
string value is preserved.
|
|
476
|
+
"""
|
|
477
|
+
out = []
|
|
478
|
+
i = 0
|
|
479
|
+
n = len(text)
|
|
480
|
+
in_string = False
|
|
481
|
+
escape = False
|
|
482
|
+
while i < n:
|
|
483
|
+
ch = text[i]
|
|
484
|
+
if in_string:
|
|
485
|
+
out.append(ch)
|
|
486
|
+
if escape:
|
|
487
|
+
escape = False
|
|
488
|
+
elif ch == "\\":
|
|
489
|
+
escape = True
|
|
490
|
+
elif ch == '"':
|
|
491
|
+
in_string = False
|
|
492
|
+
i += 1
|
|
493
|
+
continue
|
|
494
|
+
if ch == '"':
|
|
495
|
+
in_string = True
|
|
496
|
+
out.append(ch)
|
|
497
|
+
i += 1
|
|
498
|
+
continue
|
|
499
|
+
if ch == "/" and i + 1 < n and text[i + 1] == "/":
|
|
500
|
+
i += 2
|
|
501
|
+
while i < n and text[i] not in "\r\n":
|
|
502
|
+
i += 1
|
|
503
|
+
continue
|
|
504
|
+
if ch == "/" and i + 1 < n and text[i + 1] == "*":
|
|
505
|
+
i += 2
|
|
506
|
+
while i + 1 < n and not (text[i] == "*" and text[i + 1] == "/"):
|
|
507
|
+
i += 1
|
|
508
|
+
i += 2
|
|
509
|
+
continue
|
|
510
|
+
out.append(ch)
|
|
511
|
+
i += 1
|
|
512
|
+
stripped = "".join(out)
|
|
513
|
+
# Drop trailing commas before a closing brace/bracket — string-aware, so a
|
|
514
|
+
# comma inside a string value (e.g. "a, }") is never touched. A prior regex
|
|
515
|
+
# pass here was NOT string-aware and silently corrupted such values during a
|
|
516
|
+
# JSONC merge (follow-up to the issue #71 fix).
|
|
517
|
+
result = []
|
|
518
|
+
i = 0
|
|
519
|
+
n = len(stripped)
|
|
520
|
+
in_string = False
|
|
521
|
+
escape = False
|
|
522
|
+
while i < n:
|
|
523
|
+
ch = stripped[i]
|
|
524
|
+
if in_string:
|
|
525
|
+
result.append(ch)
|
|
526
|
+
if escape:
|
|
527
|
+
escape = False
|
|
528
|
+
elif ch == "\\":
|
|
529
|
+
escape = True
|
|
530
|
+
elif ch == '"':
|
|
531
|
+
in_string = False
|
|
532
|
+
i += 1
|
|
533
|
+
continue
|
|
534
|
+
if ch == '"':
|
|
535
|
+
in_string = True
|
|
536
|
+
result.append(ch)
|
|
537
|
+
i += 1
|
|
538
|
+
continue
|
|
539
|
+
if ch == ",":
|
|
540
|
+
j = i + 1
|
|
541
|
+
while j < n and stripped[j] in " \t\r\n":
|
|
542
|
+
j += 1
|
|
543
|
+
if j < n and stripped[j] in "}]":
|
|
544
|
+
i += 1 # trailing comma: drop it
|
|
545
|
+
continue
|
|
546
|
+
result.append(ch)
|
|
547
|
+
i += 1
|
|
548
|
+
return "".join(result)
|
|
549
|
+
|
|
550
|
+
|
|
461
551
|
def read_json(path):
|
|
462
|
-
"""Read a JSON file
|
|
552
|
+
"""Read a JSON/JSONC config file.
|
|
553
|
+
|
|
554
|
+
Returns an empty dict when the file is absent or empty (safe to create).
|
|
555
|
+
Raises :class:`ConfigParseError` when the file has content that cannot be
|
|
556
|
+
parsed even after stripping JSONC comments and trailing commas -- callers
|
|
557
|
+
must refuse to overwrite such a file, or they would wipe the user's
|
|
558
|
+
existing settings (issue #71: Zed's settings.json ships with comments).
|
|
559
|
+
"""
|
|
463
560
|
try:
|
|
464
561
|
with open(path, "r") as f:
|
|
465
|
-
|
|
466
|
-
except
|
|
562
|
+
raw = f.read()
|
|
563
|
+
except FileNotFoundError:
|
|
467
564
|
return {}
|
|
468
565
|
|
|
566
|
+
if not raw.strip():
|
|
567
|
+
return {}
|
|
568
|
+
|
|
569
|
+
try:
|
|
570
|
+
return json.loads(raw)
|
|
571
|
+
except json.JSONDecodeError:
|
|
572
|
+
pass
|
|
573
|
+
|
|
574
|
+
try:
|
|
575
|
+
return json.loads(_strip_jsonc(raw))
|
|
576
|
+
except json.JSONDecodeError as exc:
|
|
577
|
+
raise ConfigParseError(str(exc)) from exc
|
|
578
|
+
|
|
469
579
|
|
|
470
580
|
def write_json(path, data):
|
|
471
581
|
"""Write JSON to file, creating parent directories."""
|
|
@@ -501,8 +611,18 @@ def write_client_config(client, python_path, server_path, api_path, lib_path, dr
|
|
|
501
611
|
preview = {config_key: {"davinci-resolve": server_entry}}
|
|
502
612
|
return True, f"Would write to {config_path}:\n{json.dumps(preview, indent=2)}"
|
|
503
613
|
|
|
504
|
-
# Read existing config and merge
|
|
505
|
-
|
|
614
|
+
# Read existing config and merge. If the file exists but cannot be parsed,
|
|
615
|
+
# refuse to overwrite it -- silently replacing it would wipe the user's
|
|
616
|
+
# settings (issue #71, especially Zed's commented settings.json).
|
|
617
|
+
try:
|
|
618
|
+
existing = read_json(config_path)
|
|
619
|
+
except ConfigParseError as exc:
|
|
620
|
+
return False, (
|
|
621
|
+
f"{config_path} exists but could not be parsed ({exc}). "
|
|
622
|
+
f"Refusing to overwrite to avoid data loss. Add the "
|
|
623
|
+
f'"{config_key}" entry manually using the manual config output '
|
|
624
|
+
f"(run with --manual)."
|
|
625
|
+
)
|
|
506
626
|
|
|
507
627
|
if config_key not in existing:
|
|
508
628
|
existing[config_key] = {}
|
package/package.json
CHANGED
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.54.
|
|
83
|
+
VERSION = "2.54.3"
|
|
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()}")
|