premiere-pro-mcp 1.4.0 → 1.6.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
@@ -6,6 +6,54 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.6.0] - 2026-07-31
10
+
11
+ ### Added
12
+
13
+ - Added a capability-aware UXP foundation for revisioned project inspection, verified saves,
14
+ preset-based sequence creation, OTIO/FCP XML interchange, transcript-language discovery,
15
+ Object Mask detection, and Adobe Media Encoder controls on compatible Premiere hosts.
16
+ - Added explicit UXP operation outcomes and bounded operation-ID replay protection so a client retry
17
+ does not repeat a completed command within the same panel session.
18
+
19
+ ### Changed
20
+
21
+ - Documented the 10 UXP MCP tools that become available when an authenticated local panel is
22
+ connected, including their host-version and live-verification boundaries.
23
+ - Updated the MCP SDK and Node type dependencies and GitHub Actions artifact actions.
24
+
25
+ ### Fixed
26
+
27
+ - `create_project` now rejects directory paths and verifies that Premiere switched to the exact
28
+ requested `.prproj` path before reporting success, preventing edits from continuing in a
29
+ previously open project after a failed creation attempt.
30
+ - Claude Desktop bundle packaging now invokes npm through the active Node executable so the
31
+ release build works on Windows where `npm` is exposed as a command shim.
32
+
33
+ ## [1.5.0] - 2026-07-30
34
+
35
+ ### Added
36
+
37
+ - Added `detect_silence` for finding dead air in local source media with FFmpeg, including
38
+ Docker support and clear local-install guidance.
39
+ - Added anonymous, opt-out PostHog usage telemetry with prompt flushing for low-volume servers.
40
+ - Added an immersive editorial landing-page experience, product demo video, changelog page, and
41
+ a 30-day launch plan.
42
+
43
+ ### Changed
44
+
45
+ - Expanded the MCP surface to 279 tools and limited advertised tools to those allowed by the
46
+ active capability profile.
47
+ - Documented capability-filtered discovery, remote media-path constraints, and the difference
48
+ between the 279 registered tools and the 277 tools available to the default profile.
49
+
50
+ ### Fixed
51
+
52
+ - Structural timeline tools now verify razor, ripple-delete, transition, and track-targeting
53
+ mutations instead of reporting success when Premiere applied only part or none of an edit.
54
+ - Server metadata now reports the package version rather than a stale hard-coded value.
55
+ - Resolved CodeQL findings in HTTP authentication and filesystem-path handling.
56
+
9
57
  ## [1.4.0] - 2026-07-26
10
58
 
11
59
  ### Added
package/README.md CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  # Premiere Pro MCP Server
4
4
 
5
+ [![MCP Toplist](https://mcptoplist.com/badge/glama%2Fleancoderkavy%2Fpremiere-pro-mcp.svg)](https://mcptoplist.com/server/glama%2Fleancoderkavy%2Fpremiere-pro-mcp)
6
+
5
7
  **Give AI full control over Adobe Premiere Pro.**
6
8
 
7
- 278 tools across 31 modules, 3 resources, and 4 guided workflows.
9
+ 279 core tools across 31 modules, 3 resources, and 4 guided workflows. A connected UXP host adds 10 capability-gated tools.
8
10
 
9
11
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
10
12
  [![Node.js](https://img.shields.io/badge/Node.js-20.19%2B-green.svg)](https://nodejs.org)
@@ -25,18 +27,14 @@ An [MCP (Model Context Protocol)](https://modelcontextprotocol.io) server that l
25
27
  "Add the B-roll clips to V2, apply a cross dissolve between each, color correct them to match the A-roll, and export a 1080p ProRes."
26
28
  ```
27
29
 
28
- The AI handles the entire workflow through 278 tools spanning the supported ExtendScript, QE DOM, and safe edit-planning surfaces.
30
+ The AI handles the entire workflow through 279 core tools spanning the supported ExtendScript, QE DOM, local media analysis, and safe edit-planning surfaces. A compatible, authenticated UXP panel adds 10 documented, capability-gated workflows without replacing the production CEP bridge.
29
31
 
30
- ### What's new in 1.3.1
32
+ ### What's new in 1.6.0
31
33
 
32
- - Windows npm releases now include a verified, signed CEP ZXP and install it automatically, covering
33
- hosts that reject the raw development bundle even when `PlayerDebugMode` is enabled.
34
- - `set_sequence_frame_rate` now converts frames per second to Premiere ticks per frame and verifies
35
- the applied setting instead of assigning a numeric frame period.
36
- - `premiere-pro-mcp --diagnose-cep` checks the installed manifest, debug-key types, and recent
37
- Premiere signature failures.
38
- - TypeScript 7, Vitest 4, Zod 4, MCP SDK 1.29, Next.js 16.2, and patched transitive dependencies
39
- modernize the toolchain and remove production audit findings.
34
+ - **Capability-aware UXP foundation:** compatible Premiere 25.6-26.3 hosts can expose revisioned project inspection, verified saves, preset sequence creation, OTIO/FCP XML interchange, transcript-language discovery, Object Mask detection, and Adobe Media Encoder controls.
35
+ - **Retry-safe UXP mutations:** optional operation IDs prevent a client retry from repeating a completed command in the same panel session, while each command reports `verified` or `committed_unverified` evidence honestly.
36
+ - **Safe project creation:** `create_project` rejects directory paths and verifies Premiere opened the requested `.prproj` before reporting success.
37
+ - **Current distribution metadata:** the CEP/UXP panels, Codex and Claude plugins, landing page, and npm package now all identify v1.6.0.
40
38
 
41
39
  ### Added in 1.2.0
42
40
 
@@ -49,6 +47,14 @@ The AI handles the entire workflow through 278 tools spanning the supported Exte
49
47
 
50
48
  ## Quick Start
51
49
 
50
+ ### Before you begin
51
+
52
+ - Node.js **20.19 or newer** on Windows or macOS.
53
+ - Adobe Premiere Pro **2020–2026**. Keep Premiere, the CEP bridge, and your MCP client on the same computer for the recommended local setup.
54
+ - Optional: [ffmpeg](https://ffmpeg.org/download.html) on `PATH` for `detect_silence`
55
+ (`brew install ffmpeg` on macOS or `winget install Gyan.FFmpeg` on Windows).
56
+ The production Docker image already includes it.
57
+
52
58
  ### 1. Install
53
59
 
54
60
  **Option A — npm (recommended):**
@@ -138,6 +144,20 @@ done
138
144
 
139
145
  ### 3. Configure your MCP client
140
146
 
147
+ If you installed from npm, configure the client to run the global command:
148
+
149
+ ```json
150
+ {
151
+ "mcpServers": {
152
+ "premiere-pro": {
153
+ "command": "premiere-pro-mcp"
154
+ }
155
+ }
156
+ }
157
+ ```
158
+
159
+ If you cloned the repository instead, use the source-build configuration shown below for your client.
160
+
141
161
  <details>
142
162
  <summary><strong>Claude Desktop</strong></summary>
143
163
 
@@ -213,7 +233,8 @@ Add to your VS Code MCP server configuration:
213
233
  1. Open (or restart) Premiere Pro
214
234
  2. The bridge starts automatically using the default temp directory (or its previously saved setting)
215
235
  3. Optionally go to **Window > Extensions > MCP Bridge** to confirm the green "Running" status or change the **Temp Directory** to match your MCP client config
216
- 4. Ask your AI assistant: *"What's my current Premiere Pro project?"*
236
+ 4. Ask your AI assistant to run `get_capabilities`, then `ping`, with Premiere open.
237
+ 5. For a safe first request, ask: *"What is my current Premiere Pro project and active sequence? Do not make changes."*
217
238
 
218
239
  The default bridge directory is derived from the operating system on both sides, so most local setups should not set `PREMIERE_TEMP_DIR`. If you override it, use the same absolute path in the MCP server and CEP panel; Windows and macOS paths are not interchangeable.
219
240
 
@@ -227,11 +248,11 @@ From a clone of this repository:
227
248
  ```bash
228
249
  codex plugin marketplace add .
229
250
  codex plugin add premiere-pro@premiere-pro-mcp
230
- npx -y premiere-pro-mcp@1.3.1 --install-cep
251
+ npx -y premiere-pro-mcp@1.6.0 --install-cep
231
252
  ```
232
253
 
233
254
  Restart Premiere Pro and start a new Codex session after installation. The plugin
234
- launches `premiere-pro-mcp@1.3.1` through `npx`; the separate CEP installation is
255
+ launches `premiere-pro-mcp@1.6.0` through `npx`; the separate CEP installation is
235
256
  required because the MCP server communicates with the running Premiere host through
236
257
  the local bridge.
237
258
 
@@ -251,7 +272,7 @@ For Claude Code, add this repository as a marketplace and install the plugin:
251
272
  Then install the Premiere bridge and start a new Claude Code session:
252
273
 
253
274
  ```bash
254
- npx -y premiere-pro-mcp@1.3.1 --install-cep
275
+ npx -y premiere-pro-mcp@1.6.0 --install-cep
255
276
  ```
256
277
 
257
278
  The Claude Code package lives in
@@ -276,9 +297,9 @@ installed separately.
276
297
  | CEP production bridge | Premiere Pro 2020–2026 | Premiere Pro 2020–2026 | Run `get_capabilities`, then `ping` with Premiere open |
277
298
  | UXP preview bridge | Premiere Pro 25.6+ | Premiere Pro 25.6+ | Live loopback WebSocket and host API verification required |
278
299
  | npm CEP installer | Copies plugin and verifies `REG_SZ` debug keys | Copies plugin and verifies the installed manifest/debug settings | Restart Premiere after installation |
279
- | CI build and unit tests | Node 18 and 22 | Node 18 and 22 | GitHub-hosted OS runners; no Adobe host is available in CI |
300
+ | CI build and unit tests | Node 20, 22, and 24 | Node 20, 22, and 24 | GitHub-hosted OS runners; no Adobe host is available in CI |
280
301
 
281
- `get_capabilities` reports the current operating system, temp directory, CEP/UXP coverage, enabled authority profile, and any live-host verification still required. It also includes a `tools` catalog generated from the tools actually registered by the server. Every entry identifies:
302
+ `get_capabilities` reports the current operating system, temp directory, CEP/UXP coverage, enabled authority profile, and any live-host verification still required. It also includes the full `tools` catalog generated from the tools registered by the server, including tools disabled by the active profile. Every entry identifies:
282
303
 
283
304
  - the execution backend (`local`, CEP/ExtendScript, QE, or orchestrator);
284
305
  - static support status (`supported`, `limited`, `experimental`, or `unsupported`);
@@ -289,6 +310,18 @@ installed separately.
289
310
 
290
311
  QE-backed tools are reported as `experimental` because QE is undocumented and can vary between Premiere builds. Authority availability is reported separately from implementation support, so disabling `edit`, for example, does not incorrectly label editing tools as unsupported. Static metadata never claims that a Premiere operation succeeded; use `ping` and inspect each tool result for runtime evidence.
291
312
 
313
+ MCP `tools/list` is filtered to the active authority profile. The default
314
+ `inspect,edit,export,filesystem` profile advertises 277 of the 279 registered
315
+ tools and omits `execute_extendscript` and `evaluate_expression`, which require
316
+ explicit `unsafe-script` authority. `ping` and `get_capabilities` remain visible
317
+ under every profile so a restricted or misconfigured server can still explain
318
+ its state. The call-time capability guard remains authoritative even if listing
319
+ metadata is wrong.
320
+
321
+ The MCP handshake reads `serverInfo.version` from the installed `package.json`,
322
+ so clients receive the package version that is actually running rather than a
323
+ separately maintained literal.
324
+
292
325
  Tools with mixed execution boundaries can provide explicit operational metadata at registration. This is used for local file verification, static feature-support reports, and hybrid local-plus-Premiere validation so the capability catalog does not infer a host dependency from naming alone.
293
326
 
294
327
  ### Collaboration and AI feature boundaries
@@ -329,7 +362,14 @@ PREMIERE_UXP_TOKEN="replace-with-a-long-random-secret" premiere-pro-mcp
329
362
 
330
363
  Enter the same token in the UXP panel. The listener binds only to `127.0.0.1:7777`, authenticates the WebSocket upgrade, requires a versioned capability handshake, correlates concurrent requests, and fails pending work on timeout or disconnect. Set `PREMIERE_UXP_PORT` to use another loopback port.
331
364
 
332
- When enabled, MCP discovery includes `get_uxp_capabilities`, `get_uxp_state`, and `export_frame_uxp`. A failed UXP command is never silently retried through CEP because the first operation may have partially succeeded.
365
+ When enabled, MCP discovery includes `get_uxp_capabilities`, `get_uxp_state`, `inspect_project_uxp`, `save_project_uxp`, `create_sequence_with_preset_uxp`, `export_interchange_uxp`, `get_transcript_languages_uxp`, `detect_object_masks_uxp`, `configure_encoder_uxp`, and `export_frame_uxp`. Commands are advertised only while the authenticated local UXP bridge is connected; the host capability handshake remains the authority for support in the running Premiere build. A failed UXP command is never silently retried through CEP because the first operation may have partially succeeded.
366
+
367
+ Premiere 26.2-26.3 hosts also expose documented UXP workflows for revisioned project
368
+ inspection, verified project saves, preset-based sequence creation, OTIO/FCP XML
369
+ interchange, transcript-language discovery, Object Mask detection, and Adobe Media
370
+ Encoder control. Mutations accept optional idempotency keys and return explicit
371
+ verification outcomes. See [the UXP capability foundation](docs/uxp-capability-foundation.md)
372
+ for the command matrix and live-host validation boundary.
333
373
 
334
374
  ---
335
375
 
@@ -373,7 +413,7 @@ The file-based IPC bridge is simple, reliable, and works across macOS and Window
373
413
 
374
414
  ---
375
415
 
376
- ## Tools (278)
416
+ ## Tools (279 core total; 277 under the default profile; 287 with a connected UXP bridge)
377
417
 
378
418
  ### Discovery & Inspection (10 + 10)
379
419
 
@@ -507,13 +547,12 @@ than presenting UI-only operations as available tools.
507
547
  | `get_workspaces` / `set_workspace` | Switch workspace layouts |
508
548
  | `create_caption_track` | Create caption/subtitle tracks |
509
549
 
510
- ### Scripting (6)
550
+ ### Scripting (2)
511
551
 
512
552
  | Tool | Description |
513
553
  | :--- | :----------- |
514
- | `execute_extendscript` | Run arbitrary ExtendScript (ES3) |
515
- | `evaluate_expression` | Quick one-line eval |
516
- | `send_raw_script` | Bypass security validation (advanced) |
554
+ | `execute_extendscript` | Run arbitrary ExtendScript (ES3); requires explicit `unsafe-script` authority |
555
+ | `evaluate_expression` | Evaluate a one-line expression; requires explicit `unsafe-script` authority |
517
556
 
518
557
  ### ...and 100+ more
519
558
 
@@ -581,6 +620,7 @@ Then connect with:
581
620
  ```
582
621
 
583
622
  > **Note:** The file bridge still requires the CEP plugin to share the same `PREMIERE_TEMP_DIR`. For cloud deployments this means running a sync agent or using `fly proxy` / WireGuard to reach your local machine.
623
+ > `detect_silence` can analyze only media paths available inside the server filesystem; a desktop-only path is not automatically available to a remote Fly machine.
584
624
 
585
625
  ---
586
626
 
@@ -596,6 +636,16 @@ Then connect with:
596
636
  | `PORT` | HTTP port (HTTP/SSE transport only) | `3000` |
597
637
  | `MCP_AUTH_TOKEN` | Bearer token required by the HTTP transport | unset |
598
638
  | `ALLOW_UNAUTHENTICATED` | Set to `1` to run HTTP without auth (unsafe; throwaway instances only) | unset |
639
+ | `POSTHOG_API_KEY` | PostHog project token; enables privacy-safe MCP usage telemetry | unset |
640
+ | `POSTHOG_HOST` | PostHog ingestion host | `https://us.i.posthog.com` |
641
+ | `POSTHOG_ENVIRONMENT` | Environment property attached to telemetry events | `production` |
642
+ | `POSTHOG_DISTINCT_ID` | Optional stable anonymous server identifier | Fly machine ID or random boot ID |
643
+
644
+ When PostHog is enabled, the server records `mcp_connection_attempt`,
645
+ `mcp_request`, and `mcp_tool_call`. Events contain operational fields such as
646
+ method, tool name, outcome, status code, and duration. Authentication tokens,
647
+ IP addresses, MCP arguments, project paths, media names, and tool results are
648
+ never sent. Person profiles are disabled for these events.
599
649
 
600
650
  ---
601
651
 
@@ -606,7 +656,7 @@ premiere-pro-mcp/
606
656
  ├── src/
607
657
  │ ├── index.ts # Entry point — stdio transport setup
608
658
  │ ├── http-server.ts # Entry point — HTTP/SSE transport (Fly.io / remote)
609
- │ ├── server.ts # MCP server — registers 278 tools + 3 resources + 4 prompts
659
+ │ ├── server.ts # MCP server — registers 279 tools, filtered by authority profile
610
660
  │ ├── bridge/
611
661
  │ │ ├── file-bridge.ts # File-based IPC (write .jsx, poll .json)
612
662
  │ │ └── script-builder.ts # ExtendScript generator with ES3 helpers
@@ -619,7 +669,7 @@ premiere-pro-mcp/
619
669
  │ │ ├── timeline.ts # Timeline clip operations
620
670
  │ │ ├── effects.ts # Effect application and color correction
621
671
  │ │ ├── transitions.ts # Transition management (QE DOM)
622
- │ │ ├── audio.ts # Audio levels and keyframes
672
+ │ │ ├── audio.ts # Audio levels, keyframes, and ffmpeg silence analysis
623
673
  │ │ ├── av-settings.ts # Documented AV inspection, mapping, and capability boundaries
624
674
  │ │ ├── text.ts # Text overlays and MOGRTs
625
675
  │ │ ├── markers.ts # Sequence and clip markers
@@ -675,8 +725,9 @@ All generated scripts use **ES3 syntax** (`var`, manual `for` loops, no arrow fu
675
725
  ### Security
676
726
 
677
727
  Understand the trust model before deploying this: **any client that can reach the MCP
678
- server can control Premiere Pro.** `execute_extendscript` and `send_raw_script` are
679
- arbitrary-code-execution tools by design and are disabled by default. Enable them only by setting
728
+ server can control Premiere Pro.** `execute_extendscript` and `evaluate_expression` are
729
+ arbitrary-code-execution tools by design and are omitted from discovery and denied at call time
730
+ by default. Enable them only by setting
680
731
  `PREMIERE_MCP_CAPABILITIES=inspect,edit,export,filesystem,unsafe-script`.
681
732
 
682
733
  - **Run it locally over stdio** unless you have a specific reason not to. That's the safe default.
Binary file
@@ -1,8 +1,8 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <ExtensionManifest Version="7.0" ExtensionBundleId="com.mcp.premiere.bridge" ExtensionBundleVersion="1.4.0" ExtensionBundleName="MCP Bridge">
2
+ <ExtensionManifest Version="7.0" ExtensionBundleId="com.mcp.premiere.bridge" ExtensionBundleVersion="1.6.0" ExtensionBundleName="MCP Bridge">
3
3
  <ExtensionList>
4
- <Extension Id="com.mcp.premiere.bridge.panel" Version="1.4.0"/>
5
- <Extension Id="com.mcp.premiere.bridge.headless" Version="1.4.0"/>
4
+ <Extension Id="com.mcp.premiere.bridge.panel" Version="1.6.0"/>
5
+ <Extension Id="com.mcp.premiere.bridge.headless" Version="1.6.0"/>
6
6
  </ExtensionList>
7
7
  <ExecutionEnvironment>
8
8
  <HostList>
@@ -65,7 +65,7 @@
65
65
  <section class="update-section" aria-live="polite">
66
66
  <div class="update-copy">
67
67
  <span class="section-label">Connector updates</span>
68
- <strong id="updateTitle">Version 1.4.0</strong>
68
+ <strong id="updateTitle">Version 1.6.0</strong>
69
69
  <span id="updateDetail">Checking for updates…</span>
70
70
  </div>
71
71
  <button id="btnUpdate" class="button button-update" onclick="handleUpdateClick()" type="button" disabled>
@@ -7,7 +7,7 @@
7
7
  })(this, function () {
8
8
  "use strict";
9
9
 
10
- var CURRENT_VERSION = "1.4.0";
10
+ var CURRENT_VERSION = "1.6.0";
11
11
  var LATEST_RELEASE_API =
12
12
  "https://api.github.com/repos/leancoderkavy/premiere-pro-mcp/releases/latest";
13
13
  var RELEASES_URL =
@@ -28,6 +28,7 @@ import { fileURLToPath } from "node:url";
28
28
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
29
29
  import { createServer } from "./server.js";
30
30
  import { cleanupTempDir, getTempDir } from "./bridge/file-bridge.js";
31
+ import { getTelemetry } from "./telemetry.js";
31
32
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
32
33
  const LANDING_DIR = path.resolve(__dirname, "../landing-dist");
33
34
  const MIME = {
@@ -36,6 +37,7 @@ const MIME = {
36
37
  ".css": "text/css; charset=utf-8",
37
38
  ".json": "application/json",
38
39
  ".png": "image/png",
40
+ ".mp4": "video/mp4",
39
41
  ".svg": "image/svg+xml",
40
42
  ".ico": "image/x-icon",
41
43
  ".woff2": "font/woff2",
@@ -96,6 +98,8 @@ const bridgeOptions = {
96
98
  ? parseInt(process.env.PREMIERE_TIMEOUT_MS, 10)
97
99
  : undefined,
98
100
  };
101
+ process.env.PREMIERE_MCP_TRANSPORT = "http";
102
+ const telemetry = getTelemetry();
99
103
  const tempDir = getTempDir(bridgeOptions);
100
104
  console.error(`[premiere-pro-mcp] Starting HTTP server on port ${PORT}...`);
101
105
  console.error(`[premiere-pro-mcp] Temp directory: ${tempDir}`);
@@ -118,11 +122,20 @@ const httpServer = http.createServer(async (req, res) => {
118
122
  }
119
123
  // Bearer token auth (constant-time; required unless ALLOW_UNAUTHENTICATED=1)
120
124
  if (!isAuthorized(req)) {
125
+ telemetry.capture("mcp_connection_attempt", {
126
+ outcome: "unauthorized",
127
+ method: req.method ?? "unknown",
128
+ });
121
129
  res.writeHead(401, { "Content-Type": "application/json" });
122
130
  res.end(JSON.stringify({ error: "Unauthorized" }));
123
131
  return;
124
132
  }
125
- const mcpServer = createServer(bridgeOptions);
133
+ const requestStartedAt = Date.now();
134
+ telemetry.capture("mcp_connection_attempt", {
135
+ outcome: "authorized",
136
+ method: req.method ?? "unknown",
137
+ });
138
+ const mcpServer = createServer(bridgeOptions, { telemetry });
126
139
  const transport = new StreamableHTTPServerTransport({
127
140
  sessionIdGenerator: undefined, // stateless
128
141
  });
@@ -133,8 +146,21 @@ const httpServer = http.createServer(async (req, res) => {
133
146
  try {
134
147
  await mcpServer.connect(transport);
135
148
  await transport.handleRequest(req, res);
149
+ telemetry.capture("mcp_request", {
150
+ outcome: res.statusCode >= 400 ? "failed" : "succeeded",
151
+ method: req.method ?? "unknown",
152
+ status_code: res.statusCode,
153
+ duration_ms: Date.now() - requestStartedAt,
154
+ });
136
155
  }
137
156
  catch (err) {
157
+ telemetry.capture("mcp_request", {
158
+ outcome: "failed",
159
+ method: req.method ?? "unknown",
160
+ status_code: 500,
161
+ duration_ms: Date.now() - requestStartedAt,
162
+ error_type: err instanceof Error ? err.name : "UnknownError",
163
+ });
138
164
  console.error("[premiere-pro-mcp] Request error:", err);
139
165
  if (!res.headersSent) {
140
166
  res.writeHead(500, { "Content-Type": "application/json" });
@@ -152,12 +178,12 @@ httpServer.listen(PORT, "0.0.0.0", () => {
152
178
  console.error(`[premiere-pro-mcp] Auth: DISABLED via ALLOW_UNAUTHENTICATED — anyone can drive this server`);
153
179
  }
154
180
  });
155
- process.on("SIGTERM", () => {
156
- console.error("[premiere-pro-mcp] SIGTERM received, shutting down...");
157
- httpServer.close(() => process.exit(0));
158
- });
159
- process.on("SIGINT", () => {
160
- console.error("[premiere-pro-mcp] SIGINT received, shutting down...");
161
- httpServer.close(() => process.exit(0));
162
- });
181
+ async function shutdown(signal) {
182
+ console.error(`[premiere-pro-mcp] ${signal} received, shutting down...`);
183
+ httpServer.close();
184
+ await telemetry.shutdown();
185
+ process.exit(0);
186
+ }
187
+ process.on("SIGTERM", () => void shutdown("SIGTERM"));
188
+ process.on("SIGINT", () => void shutdown("SIGINT"));
163
189
  //# sourceMappingURL=http-server.js.map
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
3
  import { createServer } from "./server.js";
4
4
  import { cleanupTempDir, getTempDir } from "./bridge/file-bridge.js";
5
+ import { getTelemetry } from "./telemetry.js";
5
6
  import { execFileSync } from "child_process";
6
7
  import { fileURLToPath } from "url";
7
8
  import path from "path";
@@ -19,7 +20,7 @@ function debugLog(message) {
19
20
  const args = process.argv.slice(2);
20
21
  if (args.includes("--help") || args.includes("-h")) {
21
22
  console.log(`
22
- premiere-pro-mcp — MCP server for Adobe Premiere Pro (278 tools)
23
+ premiere-pro-mcp — MCP server for Adobe Premiere Pro (279 tools)
23
24
 
24
25
  Usage:
25
26
  premiere-pro-mcp Start the MCP server (stdio transport)
@@ -79,6 +80,8 @@ if (args.includes("--install-cep") || args.includes("--diagnose-cep")) {
79
80
  process.exit(0);
80
81
  }
81
82
  async function main() {
83
+ process.env.PREMIERE_MCP_TRANSPORT = "stdio";
84
+ const telemetry = getTelemetry();
82
85
  const bridgeOptions = {
83
86
  tempDir: process.env.PREMIERE_TEMP_DIR,
84
87
  timeoutMs: process.env.PREMIERE_TIMEOUT_MS
@@ -102,7 +105,7 @@ async function main() {
102
105
  const address = uxpBridge.address();
103
106
  debugLog(`UXP bridge listening on ws://${address.host}:${address.port}${address.path}`);
104
107
  }
105
- const server = createServer(bridgeOptions, { uxpBridge });
108
+ const server = createServer(bridgeOptions, { uxpBridge, telemetry });
106
109
  const transport = new StdioServerTransport();
107
110
  await server.connect(transport);
108
111
  debugLog("Server connected and ready");
@@ -110,6 +113,7 @@ async function main() {
110
113
  if (uxpBridge)
111
114
  await uxpBridge.stop();
112
115
  await server.close();
116
+ await telemetry.shutdown();
113
117
  };
114
118
  process.once("SIGINT", () => void shutdown().finally(() => process.exit(0)));
115
119
  process.once("SIGTERM", () => void shutdown().finally(() => process.exit(0)));
@@ -19,5 +19,20 @@ export declare function requireCapability(config: CapabilityConfig, capability:
19
19
  export declare const UNSAFE_TOOL_NAMES: Set<string>;
20
20
  /** A conservative classification for centralized server registration. */
21
21
  export declare function capabilityForTool(toolName: string): Capability;
22
+ /**
23
+ * Tools that stay advertised under every profile, because they are how an
24
+ * operator diagnoses a profile that is too narrow: get_capabilities reports the
25
+ * active authority, and ping proves the bridge is alive. Withholding them makes
26
+ * a misconfigured server look broken with no supported way to ask why.
27
+ */
28
+ export declare const ALWAYS_LISTED_TOOL_NAMES: Set<string>;
29
+ /**
30
+ * Whether a tool should appear in tools/list under the given profile.
31
+ *
32
+ * This is an advertising decision, not an enforcement one — guardToolHandler
33
+ * remains the authority check at call time. Keeping the two separate means a
34
+ * listing bug can never widen what a profile is actually allowed to do.
35
+ */
36
+ export declare function isToolPermitted(toolName: string, config: CapabilityConfig): boolean;
22
37
  export declare function guardToolHandler<TArgs, TResult>(toolName: string, handler: (args: TArgs) => Promise<TResult>, config?: CapabilityConfig, createOperationId?: () => string): (args: TArgs) => Promise<TResult>;
23
38
  //# sourceMappingURL=capabilities.d.ts.map
@@ -39,7 +39,10 @@ export function requireCapability(config, capability, operationId) {
39
39
  }
40
40
  export const UNSAFE_TOOL_NAMES = new Set(["execute_extendscript", "send_raw_script", "evaluate_expression"]);
41
41
  const INSPECT_TOOL_NAMES = new Set(["ping", "get_capabilities", "preview_edit_plan"]);
42
- const FILESYSTEM_TOOL_NAMES = new Set(["apply_lut", "set_scratch_disk_path", "verify_delivery_file"]);
42
+ // detect_silence reads a media file from disk and shells out to ffmpeg. It
43
+ // changes nothing in Premiere, so classifying it as "edit" would overstate what
44
+ // it does; filesystem is the authority it actually needs.
45
+ const FILESYSTEM_TOOL_NAMES = new Set(["apply_lut", "set_scratch_disk_path", "verify_delivery_file", "detect_silence"]);
43
46
  /** A conservative classification for centralized server registration. */
44
47
  export function capabilityForTool(toolName) {
45
48
  if (UNSAFE_TOOL_NAMES.has(toolName))
@@ -56,6 +59,25 @@ export function capabilityForTool(toolName) {
56
59
  // keeps new tools fail-closed instead of silently bypassing the authority profile.
57
60
  return "edit";
58
61
  }
62
+ /**
63
+ * Tools that stay advertised under every profile, because they are how an
64
+ * operator diagnoses a profile that is too narrow: get_capabilities reports the
65
+ * active authority, and ping proves the bridge is alive. Withholding them makes
66
+ * a misconfigured server look broken with no supported way to ask why.
67
+ */
68
+ export const ALWAYS_LISTED_TOOL_NAMES = new Set(["ping", "get_capabilities"]);
69
+ /**
70
+ * Whether a tool should appear in tools/list under the given profile.
71
+ *
72
+ * This is an advertising decision, not an enforcement one — guardToolHandler
73
+ * remains the authority check at call time. Keeping the two separate means a
74
+ * listing bug can never widen what a profile is actually allowed to do.
75
+ */
76
+ export function isToolPermitted(toolName, config) {
77
+ if (ALWAYS_LISTED_TOOL_NAMES.has(toolName))
78
+ return true;
79
+ return config.capabilities.has(capabilityForTool(toolName));
80
+ }
59
81
  export function guardToolHandler(toolName, handler, config = resolveCapabilities(), createOperationId = randomUUID) {
60
82
  const required = capabilityForTool(toolName);
61
83
  return async (args) => {
package/dist/server.d.ts CHANGED
@@ -1,8 +1,16 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { BridgeOptions } from "./bridge/file-bridge.js";
3
3
  import type { UxpWebSocketBridge } from "./bridge/uxp-websocket-bridge.js";
4
+ import { type Telemetry } from "./telemetry.js";
5
+ /**
6
+ * Read the version from package.json rather than hardcoding it. A literal here
7
+ * silently drifts from the published package every release, which makes the
8
+ * version reported over MCP useless for triaging bug reports.
9
+ */
10
+ export declare const SERVER_VERSION: string;
4
11
  export interface ServerOptions {
5
12
  uxpBridge?: UxpWebSocketBridge;
13
+ telemetry?: Telemetry;
6
14
  }
7
15
  export declare function createServer(bridgeOptions: BridgeOptions, serverOptions?: ServerOptions): McpServer;
8
16
  //# sourceMappingURL=server.d.ts.map
package/dist/server.js CHANGED
@@ -31,11 +31,31 @@ import { getEditPlanTools } from "./tools/edit-plans.js";
31
31
  import { getAvSettingsTools } from "./tools/av-settings.js";
32
32
  import { getRecoveryTools } from "./tools/recovery.js";
33
33
  import { getUxpTools } from "./tools/uxp.js";
34
- import { guardToolHandler, resolveCapabilities } from "./security/index.js";
34
+ import { guardToolHandler, isToolPermitted, resolveCapabilities, } from "./security/index.js";
35
35
  import { EXTENDSCRIPT_REFERENCE } from "./resources/extendscript-reference.js";
36
36
  import { WORKFLOW_PROMPTS, WORKFLOW_RESOURCE } from "./workflows/catalog.js";
37
37
  import { annotationsForTool, structuredToolResult, } from "./workflows/tool-metadata.js";
38
+ import { getTelemetry } from "./telemetry.js";
38
39
  import { z } from "zod";
40
+ import { readFileSync } from "node:fs";
41
+ import { fileURLToPath } from "node:url";
42
+ import path from "node:path";
43
+ /**
44
+ * Read the version from package.json rather than hardcoding it. A literal here
45
+ * silently drifts from the published package every release, which makes the
46
+ * version reported over MCP useless for triaging bug reports.
47
+ */
48
+ export const SERVER_VERSION = (() => {
49
+ try {
50
+ const here = path.dirname(fileURLToPath(import.meta.url));
51
+ const raw = readFileSync(path.resolve(here, "../package.json"), "utf8");
52
+ const version = JSON.parse(raw).version;
53
+ return typeof version === "string" && version ? version : "unknown";
54
+ }
55
+ catch {
56
+ return "unknown";
57
+ }
58
+ })();
39
59
  const PREMIERE_INSTRUCTIONS = `You are controlling Adobe Premiere Pro through MCP tools. Follow these best practices:
40
60
 
41
61
  WORKFLOW ORDER:
@@ -222,18 +242,34 @@ function collectTools(bridgeOptions, capabilities, uxpBridge) {
222
242
  export function createServer(bridgeOptions, serverOptions = {}) {
223
243
  const server = new McpServer({
224
244
  name: "premiere-pro-mcp",
225
- version: "1.2.0",
245
+ version: SERVER_VERSION,
226
246
  });
227
247
  const capabilities = resolveCapabilities();
248
+ const telemetry = serverOptions.telemetry ?? getTelemetry();
228
249
  // Collect all tools from each module
229
250
  const toolModules = collectTools(bridgeOptions, capabilities, serverOptions.uxpBridge);
230
251
  // Register each tool with the MCP server
252
+ let withheld = 0;
231
253
  for (const [name, tool] of Object.entries(toolModules)) {
254
+ // Don't advertise tools the active authority profile will always refuse.
255
+ // guardToolHandler still rejects them at call time, but listing an
256
+ // unusable tool spends client context and invites the model to attempt a
257
+ // call that cannot succeed.
258
+ if (!isToolPermitted(name, capabilities)) {
259
+ withheld++;
260
+ continue;
261
+ }
232
262
  const zodShape = jsonSchemaToZodShape(tool.parameters);
233
263
  const guardedHandler = guardToolHandler(name, tool.handler, capabilities);
234
264
  server.tool(name, tool.description, zodShape, annotationsForTool(name), async (args) => {
265
+ const startedAt = Date.now();
235
266
  try {
236
267
  const result = await guardedHandler(args);
268
+ telemetry.capture("mcp_tool_call", {
269
+ tool: name,
270
+ outcome: result.success ? "succeeded" : "failed",
271
+ duration_ms: Date.now() - startedAt,
272
+ });
237
273
  if (result.success) {
238
274
  // Special handling for capture_frame: return image content block
239
275
  const data = result.data;
@@ -281,6 +317,12 @@ export function createServer(bridgeOptions, serverOptions = {}) {
281
317
  }
282
318
  }
283
319
  catch (err) {
320
+ telemetry.capture("mcp_tool_call", {
321
+ tool: name,
322
+ outcome: "failed",
323
+ duration_ms: Date.now() - startedAt,
324
+ error_type: err instanceof Error ? err.name : "UnknownError",
325
+ });
284
326
  return {
285
327
  structuredContent: structuredToolResult(name, false, undefined, err instanceof Error ? err.message : String(err)),
286
328
  content: [
@@ -294,6 +336,11 @@ export function createServer(bridgeOptions, serverOptions = {}) {
294
336
  }
295
337
  });
296
338
  }
339
+ if (withheld > 0) {
340
+ debugLog(`Withheld ${withheld} tool(s) from tools/list — not permitted by the ` +
341
+ `active capability profile (${[...capabilities.capabilities].sort().join(", ")}). ` +
342
+ `Call get_capabilities to see the enabled authority.`);
343
+ }
297
344
  // Register LLM instructions resource
298
345
  server.resource("premiere-instructions", "config://premiere-instructions", {
299
346
  description: "Instructions and best practices for using Premiere Pro via MCP tools",
@@ -0,0 +1,8 @@
1
+ export type TelemetryProperties = Record<string, string | number | boolean | null | undefined>;
2
+ export interface Telemetry {
3
+ readonly enabled: boolean;
4
+ capture(event: string, properties?: TelemetryProperties): void;
5
+ shutdown(): Promise<void>;
6
+ }
7
+ export declare function getTelemetry(): Telemetry;
8
+ //# sourceMappingURL=telemetry.d.ts.map