davinci-resolve-mcp 4.7.4 → 4.7.6

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,41 @@
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.7.6 — `serverInfo.version` reports this project's version
6
+
7
+ ### Fixed
8
+
9
+ - **The MCP `initialize` handshake advertised the installed SDK's version (1.30.0)
10
+ as the server's, not the project's.** ([#243](https://github.com/samuelgursky/davinci-resolve-mcp/issues/243) reported with the diagnosis by @Eniot666; fixed in [#244](https://github.com/samuelgursky/davinci-resolve-mcp/pull/244) by @DYNOSuprovo)
11
+ `FastMCP.__init__` in the 1.x SDK has no `version` parameter, so it builds the
12
+ low-level `Server` without one and `create_initialization_options()` falls back to
13
+ `pkg_version("mcp")`. Every client displayed the SDK's number, so the version a user
14
+ quoted in a bug report was not this project's. Both FastMCP instances (compound and
15
+ granular) now set `_mcp_server.version = VERSION` right after construction, and the
16
+ test-mode stub carries the attribute. This reaches through a private SDK attribute
17
+ on purpose — the SDK exposes no public route — and
18
+ `tests/test_server_info_version.py` pins `create_initialization_options().server_version`
19
+ on both servers so a future SDK that adds the parameter can replace it cleanly.
20
+
21
+ ## What's New in v4.7.5 — the control panel serves on the `::1` loopback it accepts
22
+
23
+ ### Fixed
24
+
25
+ - **`open_control_panel(host="::1")` was accepted by both loopback guards and could
26
+ never start.** ([#242](https://github.com/samuelgursky/davinci-resolve-mcp/pull/242), @Dev-next-gen)
27
+ The panel's `ThreadingHTTPServer` is AF_INET, so binding `("::1", port)` raised
28
+ `socket.gaierror` and the tool reported "Control panel child exited (rc=1) before
29
+ serving" — while the panel's own `--host` refusal message listed `::1` as allowed.
30
+ Behind it, the launch URL, the pidfile URL and the `/api/boot` probe URL were all
31
+ written `http://::1:<port>/`, which neither a browser nor urllib parses.
32
+ `make_panel_server()` now uses an AF_INET6 subclass for an IPv6 literal, and the
33
+ three URLs bracket the host. The Host/Origin gate already accepted `[::1]`; IPv4
34
+ and `localhost` take the same paths as before. Guard test:
35
+ `tests/test_control_panel_ipv6_loopback.py` launches the real panel on `::1`
36
+ through `_open_control_panel`, GETs `/` over IPv6, parses the issued URL, and probes
37
+ `/api/boot` with the issued token; it skips on a host with no IPv6 loopback and ran
38
+ (did not skip) on the macOS landing machine.
39
+
5
40
  ## What's New in v4.7.4 — the networked transport can serve a client on another machine
6
41
 
7
42
  ### Fixed
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.7.4-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-4.7.6-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(389%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-4.7.4-blue.svg)](https://github.com/samuelgursky/davinci-resolve-mcp/releases)
5
+ [![Version](https://img.shields.io/badge/version-4.7.6-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(389%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.7.4 版 README。如与英文原版有出入,以 [英文原版](README.md) 为准。
15
+ > 本翻译对应 v4.7.6 版 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.7.4"
40
+ VERSION = "4.7.6"
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.7.4",
3
+ "version": "4.7.6",
4
4
  "description": "NPM bootstrapper for the DaVinci Resolve MCP Server.",
5
5
  "license": "MIT",
6
6
  "author": "Samuel Gursky <samgursky@gmail.com>",
@@ -10,6 +10,7 @@ import json
10
10
  import os
11
11
  import re
12
12
  import secrets
13
+ import socket
13
14
  import sqlite3
14
15
  import sys
15
16
  import threading
@@ -16289,6 +16290,23 @@ def _loopback_host(value: str) -> str:
16289
16290
  return value
16290
16291
 
16291
16292
 
16293
+ class _IPv6ThreadingHTTPServer(ThreadingHTTPServer):
16294
+ address_family = socket.AF_INET6
16295
+
16296
+
16297
+ def make_panel_server(host: str, port: int, handler: type) -> ThreadingHTTPServer:
16298
+ """Bind the panel. ThreadingHTTPServer is AF_INET, so `::1` needs the v6 class."""
16299
+ bind = host.strip("[]")
16300
+ server_cls = _IPv6ThreadingHTTPServer if ":" in bind else ThreadingHTTPServer
16301
+ return server_cls((bind, port), handler)
16302
+
16303
+
16304
+ def panel_url_host(host: str) -> str:
16305
+ """Host as it goes in a URL: an IPv6 literal must be bracketed."""
16306
+ bind = host.strip("[]")
16307
+ return f"[{bind}]" if ":" in bind else bind
16308
+
16309
+
16292
16310
  def parse_args() -> argparse.Namespace:
16293
16311
  parser = argparse.ArgumentParser(description="Run the local Resolve MCP control panel.")
16294
16312
  parser.add_argument("--host", default="127.0.0.1", type=_loopback_host)
@@ -16325,10 +16343,10 @@ def main() -> None:
16325
16343
  state = DashboardState(args.project_name, args.project_id, args.analysis_root)
16326
16344
  Handler.state = state
16327
16345
  Handler.token = resolve_panel_token()
16328
- server = ThreadingHTTPServer((args.host, args.port), Handler)
16346
+ server = make_panel_server(args.host, args.port, Handler)
16329
16347
  # The token rides in the URL fragment: browsers keep fragments client-side,
16330
16348
  # so it never appears in a request line, proxy log, or Referer.
16331
- url = f"http://{args.host}:{args.port}/#token={Handler.token}"
16349
+ url = f"http://{panel_url_host(args.host)}:{args.port}/#token={Handler.token}"
16332
16350
  # flush: under --no-open the URL (with its token) is the only handle the
16333
16351
  # operator gets, and a piped/redirected stdout would otherwise hold it back.
16334
16352
  print(f"DaVinci Resolve MCP: {url}", flush=True)
@@ -93,7 +93,7 @@ if not logging.getLogger().handlers:
93
93
  handlers=[logging.StreamHandler()],
94
94
  )
95
95
 
96
- VERSION = "4.7.4"
96
+ VERSION = "4.7.6"
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()}")
@@ -101,6 +101,8 @@ logger.info(f"Using Resolve API path: {RESOLVE_API_PATH}")
101
101
  logger.info(f"Using Resolve library path: {RESOLVE_LIB_PATH}")
102
102
 
103
103
  mcp = FastMCP("DaVinciResolveMCP")
104
+ if hasattr(mcp, "_mcp_server"):
105
+ mcp._mcp_server.version = VERSION
104
106
 
105
107
  READ_ONLY_TOOL = ToolAnnotations(
106
108
  readOnlyHint=True,
package/src/server.py CHANGED
@@ -11,7 +11,7 @@ Usage:
11
11
  python src/server.py --full # Start the 377-tool granular server instead
12
12
  """
13
13
 
14
- VERSION = "4.7.4"
14
+ VERSION = "4.7.6"
15
15
 
16
16
  import base64
17
17
  import os
@@ -263,6 +263,8 @@ mcp = FastMCP(
263
263
  "issue link for the user to review and submit; nothing is filed for them."
264
264
  ),
265
265
  )
266
+ if hasattr(mcp, "_mcp_server"):
267
+ mcp._mcp_server.version = VERSION
266
268
 
267
269
  READ_ONLY_TOOL = mcp_types.ToolAnnotations(
268
270
  readOnlyHint=True,
@@ -17491,6 +17493,11 @@ def _control_panel_read_token() -> Optional[str]:
17491
17493
  _CONTROL_PANEL_LOOPBACK_HOSTS = frozenset({"127.0.0.1", "localhost", "::1"})
17492
17494
 
17493
17495
 
17496
+ def _control_panel_url_host(host: str) -> str:
17497
+ """Host as it goes in a URL: `::1` must be written `[::1]`."""
17498
+ return f"[{host}]" if ":" in host else host
17499
+
17500
+
17494
17501
  def _control_panel_pid_alive(pid: int) -> bool:
17495
17502
  """Check whether a PID is alive on this OS without killing it."""
17496
17503
  if not pid or pid <= 0:
@@ -17578,7 +17585,7 @@ def _control_panel_probe(host: str, port: int, timeout: float = 1.5,
17578
17585
  """
17579
17586
  import urllib.error
17580
17587
  import urllib.request
17581
- url = f"http://{host}:{port}/api/boot"
17588
+ url = f"http://{_control_panel_url_host(host)}:{port}/api/boot"
17582
17589
  headers = {"Authorization": f"Bearer {token}"} if token else {}
17583
17590
  try:
17584
17591
  req = urllib.request.Request(url, headers=headers)
@@ -17838,7 +17845,7 @@ def _open_control_panel(p: Dict[str, Any]) -> Dict[str, Any]:
17838
17845
  # Write the pidfile so subsequent calls find it. The token travels in the
17839
17846
  # URL fragment — browsers never send fragments, so it stays out of every
17840
17847
  # request line and log.
17841
- url = f"http://{host}:{port}/#token={panel_token}"
17848
+ url = f"http://{_control_panel_url_host(host)}:{port}/#token={panel_token}"
17842
17849
  state = {
17843
17850
  "pid": proc.pid,
17844
17851
  "port": port,
@@ -51,7 +51,7 @@ def _real_mcp_is_importable() -> bool:
51
51
  def _build_stub_modules(*, stdio_note: str) -> Dict[str, types.ModuleType]:
52
52
  class FastMCP:
53
53
  def __init__(self, *args, **kwargs):
54
- pass
54
+ self._mcp_server = types.SimpleNamespace(version=None)
55
55
 
56
56
  def _decorator(self, *args, **kwargs):
57
57
  def decorate(func):