premiere-pro-mcp 1.18.1 → 1.18.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 +34 -0
- package/README.md +18 -16
- package/after-effects-cep-plugin/CSXS/manifest.xml +2 -2
- package/artifacts/MCPBridgeCEP.zxp +0 -0
- package/cep-plugin/CSXS/manifest.xml +3 -3
- package/cep-plugin/index.html +1 -1
- package/cep-plugin/updater.cjs +1 -1
- package/dist/bridge/script-builder.js +8 -0
- package/dist/bridge/uxp-websocket-bridge.js +11 -0
- package/dist/platform-capabilities.js +2 -0
- package/dist/resources/adobe-api-inventory.json +4 -1
- package/dist/resources/adobe-uxp-coverage.json +25 -0
- package/dist/security/capabilities.js +4 -0
- package/dist/security/path-guard.d.ts +10 -0
- package/dist/security/path-guard.js +106 -0
- package/dist/tools/export.js +6 -1
- package/dist/tools/playhead.js +19 -6
- package/dist/tools/scripting.d.ts +4 -0
- package/dist/tools/scripting.js +30 -8
- package/dist/tools/uxp.d.ts +85 -0
- package/dist/tools/uxp.js +47 -0
- package/docs/adobe-beta-work-area-drift.md +6 -2
- package/docs/supported-actions.md +4 -3
- package/package.json +11 -5
- package/public-product-manifest.json +3 -3
- package/scripts/validate-adobe-marketplace-branding.mjs +5 -1
- package/uxp-plugin/commands.cjs +88 -0
- package/uxp-plugin/manifest.json +1 -1
- package/uxp-plugin/next-workflows.cjs +16 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,40 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.18.3] - 2026-09-28
|
|
10
|
+
|
|
11
|
+
### Security
|
|
12
|
+
|
|
13
|
+
- UXP path arguments are now checked in the MCP server process before a command is sent. Any absolute path that goes through a symbolic link or directory junction is refused with `UXP_PATH_SYMLINK_REFUSED`. Premiere 26.5 UXP reports a link's own path and cannot `lstat`, so the panel's workspace check could be bypassed through a link inside the approved folder. (#640)
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- `get_work_area` now reads work-area points as seconds, as live Premiere 25.2 and 26.5.1 hosts return them. It had divided them by ticks-per-second and reported values near zero. `set_work_area` writes seconds and reads the result back; it fails honestly when a build ignores the write. (#642)
|
|
18
|
+
- `export_sequence` now fails when Premiere rejects the render or writes no file. (#647)
|
|
19
|
+
- UXP source-media timing reads Premiere 26.5's documented synchronous `Media.getStart()` / `getDuration()` before the deprecated Promise-returning `start` / `duration` properties, so `manage_source_media_timing_uxp` can set a start time on 26.5 hosts instead of reporting the command unavailable.
|
|
20
|
+
|
|
21
|
+
### Verification scope
|
|
22
|
+
|
|
23
|
+
- Automated checks validate package behavior. The UXP media timing change and the server-side symlink guard have not been verified on a licensed Premiere host.
|
|
24
|
+
|
|
25
|
+
## [1.18.2] - 2026-09-26
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- Documented Premiere 26.5 WorkAreaUtils coverage with a capability-gated UXP tool. (#637)
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Prevented inspect tools from evaluating unescaped ExtendScript. (#639)
|
|
34
|
+
|
|
35
|
+
### Documentation
|
|
36
|
+
|
|
37
|
+
- Clarified the independent Adobe Premiere Pro MCP search presentation on GitHub and the project site. (#643)
|
|
38
|
+
|
|
39
|
+
### Verification scope
|
|
40
|
+
|
|
41
|
+
- Automated checks validate package behavior. The new UXP operation has not been verified on a licensed Premiere host in this release.
|
|
42
|
+
|
|
9
43
|
## [1.18.1] - 2026-09-23
|
|
10
44
|
|
|
11
45
|
### Fixed
|
package/README.md
CHANGED
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
|
|
7
7
|
[](https://mcptoplist.com/server/glama%2Fleancoderkavy%2Fpremiere-pro-mcp)
|
|
8
8
|
|
|
9
|
-
**
|
|
9
|
+
**Adobe Premiere Pro MCP server for reviewable, local-first workflows with compatible AI assistants.**
|
|
10
|
+
|
|
11
|
+
Premiere Pro automation for Claude, Cursor, Codex, and other MCP clients: AI-assisted editing, project organization, and export checks through Adobe's documented UXP and CEP/ExtendScript APIs.
|
|
10
12
|
|
|
11
13
|
Free, MIT licensed, local-first, and published to npm as [`premiere-pro-mcp`](https://www.npmjs.com/package/premiere-pro-mcp) — the only package name that installs this project.
|
|
12
14
|
|
|
13
15
|
[Website](https://premiere-pro-mcp.com/) · [Recorded demo](https://premiere-pro-mcp.com/demo/) · [Compare servers](https://premiere-pro-mcp.com/compare/) · [Setup guides](https://premiere-pro-mcp.com/blog/how-to-set-up-premiere-pro-mcp/) · [Search tools](https://premiere-pro-mcp.com/tools/) · [Troubleshooting](https://premiere-pro-mcp.com/docs/troubleshooting/) · [Release facts](https://premiere-pro-mcp.com/facts/)
|
|
14
16
|
|
|
15
|
-
Development source: 384 core tools across 57 modules, 4 resources, and 19 guided workflows. A connected UXP host adds
|
|
17
|
+
Development source: 384 core tools across 57 modules, 4 resources, and 19 guided workflows. A connected UXP host adds 96 capability-gated tools.
|
|
16
18
|
|
|
17
19
|
The [completed AE render handoff](docs/after-effects-render-handoff.md) previews and confirms importing one finished render into an existing Premiere bin, with host and file rechecks and an import receipt.
|
|
18
20
|
|
|
@@ -104,14 +106,14 @@ and repository before configuring a client. The new
|
|
|
104
106
|
VS Code, or Codex settings that point directly to this installation. It is a
|
|
105
107
|
feature included in v1.15.1 and later.
|
|
106
108
|
|
|
107
|
-
The current source exposes 384 core tools for supported workflow steps spanning the supported ExtendScript, QE DOM, local media and interchange analysis, revisioned project-context retrieval, safe edit-planning, project-intake preview, review handoff, connection verification, and guarded After Effects MOGRT authoring, batch, library, render-queue, inspection, and Premiere-handoff workflows. A compatible, authenticated UXP panel adds
|
|
109
|
+
The current source exposes 384 core tools for supported workflow steps spanning the supported ExtendScript, QE DOM, local media and interchange analysis, revisioned project-context retrieval, safe edit-planning, project-intake preview, review handoff, connection verification, and guarded After Effects MOGRT authoring, batch, library, render-queue, inspection, and Premiere-handoff workflows. A compatible, authenticated UXP panel adds 96 documented, capability-gated tools without replacing the production CEP bridge.
|
|
108
110
|
|
|
109
111
|
<a id="latest-release"></a>
|
|
110
112
|
|
|
111
|
-
### Latest release: 1.18.
|
|
113
|
+
### Latest release: 1.18.2
|
|
112
114
|
|
|
113
|
-
The published v1.18.
|
|
114
|
-
and
|
|
115
|
+
The published v1.18.2 npm artifact contains **384 core tools**, 382 in its default profile,
|
|
116
|
+
and 478 with a compatible UXP connection. The development catalog above can include
|
|
115
117
|
unreleased work. See the [versioned facts and package provenance](https://premiere-pro-mcp.com/facts/).
|
|
116
118
|
|
|
117
119
|
### Try a bounded workflow
|
|
@@ -160,7 +162,7 @@ if the connection is unavailable.
|
|
|
160
162
|
local Premiere processes. See the generated [supported action catalog](docs/supported-actions.md)
|
|
161
163
|
for individual capability and verification contracts.
|
|
162
164
|
|
|
163
|
-
See the [v1.18.
|
|
165
|
+
See the [v1.18.2 release notes](https://github.com/leancoderkavy/premiere-pro-mcp/releases/tag/v1.18.2)
|
|
164
166
|
for complete details. Live installation in Premiere Pro still requires host verification.
|
|
165
167
|
|
|
166
168
|
### Current MCP protocol support
|
|
@@ -208,7 +210,7 @@ their bins, media rules, and organization rules before a facility uses one.
|
|
|
208
210
|
> ### Install the published package (verify the name)
|
|
209
211
|
>
|
|
210
212
|
> ```bash
|
|
211
|
-
> npm i -g premiere-pro-mcp@1.18.
|
|
213
|
+
> npm i -g premiere-pro-mcp@1.18.2
|
|
212
214
|
> ```
|
|
213
215
|
>
|
|
214
216
|
> This repository publishes only **`premiere-pro-mcp`**. A differently named package (`adobe-premiere-pro-mcp`) may also declare a `premiere-pro-mcp` executable. Before configuring a client, confirm:
|
|
@@ -216,7 +218,7 @@ their bins, media rules, and organization rules before a facility uses one.
|
|
|
216
218
|
> | Check | Expected |
|
|
217
219
|
> | --- | --- |
|
|
218
220
|
> | Package name | `premiere-pro-mcp` (not `adobe-premiere-pro-mcp`) |
|
|
219
|
-
> | Version | `1.18.
|
|
221
|
+
> | Version | `1.18.2` |
|
|
220
222
|
> | Homepage / repo | https://premiere-pro-mcp.com/ · https://github.com/leancoderkavy/premiere-pro-mcp |
|
|
221
223
|
>
|
|
222
224
|
> ```bash
|
|
@@ -229,9 +231,9 @@ their bins, media rules, and organization rules before a facility uses one.
|
|
|
229
231
|
|
|
230
232
|
### Easiest supported path: Claude Desktop
|
|
231
233
|
|
|
232
|
-
1. Download the current [Claude Desktop bundle (`.mcpb`)](https://github.com/leancoderkavy/premiere-pro-mcp/releases/download/v1.18.
|
|
234
|
+
1. Download the current [Claude Desktop bundle (`.mcpb`)](https://github.com/leancoderkavy/premiere-pro-mcp/releases/download/v1.18.2/premiere-pro-mcp-1.18.2.mcpb).
|
|
233
235
|
2. In Claude Desktop, open **Settings > Extensions > Advanced settings > Install Extension**, select the downloaded bundle, and restart Claude Desktop.
|
|
234
|
-
3. Download the separate [signed Premiere connector (`.zxp`)](https://github.com/leancoderkavy/premiere-pro-mcp/releases/download/v1.18.
|
|
236
|
+
3. Download the separate [signed Premiere connector (`.zxp`)](https://github.com/leancoderkavy/premiere-pro-mcp/releases/download/v1.18.2/MCPBridgeCEP.zxp). Open it with your trusted ZXP installer. If your computer has no ZXP installer, use the npm connector installer in **Advanced setup** below.
|
|
235
237
|
4. Restart Premiere, open a project, then open **Window > Extensions > MCP for Adobe Premiere Pro**.
|
|
236
238
|
5. In Claude, enter: `Safely check my Premiere connection with verify_premiere_connection. Make no changes.`
|
|
237
239
|
|
|
@@ -531,11 +533,11 @@ From a clone of this repository:
|
|
|
531
533
|
```bash
|
|
532
534
|
codex plugin marketplace add .
|
|
533
535
|
codex plugin add premiere-pro@premiere-pro-mcp
|
|
534
|
-
npx -y premiere-pro-mcp@1.18.
|
|
536
|
+
npx -y premiere-pro-mcp@1.18.2 --install-cep
|
|
535
537
|
```
|
|
536
538
|
|
|
537
539
|
Restart Premiere Pro and start a new Codex session after installation. The plugin
|
|
538
|
-
launches `premiere-pro-mcp@1.18.
|
|
540
|
+
launches `premiere-pro-mcp@1.18.2` through `npx`; the separate CEP installation is
|
|
539
541
|
required because the MCP server communicates with the running Premiere host through
|
|
540
542
|
the local bridge.
|
|
541
543
|
|
|
@@ -573,7 +575,7 @@ For Claude Code, add this repository as a marketplace and install the plugin:
|
|
|
573
575
|
Then install the Premiere bridge and start a new Claude Code session:
|
|
574
576
|
|
|
575
577
|
```bash
|
|
576
|
-
npx -y premiere-pro-mcp@1.18.
|
|
578
|
+
npx -y premiere-pro-mcp@1.18.2 --install-cep
|
|
577
579
|
```
|
|
578
580
|
|
|
579
581
|
The Claude Code package lives in
|
|
@@ -897,11 +899,11 @@ reports revision-bound notes, VFX state, change impact and turnover exceptions.
|
|
|
897
899
|
It performs local inspection; host edits and exports use separate guarded tools.
|
|
898
900
|
See [usage, example and remaining execution adapters](docs/film-editorial-workflows.md).
|
|
899
901
|
|
|
900
|
-
## Tools (384 core total; 382 under the default profile;
|
|
902
|
+
## Tools (384 core total; 382 under the default profile; 478 with a connected UXP bridge)
|
|
901
903
|
|
|
902
904
|
The [complete supported-actions catalog](docs/supported-actions.md) lists every
|
|
903
905
|
registered core tool, the two tools restricted behind explicit `unsafe-script`
|
|
904
|
-
authority, and all
|
|
906
|
+
authority, and all 96 authenticated UXP additions with their current action or mode
|
|
905
907
|
values. It is generated from the same MCP registration surface returned to clients;
|
|
906
908
|
the tables below are a shorter workflow-oriented overview.
|
|
907
909
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<ExtensionManifest Version="7.0" ExtensionBundleId="com.mcp.aftereffects.bridge" ExtensionBundleVersion="1.18.
|
|
2
|
+
<ExtensionManifest Version="7.0" ExtensionBundleId="com.mcp.aftereffects.bridge" ExtensionBundleVersion="1.18.3" ExtensionBundleName="MCP for Adobe After Effects">
|
|
3
3
|
<ExtensionList>
|
|
4
|
-
<Extension Id="com.mcp.aftereffects.bridge.panel" Version="1.18.
|
|
4
|
+
<Extension Id="com.mcp.aftereffects.bridge.panel" Version="1.18.3"/>
|
|
5
5
|
</ExtensionList>
|
|
6
6
|
<ExecutionEnvironment>
|
|
7
7
|
<HostList>
|
|
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.18.
|
|
2
|
+
<ExtensionManifest Version="7.0" ExtensionBundleId="com.mcp.premiere.bridge" ExtensionBundleVersion="1.18.3" ExtensionBundleName="MCP for Adobe Premiere Pro">
|
|
3
3
|
<ExtensionList>
|
|
4
|
-
<Extension Id="com.mcp.premiere.bridge.panel" Version="1.18.
|
|
5
|
-
<Extension Id="com.mcp.premiere.bridge.headless" Version="1.18.
|
|
4
|
+
<Extension Id="com.mcp.premiere.bridge.panel" Version="1.18.3"/>
|
|
5
|
+
<Extension Id="com.mcp.premiere.bridge.headless" Version="1.18.3"/>
|
|
6
6
|
</ExtensionList>
|
|
7
7
|
<ExecutionEnvironment>
|
|
8
8
|
<HostList>
|
package/cep-plugin/index.html
CHANGED
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
<section class="update-section" aria-live="polite">
|
|
82
82
|
<div class="update-copy">
|
|
83
83
|
<span class="section-label">MCP updates</span>
|
|
84
|
-
<strong id="updateTitle">Version 1.18.
|
|
84
|
+
<strong id="updateTitle">Version 1.18.3</strong>
|
|
85
85
|
<span id="updateDetail">Checking the global MCP server and connector release…</span>
|
|
86
86
|
</div>
|
|
87
87
|
<button id="btnUpdate" class="button button-update" onclick="handleUpdateClick()" type="button" aria-describedby="updateDetail" disabled>
|
package/cep-plugin/updater.cjs
CHANGED
|
@@ -43,6 +43,14 @@ function __ticksToSeconds(ticks) {
|
|
|
43
43
|
return parseFloat(ticks) / TICKS_PER_SECOND;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// Sequence work-area getters return seconds (often as a string) on live 25.x
|
|
47
|
+
// and 26.x hosts. Values above 1e6 can only be ticks, so convert those.
|
|
48
|
+
function __workAreaSeconds(value) {
|
|
49
|
+
var number = parseFloat(value);
|
|
50
|
+
if (!isFinite(number) || number <= -399999) return null;
|
|
51
|
+
return number > 1000000 ? number / TICKS_PER_SECOND : number;
|
|
52
|
+
}
|
|
53
|
+
|
|
46
54
|
function __secondsToTicks(seconds) {
|
|
47
55
|
return Math.round(parseFloat(seconds) * TICKS_PER_SECOND);
|
|
48
56
|
}
|
|
@@ -2,6 +2,7 @@ import { randomUUID, timingSafeEqual } from "node:crypto";
|
|
|
2
2
|
import { EventEmitter } from "node:events";
|
|
3
3
|
import { createServer } from "node:http";
|
|
4
4
|
import { WebSocket, WebSocketServer } from "ws";
|
|
5
|
+
import { assertNoSymlinkedPaths, SymlinkPathError } from "../security/path-guard.js";
|
|
5
6
|
const LOOPBACK_HOST = "127.0.0.1";
|
|
6
7
|
const SUPPORTED_PROTOCOLS = new Set([1, 2]);
|
|
7
8
|
export class UxpBridgeError extends Error {
|
|
@@ -125,6 +126,16 @@ export class UxpWebSocketBridge extends EventEmitter {
|
|
|
125
126
|
if (hello.commands[command]?.supported !== true) {
|
|
126
127
|
throw new UxpBridgeError("UXP_COMMAND_UNSUPPORTED", `Connected Premiere host does not support UXP command '${command}'`);
|
|
127
128
|
}
|
|
129
|
+
// The panel cannot see through links on Premiere 26.5 UXP (#640), so refuse
|
|
130
|
+
// symlinked path segments here, before anything reaches the host.
|
|
131
|
+
try {
|
|
132
|
+
assertNoSymlinkedPaths(args);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (error instanceof SymlinkPathError)
|
|
136
|
+
throw new UxpBridgeError(error.code, error.message);
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
128
139
|
const minimumTimeoutMs = requestOptions.minimumTimeoutMs ?? 0;
|
|
129
140
|
if (!Number.isInteger(minimumTimeoutMs) || minimumTimeoutMs < 0) {
|
|
130
141
|
throw new Error("UXP minimum request timeout must be a non-negative integer");
|
|
@@ -245,6 +245,8 @@ export function buildPlatformCapabilityReport(capabilities, platform = process.p
|
|
|
245
245
|
"frame.export",
|
|
246
246
|
"sequence.playhead.inspect",
|
|
247
247
|
"sequence.playhead.set",
|
|
248
|
+
"workArea.inspect",
|
|
249
|
+
"workArea.set",
|
|
248
250
|
"sequence.timing.inspect",
|
|
249
251
|
"graphics.mogrtPath.inspect",
|
|
250
252
|
"timeline.selection.lift",
|
|
@@ -15,11 +15,14 @@
|
|
|
15
15
|
"total": 976,
|
|
16
16
|
"mapped": 377,
|
|
17
17
|
"unmapped": 599,
|
|
18
|
-
"manifestOnly":
|
|
18
|
+
"manifestOnly": 8
|
|
19
19
|
},
|
|
20
20
|
"manifestOnly": [
|
|
21
21
|
"MarkerCollection",
|
|
22
22
|
"VideoClipTrackItem.hasVideoTransition",
|
|
23
|
+
"WorkAreaUtilsStatic.getWorkAreaInPoint",
|
|
24
|
+
"WorkAreaUtilsStatic.getWorkAreaOutPoint",
|
|
25
|
+
"WorkAreaUtilsStatic.setWorkAreaInOutPoints",
|
|
23
26
|
"storage.localFileSystem.createPersistentToken",
|
|
24
27
|
"storage.localFileSystem.getEntryForPersistentToken",
|
|
25
28
|
"storage.localFileSystem.getFolder"
|
|
@@ -223,6 +223,31 @@
|
|
|
223
223
|
"idempotency": "operation_id",
|
|
224
224
|
"verificationBoundary": "sequence_playhead_readback"
|
|
225
225
|
},
|
|
226
|
+
{
|
|
227
|
+
"id": "guarded-work-area-control",
|
|
228
|
+
"adobeApi": [
|
|
229
|
+
"WorkAreaUtilsStatic.getWorkAreaInPoint",
|
|
230
|
+
"WorkAreaUtilsStatic.getWorkAreaOutPoint",
|
|
231
|
+
"WorkAreaUtilsStatic.setWorkAreaInOutPoints"
|
|
232
|
+
],
|
|
233
|
+
"documentationUrls": [
|
|
234
|
+
"https://developer.adobe.com/premiere-pro/uxp/changelog/"
|
|
235
|
+
],
|
|
236
|
+
"minimumPremiereVersion": "26.5.0",
|
|
237
|
+
"backend": "uxp",
|
|
238
|
+
"uxpCommand": "workArea.set",
|
|
239
|
+
"mcpTools": [
|
|
240
|
+
"manage_work_area_uxp"
|
|
241
|
+
],
|
|
242
|
+
"availability": "current",
|
|
243
|
+
"implementationStatus": "implemented",
|
|
244
|
+
"verificationStatus": "automated_contract_verified",
|
|
245
|
+
"liveHostVerificationStatus": "not_run",
|
|
246
|
+
"mutatesProject": true,
|
|
247
|
+
"undoable": false,
|
|
248
|
+
"idempotency": "operation_id",
|
|
249
|
+
"verificationBoundary": "work_area_readback"
|
|
250
|
+
},
|
|
226
251
|
{
|
|
227
252
|
"id": "guarded-app-preferences",
|
|
228
253
|
"adobeApi": [
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class SymlinkPathError extends Error {
|
|
2
|
+
readonly argumentPath: string;
|
|
3
|
+
readonly code = "UXP_PATH_SYMLINK_REFUSED";
|
|
4
|
+
constructor(argumentPath: string);
|
|
5
|
+
}
|
|
6
|
+
/** Returns the first symlinked segment of an absolute path, or null. */
|
|
7
|
+
export declare function findSymlinkedSegment(value: string): string | null;
|
|
8
|
+
/** Throws SymlinkPathError when any absolute path argument crosses a link. */
|
|
9
|
+
export declare function assertNoSymlinkedPaths(args: unknown): void;
|
|
10
|
+
//# sourceMappingURL=path-guard.d.ts.map
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { lstatSync, readlinkSync } from "node:fs";
|
|
2
|
+
import { posix, win32 } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Server-side symlink confinement for UXP path arguments (issue #640).
|
|
5
|
+
*
|
|
6
|
+
* The UXP panel checks lexical containment inside the approved workspace, but
|
|
7
|
+
* Premiere 26.5 UXP cannot see through links: `Entry.nativePath` reports the
|
|
8
|
+
* link's own path and `lstat` is unimplemented. The MCP server runs on the same
|
|
9
|
+
* machine as Premiere, so it walks every absolute path argument with Node's
|
|
10
|
+
* real filesystem before a command is sent and refuses any symlinked or
|
|
11
|
+
* junctioned segment. Segments that do not exist yet (new output files) end
|
|
12
|
+
* the walk; they cannot redirect anything.
|
|
13
|
+
*/
|
|
14
|
+
const MAX_DEPTH = 8;
|
|
15
|
+
const MAX_STRINGS = 512;
|
|
16
|
+
/** macOS calibration links that always point into /private. */
|
|
17
|
+
const SYSTEM_LINK_TARGETS = {
|
|
18
|
+
"/tmp": ["private/tmp", "/private/tmp"],
|
|
19
|
+
"/var": ["private/var", "/private/var"],
|
|
20
|
+
"/etc": ["private/etc", "/private/etc"],
|
|
21
|
+
};
|
|
22
|
+
export class SymlinkPathError extends Error {
|
|
23
|
+
argumentPath;
|
|
24
|
+
code = "UXP_PATH_SYMLINK_REFUSED";
|
|
25
|
+
constructor(argumentPath) {
|
|
26
|
+
super(`${argumentPath} goes through a symbolic link or junction. Use the real folder inside the approved workspace. No command was sent to Premiere.`);
|
|
27
|
+
this.argumentPath = argumentPath;
|
|
28
|
+
this.name = "SymlinkPathError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function absoluteFlavor(value) {
|
|
32
|
+
if (/^[A-Za-z]:[\\/]/.test(value) || /^\\\\[^\\]/.test(value))
|
|
33
|
+
return win32;
|
|
34
|
+
if (value.startsWith("/") && !value.startsWith("//"))
|
|
35
|
+
return posix;
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
function isSystemLink(segment) {
|
|
39
|
+
const targets = SYSTEM_LINK_TARGETS[segment];
|
|
40
|
+
if (!targets || process.platform !== "darwin")
|
|
41
|
+
return false;
|
|
42
|
+
try {
|
|
43
|
+
return targets.includes(readlinkSync(segment));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Returns the first symlinked segment of an absolute path, or null. */
|
|
50
|
+
export function findSymlinkedSegment(value) {
|
|
51
|
+
const flavor = absoluteFlavor(value);
|
|
52
|
+
if (!flavor || value.includes("\0"))
|
|
53
|
+
return null;
|
|
54
|
+
const normalized = flavor.normalize(value);
|
|
55
|
+
const root = flavor.parse(normalized).root;
|
|
56
|
+
const parts = normalized.slice(root.length).split(flavor.sep).filter(Boolean);
|
|
57
|
+
let current = root;
|
|
58
|
+
for (const part of parts) {
|
|
59
|
+
current = flavor.join(current, part);
|
|
60
|
+
let stats;
|
|
61
|
+
try {
|
|
62
|
+
stats = lstatSync(current);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
if (stats.isSymbolicLink()) {
|
|
68
|
+
if (!isSystemLink(current))
|
|
69
|
+
return current;
|
|
70
|
+
// Keep walking below /tmp, /var and /etc: lstat of later segments
|
|
71
|
+
// resolves through the calibration link and still sees user links.
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (!stats.isDirectory())
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function collectStrings(value, path, out, depth) {
|
|
80
|
+
if (out.length >= MAX_STRINGS || depth > MAX_DEPTH)
|
|
81
|
+
return;
|
|
82
|
+
if (typeof value === "string") {
|
|
83
|
+
if (absoluteFlavor(value))
|
|
84
|
+
out.push([path, value]);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (Array.isArray(value)) {
|
|
88
|
+
value.forEach((entry, index) => collectStrings(entry, `${path}[${index}]`, out, depth + 1));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (value && typeof value === "object") {
|
|
92
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
93
|
+
collectStrings(entry, path ? `${path}.${key}` : key, out, depth + 1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Throws SymlinkPathError when any absolute path argument crosses a link. */
|
|
98
|
+
export function assertNoSymlinkedPaths(args) {
|
|
99
|
+
const candidates = [];
|
|
100
|
+
collectStrings(args, "", candidates, 0);
|
|
101
|
+
for (const [argumentPath, value] of candidates) {
|
|
102
|
+
if (findSymlinkedSegment(value))
|
|
103
|
+
throw new SymlinkPathError(argumentPath || "path");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=path-guard.js.map
|
package/dist/tools/export.js
CHANGED
|
@@ -647,8 +647,13 @@ export function getExportTools(bridgeOptions) {
|
|
|
647
647
|
presetPath,
|
|
648
648
|
${args.work_area_only ? "app.encoder.ENCODE_WORKAREA" : "app.encoder.ENCODE_ENTIRE"}
|
|
649
649
|
);
|
|
650
|
+
// Premiere documents a boolean; false means the render was rejected.
|
|
651
|
+
// Some hosts throw instead; buildToolScript already maps that to __error.
|
|
652
|
+
if (exportResult === false) return __error("Premiere rejected the sequence export.");
|
|
653
|
+
var outputFile = new File(outputPath);
|
|
654
|
+
if (!outputFile.exists) return __error("Premiere did not write the requested export file.");
|
|
650
655
|
|
|
651
|
-
return __result({ exported: true, outputPath: outputPath, presetUsed: presetPath });
|
|
656
|
+
return __result({ exported: true, verified: true, outputPath: outputPath, presetUsed: presetPath });
|
|
652
657
|
`);
|
|
653
658
|
return sendCommand(script, { ...bridgeOptions, timeoutMs: 120000 }); // 2 min timeout for exports
|
|
654
659
|
},
|
package/dist/tools/playhead.js
CHANGED
|
@@ -65,10 +65,21 @@ export function getPlayheadTools(bridgeOptions) {
|
|
|
65
65
|
var seq = app.project.activeSequence;
|
|
66
66
|
if (!seq) return __error("No active sequence");
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
// Live hosts (25.2, 26.5.1) read and write work-area points in seconds,
|
|
69
|
+
// not ticks. Write seconds, then read back: some builds ignore the write.
|
|
70
|
+
var requestedIn = ${Number(args.in_seconds)};
|
|
71
|
+
var requestedOut = ${Number(args.out_seconds)};
|
|
72
|
+
if (!(requestedOut > requestedIn)) return __error("out_seconds must be greater than in_seconds.");
|
|
73
|
+
seq.setWorkAreaInPoint(requestedIn);
|
|
74
|
+
seq.setWorkAreaOutPoint(requestedOut);
|
|
75
|
+
var observedIn = __workAreaSeconds(seq.getWorkAreaInPoint());
|
|
76
|
+
var observedOut = __workAreaSeconds(seq.getWorkAreaOutPoint());
|
|
77
|
+
var frameSeconds = seq.timebase ? __ticksToSeconds(seq.timebase) : 1 / 24;
|
|
78
|
+
if (observedIn === null || observedOut === null ||
|
|
79
|
+
Math.abs(observedIn - requestedIn) > frameSeconds || Math.abs(observedOut - requestedOut) > frameSeconds) {
|
|
80
|
+
return __error("Premiere did not apply the work area (read back " + observedIn + " to " + observedOut + " s). Use set_sequence_in_out_points to mark an export range instead.");
|
|
81
|
+
}
|
|
82
|
+
return __result({ workAreaIn: observedIn, workAreaOut: observedOut, verified: true });
|
|
72
83
|
`);
|
|
73
84
|
return sendCommand(script, bridgeOptions);
|
|
74
85
|
},
|
|
@@ -83,8 +94,10 @@ export function getPlayheadTools(bridgeOptions) {
|
|
|
83
94
|
var inPoint = seq.getWorkAreaInPoint();
|
|
84
95
|
var outPoint = seq.getWorkAreaOutPoint();
|
|
85
96
|
return __result({
|
|
86
|
-
inSeconds:
|
|
87
|
-
outSeconds:
|
|
97
|
+
inSeconds: __workAreaSeconds(inPoint),
|
|
98
|
+
outSeconds: __workAreaSeconds(outPoint),
|
|
99
|
+
rawIn: String(inPoint),
|
|
100
|
+
rawOut: String(outPoint)
|
|
88
101
|
});
|
|
89
102
|
`);
|
|
90
103
|
return sendCommand(script, bridgeOptions);
|
|
@@ -44,6 +44,8 @@ export declare function getScriptingTools(bridgeOptions: BridgeOptions): {
|
|
|
44
44
|
properties: {
|
|
45
45
|
object_path: {
|
|
46
46
|
type: string;
|
|
47
|
+
minLength: number;
|
|
48
|
+
maxLength: number;
|
|
47
49
|
description: string;
|
|
48
50
|
};
|
|
49
51
|
max_depth: {
|
|
@@ -81,6 +83,8 @@ export declare function getScriptingTools(bridgeOptions: BridgeOptions): {
|
|
|
81
83
|
properties: {
|
|
82
84
|
sequence_id: {
|
|
83
85
|
type: string;
|
|
86
|
+
minLength: number;
|
|
87
|
+
maxLength: number;
|
|
84
88
|
description: string;
|
|
85
89
|
};
|
|
86
90
|
};
|
package/dist/tools/scripting.js
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { buildToolScript, escapeForExtendScript } from "../bridge/script-builder.js";
|
|
2
2
|
import { sendCommand, sendRawCommand } from "../bridge/file-bridge.js";
|
|
3
|
+
/** Property walk only: app/qe, identifiers, and numeric indexes. No calls or statements. */
|
|
4
|
+
const SAFE_DOM_OBJECT_PATH = /^(?:app|qe)(?:\.[A-Za-z_][A-Za-z0-9_]*|\[\d+\])*$/;
|
|
5
|
+
function safeDomObjectPath(objectPath) {
|
|
6
|
+
if (typeof objectPath !== "string" || objectPath.length === 0 || objectPath.length > 512) {
|
|
7
|
+
return { ok: false, error: "object_path must be a non-empty Premiere DOM path of at most 512 characters." };
|
|
8
|
+
}
|
|
9
|
+
if (!SAFE_DOM_OBJECT_PATH.test(objectPath)) {
|
|
10
|
+
return {
|
|
11
|
+
ok: false,
|
|
12
|
+
error: "object_path must be a property path starting with app or qe (for example app.project.activeSequence.videoTracks[0]). Function calls, operators, and statements are rejected because inspect_dom_object is not an unsafe-script tool.",
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
return { ok: true, path: objectPath };
|
|
16
|
+
}
|
|
3
17
|
export function getScriptingTools(bridgeOptions) {
|
|
4
18
|
return {
|
|
5
19
|
execute_extendscript: {
|
|
@@ -98,7 +112,7 @@ Examples:
|
|
|
98
112
|
},
|
|
99
113
|
},
|
|
100
114
|
inspect_dom_object: {
|
|
101
|
-
description: `Inspect a Premiere Pro DOM object and list its properties, methods, and values. Useful for exploring the API and debugging.
|
|
115
|
+
description: `Inspect a Premiere Pro DOM object and list its properties, methods, and values. Useful for exploring the API and debugging. object_path must be a property path starting with app or qe (identifiers and numeric indexes only). Function calls, operators, and statements are rejected; use evaluate_expression with unsafe-script for those.
|
|
102
116
|
|
|
103
117
|
Examples:
|
|
104
118
|
- "app.project" → project properties
|
|
@@ -110,7 +124,9 @@ Examples:
|
|
|
110
124
|
properties: {
|
|
111
125
|
object_path: {
|
|
112
126
|
type: "string",
|
|
113
|
-
|
|
127
|
+
minLength: 1,
|
|
128
|
+
maxLength: 512,
|
|
129
|
+
description: "Property path starting with app or qe (e.g. 'app.project.activeSequence.videoTracks[0]'). Function calls and statements are rejected.",
|
|
114
130
|
},
|
|
115
131
|
max_depth: {
|
|
116
132
|
type: "number",
|
|
@@ -120,7 +136,11 @@ Examples:
|
|
|
120
136
|
required: ["object_path"],
|
|
121
137
|
},
|
|
122
138
|
handler: async (args) => {
|
|
139
|
+
const objectPath = safeDomObjectPath(args.object_path);
|
|
140
|
+
if (!objectPath.ok)
|
|
141
|
+
return { success: false, error: objectPath.error };
|
|
123
142
|
const maxDepth = Math.min(args.max_depth ?? 1, 3);
|
|
143
|
+
const quotedPath = escapeForExtendScript(objectPath.path);
|
|
124
144
|
const script = buildToolScript(`
|
|
125
145
|
function inspectObj(obj, depth, maxD) {
|
|
126
146
|
if (depth > maxD) return "<max depth>";
|
|
@@ -174,19 +194,19 @@ Examples:
|
|
|
174
194
|
}
|
|
175
195
|
|
|
176
196
|
try {
|
|
177
|
-
var obj = ${
|
|
178
|
-
if (obj === null || obj === undefined) return __error("Object is null or undefined: ${
|
|
197
|
+
var obj = ${objectPath.path};
|
|
198
|
+
if (obj === null || obj === undefined) return __error("Object is null or undefined: ${quotedPath}");
|
|
179
199
|
var inspection = inspectObj(obj, 0, ${maxDepth});
|
|
180
200
|
return __result({
|
|
181
|
-
path: "${
|
|
201
|
+
path: "${quotedPath}",
|
|
182
202
|
type: typeof obj,
|
|
183
203
|
inspection: inspection
|
|
184
204
|
});
|
|
185
205
|
} catch(e) {
|
|
186
|
-
return __error("Cannot access: ${
|
|
206
|
+
return __error("Cannot access: ${quotedPath} — " + e.toString());
|
|
187
207
|
}
|
|
188
208
|
`);
|
|
189
|
-
return
|
|
209
|
+
return sendCommand(script, bridgeOptions);
|
|
190
210
|
},
|
|
191
211
|
},
|
|
192
212
|
list_clip_effects: {
|
|
@@ -252,13 +272,15 @@ Examples:
|
|
|
252
272
|
properties: {
|
|
253
273
|
sequence_id: {
|
|
254
274
|
type: "string",
|
|
275
|
+
minLength: 1,
|
|
276
|
+
maxLength: 512,
|
|
255
277
|
description: "Sequence name or ID. Uses active sequence if omitted.",
|
|
256
278
|
},
|
|
257
279
|
},
|
|
258
280
|
},
|
|
259
281
|
handler: async (args) => {
|
|
260
282
|
const seqLookup = args.sequence_id
|
|
261
|
-
? `var seq = __findSequence("${args.sequence_id}"); if (!seq) return __error("Sequence not found");`
|
|
283
|
+
? `var seq = __findSequence("${escapeForExtendScript(args.sequence_id)}"); if (!seq) return __error("Sequence not found");`
|
|
262
284
|
: `var seq = app.project.activeSequence; if (!seq) return __error("No active sequence");`;
|
|
263
285
|
const script = buildToolScript(`
|
|
264
286
|
${seqLookup}
|
package/dist/tools/uxp.d.ts
CHANGED
|
@@ -471,6 +471,91 @@ export declare function getUxpTools(bridge: UxpWebSocketBridge): {
|
|
|
471
471
|
error: string;
|
|
472
472
|
}>;
|
|
473
473
|
};
|
|
474
|
+
manage_work_area_uxp: {
|
|
475
|
+
description: string;
|
|
476
|
+
parameters: {
|
|
477
|
+
type: "object";
|
|
478
|
+
additionalProperties: boolean;
|
|
479
|
+
properties: {
|
|
480
|
+
action: {
|
|
481
|
+
type: string;
|
|
482
|
+
enum: string[];
|
|
483
|
+
description: string;
|
|
484
|
+
};
|
|
485
|
+
expected_sequence_guid: {
|
|
486
|
+
type: string;
|
|
487
|
+
minLength: number;
|
|
488
|
+
maxLength: number;
|
|
489
|
+
description: string;
|
|
490
|
+
};
|
|
491
|
+
expected_work_area: {
|
|
492
|
+
type: string;
|
|
493
|
+
additionalProperties: boolean;
|
|
494
|
+
properties: {
|
|
495
|
+
in_seconds: {
|
|
496
|
+
type: string;
|
|
497
|
+
minimum: number;
|
|
498
|
+
maximum: number;
|
|
499
|
+
description: string;
|
|
500
|
+
};
|
|
501
|
+
out_seconds: {
|
|
502
|
+
type: string;
|
|
503
|
+
minimum: number;
|
|
504
|
+
maximum: number;
|
|
505
|
+
description: string;
|
|
506
|
+
};
|
|
507
|
+
};
|
|
508
|
+
required: string[];
|
|
509
|
+
description: string;
|
|
510
|
+
};
|
|
511
|
+
in_seconds: {
|
|
512
|
+
type: string;
|
|
513
|
+
minimum: number;
|
|
514
|
+
maximum: number;
|
|
515
|
+
description: string;
|
|
516
|
+
};
|
|
517
|
+
out_seconds: {
|
|
518
|
+
type: string;
|
|
519
|
+
minimum: number;
|
|
520
|
+
maximum: number;
|
|
521
|
+
description: string;
|
|
522
|
+
};
|
|
523
|
+
operation_id: {
|
|
524
|
+
type: "string";
|
|
525
|
+
description: string;
|
|
526
|
+
};
|
|
527
|
+
};
|
|
528
|
+
required: string[];
|
|
529
|
+
};
|
|
530
|
+
operationalCapability: {
|
|
531
|
+
backend: "UXP";
|
|
532
|
+
backends: "uxp"[];
|
|
533
|
+
minimumPremiereVersion: string;
|
|
534
|
+
verificationBoundary: "structured_uxp_readback";
|
|
535
|
+
hostVerificationRequired: boolean;
|
|
536
|
+
notes: string[];
|
|
537
|
+
};
|
|
538
|
+
handler: (args: {
|
|
539
|
+
action: "inspect" | "set";
|
|
540
|
+
expected_sequence_guid?: string;
|
|
541
|
+
expected_work_area?: {
|
|
542
|
+
in_seconds: number;
|
|
543
|
+
out_seconds: number;
|
|
544
|
+
};
|
|
545
|
+
in_seconds?: number;
|
|
546
|
+
out_seconds?: number;
|
|
547
|
+
operation_id?: string;
|
|
548
|
+
}) => Promise<{
|
|
549
|
+
success: boolean;
|
|
550
|
+
data: {
|
|
551
|
+
backend: string;
|
|
552
|
+
result: unknown;
|
|
553
|
+
};
|
|
554
|
+
} | {
|
|
555
|
+
success: boolean;
|
|
556
|
+
error: string;
|
|
557
|
+
}>;
|
|
558
|
+
};
|
|
474
559
|
manage_sequence_playhead_uxp: {
|
|
475
560
|
description: string;
|
|
476
561
|
parameters: {
|
package/dist/tools/uxp.js
CHANGED
|
@@ -334,6 +334,53 @@ export function getUxpTools(bridge) {
|
|
|
334
334
|
});
|
|
335
335
|
},
|
|
336
336
|
},
|
|
337
|
+
manage_work_area_uxp: {
|
|
338
|
+
description: "Inspect or set the active sequence work area through Premiere 26.5+ documented UXP WorkAreaUtils. Setting requires the sequence GUID and complete work area returned by inspect, keeps out within the sequence end, serializes competing requests, and verifies native in/out readback. WorkAreaUtils is a direct call, not an undoable transaction; a runtime capability probe remains authoritative and live-host behavior is not yet verified.",
|
|
339
|
+
parameters: {
|
|
340
|
+
type: "object",
|
|
341
|
+
additionalProperties: false,
|
|
342
|
+
properties: {
|
|
343
|
+
action: { type: "string", enum: ["inspect", "set"], description: "Read the active work area or apply a guarded update." },
|
|
344
|
+
expected_sequence_guid: { type: "string", minLength: 1, maxLength: 512, description: "Required for set; copy the active sequence GUID returned by inspect." },
|
|
345
|
+
expected_work_area: {
|
|
346
|
+
type: "object",
|
|
347
|
+
additionalProperties: false,
|
|
348
|
+
properties: {
|
|
349
|
+
in_seconds: { type: "number", minimum: 0, maximum: 86400, description: "Work-area in point returned by inspect." },
|
|
350
|
+
out_seconds: { type: "number", minimum: 0, maximum: 86400, description: "Work-area out point returned by inspect." },
|
|
351
|
+
},
|
|
352
|
+
required: ["in_seconds", "out_seconds"],
|
|
353
|
+
description: "Required for set; complete work area returned by inspect. A changed value rejects the request before Premiere is called.",
|
|
354
|
+
},
|
|
355
|
+
in_seconds: { type: "number", minimum: 0, maximum: 86400, description: "Required for set; new work-area in point in seconds." },
|
|
356
|
+
out_seconds: { type: "number", minimum: 0, maximum: 86400, description: "Required for set; new work-area out point in seconds. Must exceed in_seconds and stay within the sequence end." },
|
|
357
|
+
operation_id: operationId,
|
|
358
|
+
},
|
|
359
|
+
required: ["action"],
|
|
360
|
+
},
|
|
361
|
+
operationalCapability: {
|
|
362
|
+
backend: "UXP",
|
|
363
|
+
backends: ["uxp"],
|
|
364
|
+
minimumPremiereVersion: "26.5",
|
|
365
|
+
verificationBoundary: "structured_uxp_readback",
|
|
366
|
+
hostVerificationRequired: true,
|
|
367
|
+
notes: ["Available only through an authenticated UXP bridge whose runtime capability handshake advertises the requested workArea command (Premiere 26.5+ WorkAreaUtils)."],
|
|
368
|
+
},
|
|
369
|
+
handler: async (args) => {
|
|
370
|
+
if (args.action === "inspect")
|
|
371
|
+
return invoke(bridge, "workArea.inspect");
|
|
372
|
+
return invoke(bridge, "workArea.set", {
|
|
373
|
+
...(args.expected_sequence_guid === undefined ? {} : { expectedSequenceGuid: args.expected_sequence_guid }),
|
|
374
|
+
...(args.expected_work_area === undefined ? {} : { expectedWorkArea: {
|
|
375
|
+
inSeconds: args.expected_work_area.in_seconds,
|
|
376
|
+
outSeconds: args.expected_work_area.out_seconds,
|
|
377
|
+
} }),
|
|
378
|
+
...(args.in_seconds === undefined ? {} : { inSeconds: args.in_seconds }),
|
|
379
|
+
...(args.out_seconds === undefined ? {} : { outSeconds: args.out_seconds }),
|
|
380
|
+
...(args.operation_id ? { operationId: args.operation_id } : {}),
|
|
381
|
+
});
|
|
382
|
+
},
|
|
383
|
+
},
|
|
337
384
|
manage_sequence_playhead_uxp: {
|
|
338
385
|
description: "Inspect or set the active sequence player position through documented Premiere UXP APIs. Setting requires the sequence GUID and current position returned by inspect, serializes competing requests for that sequence, and verifies native readback; it changes player state only and makes no project-save or Undo claim.",
|
|
339
386
|
parameters: {
|
|
@@ -10,8 +10,12 @@ and the
|
|
|
10
10
|
|
|
11
11
|
The generated receipt covers only the beta root binding
|
|
12
12
|
`premierepro.WorkAreaUtils`, the empty `WorkAreaUtils` instance type, and the
|
|
13
|
-
five methods of `WorkAreaUtilsStatic`.
|
|
14
|
-
|
|
13
|
+
five methods of `WorkAreaUtilsStatic`. Adobe has since shipped
|
|
14
|
+
`WorkAreaUtils` in stable Premiere 26.5.0. The capability-gated
|
|
15
|
+
`manage_work_area_uxp` tool calls the get/set-in-out methods only when the
|
|
16
|
+
connected host probes them, and requires Premiere 26.5+; see
|
|
17
|
+
[Adobe UXP 26.5 coverage](adobe-uxp-26.5-coverage.md). This receipt still
|
|
18
|
+
accounts only for the pinned declarations.
|
|
15
19
|
|
|
16
20
|
The repository's existing `get_work_area` and `set_work_area` tools use
|
|
17
21
|
established legacy host paths. This receipt does not change those paths or
|
|
@@ -13,8 +13,8 @@ source catalog may include unreleased actions.
|
|
|
13
13
|
| Registered core actions | 384 | CEP/local server catalog; host and authority checks still apply |
|
|
14
14
|
| Default-profile core actions | 382 | Advertised with `inspect,edit,export,filesystem` |
|
|
15
15
|
| Restricted core actions | 2 | Require explicit `unsafe-script` authority |
|
|
16
|
-
| Authenticated UXP additions |
|
|
17
|
-
| Default profile with UXP |
|
|
16
|
+
| Authenticated UXP additions | 96 | Advertised only while a compatible authenticated UXP panel is connected |
|
|
17
|
+
| Default profile with UXP | 478 | 382 core plus 96 UXP tools |
|
|
18
18
|
|
|
19
19
|
## How to read support
|
|
20
20
|
|
|
@@ -221,7 +221,7 @@ operation” when the tool has no enum-based mode.
|
|
|
221
221
|
| `inspect_after_effects_render_templates` | Default profile | Single operation | Read available render and output-module template names from the first existing After Effects render-queue item. It never queues or renders a composition. |
|
|
222
222
|
| `inspect_after_effects_template_source` | Default profile | Single operation | Inspect a saved After Effects source composition for MOGRT-relevant dimensions, duration, text fonts, layer-source kinds, and Essential Graphics controller names when the host exposes the AE 16.1+ readback API. It never creates a composition or returns asset paths. |
|
|
223
223
|
| `inspect_cmx3600_edl` | Default profile | Single operation | Parse a local CMX 3600 EDL into bounded event, reel, track, transition, and timecode facts without importing it into Premiere. |
|
|
224
|
-
| `inspect_dom_object` | Default profile | Single operation | Inspect a Premiere Pro DOM object and list its properties, methods, and values. Useful for exploring the API and debugging. Examples: - "app.project" → project properties - "app.project.activeSequence" → sequence properties - "app.project.activeSequence.videoTracks[0].clips[0]" → first clip on V1 - "app.project.activeSequence.videoTracks[0].clips[0].components[0]" → first component of a clip |
|
|
224
|
+
| `inspect_dom_object` | Default profile | Single operation | Inspect a Premiere Pro DOM object and list its properties, methods, and values. Useful for exploring the API and debugging. object_path must be a property path starting with app or qe (identifiers and numeric indexes only). Function calls, operators, and statements are rejected; use evaluate_expression with unsafe-script for those. Examples: - "app.project" → project properties - "app.project.activeSequence" → sequence properties - "app.project.activeSequence.videoTracks[0].clips[0]" → first clip on V1 - "app.project.activeSequence.videoTracks[0].clips[0].components[0]" → first component of a clip |
|
|
225
225
|
| `inspect_edit_readiness` | Default profile | Single operation | Audit the active sequence in one read-only bridge request for empty timelines, primary-track gaps, disabled clips, muted tracks, and excessive Motion scale. Structural diagnostics only; it cannot judge story, framing, sound, or final delivery. |
|
|
226
226
|
| `inspect_fcpxml_interchange` | Default profile | Single operation | Inspect a local FCPXML document's root version, sequence/clip counts, bounded asset declarations, and text-only parser warnings before deliberate Premiere import. |
|
|
227
227
|
| `inspect_film_editorial_workflow` | Default profile | Single operation | Validate a revision-bound film editorial manifest against captured source and timeline identities. Build complete declared coverage review groups, independent picture/audio preferences, screening-note exceptions, story dependencies, VFX state, change impact and a department turnover manifest. Local inspection only: no host edits, exports, automatic creative decisions or verified host claims. |
|
|
@@ -505,6 +505,7 @@ authenticated and the connected host advertises the required command capabilitie
|
|
|
505
505
|
| `manage_timeline_selection_uxp` | Connected UXP | `inspect`, `inspect_targets`, `replace`, `add`, `remove`, `clear` | Inspect, replace, add to, remove from, or clear the active sequence's native UXP clip selection with sequence and clip fingerprint stale-state guards. |
|
|
506
506
|
| `manage_timeline_source_label_uxp` | Connected UXP | `inspect`, `update` | Inspect or set the documented source Project-item color label resolved from one audio or video timeline coordinate in the sequence named by sequence_id (inspect falls back to the active sequence only when sequence_id is omitted; update always targets expected_snapshot.sequence_id). Update requires the complete reviewed snapshot, explicit confirmation, and an operation ID; it serializes color-label changes by source item, commits one undoable transaction, then re-reads the coordinate and source label. A source label is project-global: another use of the same source can reflect the change. It does not label a timeline-only instance, change clip timing, prove rendered appearance, playback, persistence, or Undo behavior. |
|
|
507
507
|
| `manage_track_state_uxp` | Connected UXP | `inspect`, `set_mute` | Inspect audio, video, and caption track mute state or set one media type serially with stale-state preflight and per-track readback. Adobe exposes this as direct promises, so no undo transaction is claimed. |
|
|
508
|
+
| `manage_work_area_uxp` | Connected UXP | `inspect`, `set` | Inspect or set the active sequence work area through Premiere 26.5+ documented UXP WorkAreaUtils. Setting requires the sequence GUID and complete work area returned by inspect, keeps out within the sequence end, serializes competing requests, and verifies native in/out readback. WorkAreaUtils is a direct call, not an undoable transaction; a runtime capability probe remains authoritative and live-host behavior is not yet verified. |
|
|
508
509
|
| `manage_workflow_checkpoints_uxp` | Connected UXP | `has`, `get`, `set`, `clear` | Read or transactionally write small, namespaced workflow checkpoints on the active project or a targeted sequence. Persistent values may sync with cloud projects; never store secrets, native paths, transcripts, or media names. |
|
|
509
510
|
| `organize_project_items_uxp` | Connected UXP | `inspect_bin`, `create_bin`, `create_smart_bin`, `rename`, `move`, `set_color`, `remove` | Inspect a bin or transactionally create, rename, move, color-label, and remove project items with stable-ID guards. |
|
|
510
511
|
| `plan_transcript_rough_cut_uxp` | Connected UXP | Single operation | Build a revision-locked, non-mutating rough-cut plan from Premiere's native transcript and verified 1x sequence placements. The plan orders cuts from the end of the timeline, requires a duplicate sequence, and requires re-query after every mutation. |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "premiere-pro-mcp",
|
|
3
3
|
"mcpName": "io.github.leancoderkavy/premiere-pro",
|
|
4
|
-
"version": "1.18.
|
|
4
|
+
"version": "1.18.3",
|
|
5
5
|
"description": "MCP server for Adobe Premiere Pro with 384 core AI video editing tools, a guarded After Effects MOGRT studio, and a capability-aware UXP bridge for supported Premiere workflows.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -203,7 +203,13 @@
|
|
|
203
203
|
"adobe-premiere-mcp",
|
|
204
204
|
"premiere-mcp",
|
|
205
205
|
"video-editing-mcp",
|
|
206
|
-
"uxp"
|
|
206
|
+
"uxp",
|
|
207
|
+
"adobe-uxp",
|
|
208
|
+
"premiere-pro-uxp",
|
|
209
|
+
"premiere-pro-ai",
|
|
210
|
+
"premiere-pro-automation",
|
|
211
|
+
"claude-premiere-pro",
|
|
212
|
+
"premiere-pro-extendscript"
|
|
207
213
|
],
|
|
208
214
|
"license": "MIT",
|
|
209
215
|
"publishConfig": {
|
|
@@ -214,12 +220,12 @@
|
|
|
214
220
|
"node": ">=20.19.0"
|
|
215
221
|
},
|
|
216
222
|
"dependencies": {
|
|
217
|
-
"@posthog/core": "1.
|
|
218
|
-
"@posthog/types": "1.412.
|
|
223
|
+
"@posthog/core": "1.55.1",
|
|
224
|
+
"@posthog/types": "1.412.4",
|
|
219
225
|
"@modelcontextprotocol/node": "^2.0.0",
|
|
220
226
|
"@modelcontextprotocol/server": "^2.0.0",
|
|
221
227
|
"jose": "^6.2.10",
|
|
222
|
-
"posthog-node": "5.52.
|
|
228
|
+
"posthog-node": "5.52.5",
|
|
223
229
|
"ws": "^8.21.1",
|
|
224
230
|
"zod": "^4.4.3"
|
|
225
231
|
},
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "MCP for Adobe Premiere Pro",
|
|
11
11
|
"mcpName": "io.github.leancoderkavy/premiere-pro",
|
|
12
12
|
"npmPackage": "premiere-pro-mcp",
|
|
13
|
-
"version": "1.18.
|
|
13
|
+
"version": "1.18.3",
|
|
14
14
|
"repository": "https://github.com/leancoderkavy/premiere-pro-mcp",
|
|
15
15
|
"homepage": "https://premiere-pro-mcp.com/",
|
|
16
16
|
"license": "MIT",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"capabilitySurface": {
|
|
30
30
|
"registeredCoreTools": 384,
|
|
31
31
|
"defaultProfileTools": 382,
|
|
32
|
-
"authenticatedUxpAdditions":
|
|
33
|
-
"defaultProfileWithUxp":
|
|
32
|
+
"authenticatedUxpAdditions": 96,
|
|
33
|
+
"defaultProfileWithUxp": 478,
|
|
34
34
|
"toolModules": 57,
|
|
35
35
|
"guidedWorkflows": 19
|
|
36
36
|
},
|
|
@@ -58,8 +58,12 @@ for (const surface of surfaces) {
|
|
|
58
58
|
errors.push(`${surface.file} is missing required display name evidence: ${required}`);
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
+
// The README can use the full search phrase without treating its suffix as a display name.
|
|
62
|
+
const brandingText = surface.file === "README.md"
|
|
63
|
+
? source.replaceAll("Adobe Premiere Pro MCP", "")
|
|
64
|
+
: source;
|
|
61
65
|
for (const retired of retiredDisplayNames) {
|
|
62
|
-
if (
|
|
66
|
+
if (brandingText.includes(retired)) {
|
|
63
67
|
errors.push(`${surface.file} still exposes retired display name: ${retired}`);
|
|
64
68
|
}
|
|
65
69
|
}
|
package/uxp-plugin/commands.cjs
CHANGED
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
"sequence.createPreset": { destructive: true, undoable: false, idempotent: true, requiresWorkspace: true, minHostVersion: "26.3.0", probe: canCreatePresetSequence, handler: createPresetSequence },
|
|
42
42
|
"sequence.range.inspect": { readOnly: true, minHostVersion: "25.6.0", probe: canInspectSequenceRange, handler: inspectSequenceRange },
|
|
43
43
|
"sequence.range.update": { destructive: true, undoable: true, idempotent: true, minHostVersion: "25.6.0", probe: canUpdateSequenceRange, handler: updateSequenceRange },
|
|
44
|
+
"workArea.inspect": { readOnly: true, minHostVersion: "26.5.0", probe: canInspectWorkArea, handler: inspectWorkArea },
|
|
45
|
+
"workArea.set": { idempotent: true, minHostVersion: "26.5.0", probe: canSetWorkArea, handler: setWorkArea },
|
|
44
46
|
"sequence.playhead.inspect": { readOnly: true, minHostVersion: "25.6.0", probe: canInspectSequencePlayhead, handler: inspectSequencePlayhead },
|
|
45
47
|
"sequence.playhead.set": { idempotent: true, minHostVersion: "25.6.0", probe: canSetSequencePlayhead, handler: setSequencePlayhead },
|
|
46
48
|
"time.frameAlignment.inspect": { readOnly: true, minHostVersion: "25.6.0", probe: canInspectFrameAlignment, handler: inspectFrameAlignment },
|
|
@@ -443,6 +445,84 @@
|
|
|
443
445
|
if (sequenceRangeUpdateTails.get(sequenceGuid) === tail) sequenceRangeUpdateTails.delete(sequenceGuid);
|
|
444
446
|
});
|
|
445
447
|
}
|
|
448
|
+
async function workAreaSnapshot(sequence) {
|
|
449
|
+
const sequenceGuid = sequenceGuidRequired(sequence);
|
|
450
|
+
const utils = ppro.WorkAreaUtils;
|
|
451
|
+
const [inPoint, outPoint] = await Promise.all([utils.getWorkAreaInPoint(sequence), utils.getWorkAreaOutPoint(sequence)]);
|
|
452
|
+
const workArea = {
|
|
453
|
+
inSeconds: tickSecondsRequired(inPoint, "work area in point"),
|
|
454
|
+
outSeconds: tickSecondsRequired(outPoint, "work area out point")
|
|
455
|
+
};
|
|
456
|
+
if (workArea.outSeconds < workArea.inSeconds) {
|
|
457
|
+
throw commandError("UXP_VERIFICATION_FAILED", "Premiere returned a work area whose out point precedes its in point");
|
|
458
|
+
}
|
|
459
|
+
return { sequenceGuid, workArea };
|
|
460
|
+
}
|
|
461
|
+
async function inspectWorkArea(args) {
|
|
462
|
+
assertOnlyKeys(args, []);
|
|
463
|
+
const context = await activeContext(false);
|
|
464
|
+
return { ...await workAreaSnapshot(context.sequence), verificationBoundary: "work_area_readback" };
|
|
465
|
+
}
|
|
466
|
+
function validateWorkAreaSetArgs(args) {
|
|
467
|
+
assertObject(args);
|
|
468
|
+
assertOnlyKeys(args, ["expectedSequenceGuid", "expectedWorkArea", "inSeconds", "outSeconds", "operationId"]);
|
|
469
|
+
if (typeof args.expectedSequenceGuid !== "string" || !args.expectedSequenceGuid || args.expectedSequenceGuid.length > 512) {
|
|
470
|
+
throw commandError("UXP_INVALID_ARGUMENT", "expectedSequenceGuid is required and must be at most 512 characters");
|
|
471
|
+
}
|
|
472
|
+
assertObject(args.expectedWorkArea);
|
|
473
|
+
assertOnlyKeys(args.expectedWorkArea, ["inSeconds", "outSeconds"]);
|
|
474
|
+
const expectedWorkArea = {
|
|
475
|
+
inSeconds: boundedSeconds(args.expectedWorkArea.inSeconds, "expectedWorkArea.inSeconds"),
|
|
476
|
+
outSeconds: boundedSeconds(args.expectedWorkArea.outSeconds, "expectedWorkArea.outSeconds")
|
|
477
|
+
};
|
|
478
|
+
const inSeconds = boundedSeconds(args.inSeconds, "inSeconds");
|
|
479
|
+
const outSeconds = boundedSeconds(args.outSeconds, "outSeconds");
|
|
480
|
+
if (outSeconds <= inSeconds) throw commandError("UXP_INVALID_ARGUMENT", "outSeconds must be greater than inSeconds");
|
|
481
|
+
return { expectedSequenceGuid: args.expectedSequenceGuid, expectedWorkArea, desired: { inSeconds, outSeconds } };
|
|
482
|
+
}
|
|
483
|
+
async function setWorkArea(args) {
|
|
484
|
+
const input = validateWorkAreaSetArgs(args);
|
|
485
|
+
const inTick = await tickTime(input.desired.inSeconds, "inSeconds");
|
|
486
|
+
const outTick = await tickTime(input.desired.outSeconds, "outSeconds");
|
|
487
|
+
return withSequenceRangeUpdateLock("workArea:" + input.expectedSequenceGuid, async () => {
|
|
488
|
+
const context = await activeContext(false);
|
|
489
|
+
const before = await workAreaSnapshot(context.sequence);
|
|
490
|
+
if (before.sequenceGuid !== input.expectedSequenceGuid) {
|
|
491
|
+
throw commandError("UXP_STALE_SEQUENCE", "The active sequence changed before the work-area update; inspect and retry");
|
|
492
|
+
}
|
|
493
|
+
if (!sameSeconds(before.workArea.inSeconds, input.expectedWorkArea.inSeconds) ||
|
|
494
|
+
!sameSeconds(before.workArea.outSeconds, input.expectedWorkArea.outSeconds)) {
|
|
495
|
+
throw commandError("UXP_STALE_RANGE", "The work area changed before the update; inspect the current work area and retry");
|
|
496
|
+
}
|
|
497
|
+
const endSeconds = tickSecondsRequired(await context.sequence.getEndTime(), "sequence end point");
|
|
498
|
+
if (input.desired.outSeconds > endSeconds + 0.000001) {
|
|
499
|
+
throw commandError("UXP_INVALID_ARGUMENT", "outSeconds must not exceed the sequence end");
|
|
500
|
+
}
|
|
501
|
+
const accepted = await ppro.WorkAreaUtils.setWorkAreaInOutPoints(context.sequence, inTick, outTick);
|
|
502
|
+
if (accepted !== true) throw commandError("UXP_ACTION_REJECTED", "Premiere rejected the work-area update");
|
|
503
|
+
const after = await workAreaSnapshot(context.sequence);
|
|
504
|
+
if (after.sequenceGuid !== before.sequenceGuid ||
|
|
505
|
+
!sameSeconds(after.workArea.inSeconds, input.desired.inSeconds) ||
|
|
506
|
+
!sameSeconds(after.workArea.outSeconds, input.desired.outSeconds)) {
|
|
507
|
+
throw commandError("UXP_VERIFICATION_FAILED", "Premiere did not retain the requested work area");
|
|
508
|
+
}
|
|
509
|
+
return {
|
|
510
|
+
updated: true,
|
|
511
|
+
outcome: "verified",
|
|
512
|
+
sequenceGuid: after.sequenceGuid,
|
|
513
|
+
workArea: after.workArea,
|
|
514
|
+
verified: "work_area_readback",
|
|
515
|
+
operation: operationSemantics({
|
|
516
|
+
mutatesProject: true,
|
|
517
|
+
verificationStatus: "verified",
|
|
518
|
+
verificationBoundary: "work_area_readback",
|
|
519
|
+
verificationEvidence: [{ type: "work_area", sequenceGuid: after.sequenceGuid, workArea: after.workArea }],
|
|
520
|
+
undoSupported: false,
|
|
521
|
+
cancellationSupported: false
|
|
522
|
+
})
|
|
523
|
+
};
|
|
524
|
+
});
|
|
525
|
+
}
|
|
446
526
|
async function inspectSequencePlayhead(args) {
|
|
447
527
|
assertOnlyKeys(args, []);
|
|
448
528
|
const context = await activeContext(false);
|
|
@@ -1524,6 +1604,14 @@
|
|
|
1524
1604
|
function canInspectSequenceRange() {
|
|
1525
1605
|
return activeSequenceHas(["getInPoint", "getOutPoint", "getZeroPoint", "getEndTime"]);
|
|
1526
1606
|
}
|
|
1607
|
+
function canInspectWorkArea() {
|
|
1608
|
+
return !!(ppro.WorkAreaUtils && typeof ppro.WorkAreaUtils.getWorkAreaInPoint === "function" &&
|
|
1609
|
+
typeof ppro.WorkAreaUtils.getWorkAreaOutPoint === "function");
|
|
1610
|
+
}
|
|
1611
|
+
async function canSetWorkArea() {
|
|
1612
|
+
return !!(canInspectWorkArea() && typeof ppro.WorkAreaUtils.setWorkAreaInOutPoints === "function" &&
|
|
1613
|
+
ppro.TickTime && typeof ppro.TickTime.createWithSeconds === "function" && await activeSequenceHas(["getEndTime"]));
|
|
1614
|
+
}
|
|
1527
1615
|
function canInspectSequencePlayhead() {
|
|
1528
1616
|
return activeSequenceHas(["getPlayerPosition"]);
|
|
1529
1617
|
}
|
package/uxp-plugin/manifest.json
CHANGED
|
@@ -1196,9 +1196,17 @@
|
|
|
1196
1196
|
return { projectItemId, startSeconds: start, durationSeconds: duration };
|
|
1197
1197
|
}
|
|
1198
1198
|
|
|
1199
|
+
// Premiere 26.5 documents synchronous Media.getStart()/getDuration() and
|
|
1200
|
+
// deprecates the start/duration properties, which return Promise<TickTime>.
|
|
1201
|
+
function mediaTimingGetter(media, propertyName) {
|
|
1202
|
+
const getterName = propertyName === "start" ? "getStart" : "getDuration";
|
|
1203
|
+
return media && typeof media[getterName] === "function" ? getterName : null;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1199
1206
|
function synchronousMediaTimingValue(media, propertyName) {
|
|
1200
1207
|
try {
|
|
1201
|
-
const
|
|
1208
|
+
const getterName = mediaTimingGetter(media, propertyName);
|
|
1209
|
+
const value = getterName ? media[getterName]() : media[propertyName];
|
|
1202
1210
|
if (value && typeof value.then === "function") return null;
|
|
1203
1211
|
return boundedMediaTimingSeconds(value);
|
|
1204
1212
|
} catch (_) { return null; }
|
|
@@ -1332,6 +1340,13 @@
|
|
|
1332
1340
|
}
|
|
1333
1341
|
|
|
1334
1342
|
async function mediaTimingValue(media, propertyName) {
|
|
1343
|
+
const getterName = mediaTimingGetter(media, propertyName);
|
|
1344
|
+
if (getterName) {
|
|
1345
|
+
try {
|
|
1346
|
+
const seconds = boundedMediaTimingSeconds(await media[getterName]());
|
|
1347
|
+
if (seconds != null) return { accessor: getterName + "()", seconds };
|
|
1348
|
+
} catch (_) { /* fall back to the deprecated property */ }
|
|
1349
|
+
}
|
|
1335
1350
|
try {
|
|
1336
1351
|
return { accessor: propertyName, seconds: boundedMediaTimingSeconds(await media[propertyName]) };
|
|
1337
1352
|
} catch (_) {
|