premiere-pro-mcp 1.15.2 → 1.16.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 +45 -0
- package/README.md +76 -15
- package/after-effects-cep-plugin/CSXS/manifest.xml +2 -2
- package/after-effects-cep-plugin/bridge-directory-security.cjs +233 -0
- package/after-effects-cep-plugin/index.html +1 -0
- package/after-effects-cep-plugin/main.js +17 -2
- package/artifacts/MCPBridgeCEP.zxp +0 -0
- package/cep-plugin/CSXS/manifest.xml +3 -3
- package/cep-plugin/bridge-directory-security.cjs +233 -0
- package/cep-plugin/index.html +3 -2
- package/cep-plugin/main.js +28 -9
- package/cep-plugin/updater.cjs +1 -1
- package/dist/ai/reaction-shorts.d.ts +126 -0
- package/dist/ai/reaction-shorts.js +473 -0
- package/dist/ai/short-form-candidates.js +3 -1
- package/dist/ai/word-timeline.d.ts +1 -1
- package/dist/ai/word-timeline.js +11 -2
- package/dist/bridge/file-bridge.d.ts +31 -0
- package/dist/bridge/file-bridge.js +157 -14
- package/dist/bridge/uxp-websocket-bridge.js +8 -1
- package/dist/diagnostics.d.ts +5 -1
- package/dist/diagnostics.js +11 -5
- package/dist/homepage-experiment.d.ts +2 -0
- package/dist/homepage-experiment.js +21 -0
- package/dist/http-security.js +3 -1
- package/dist/http-server.js +49 -12
- package/dist/index.js +8 -3
- package/dist/landing-documents.d.ts +38 -0
- package/dist/landing-documents.js +142 -0
- package/dist/resources/adobe-uxp-coverage.json +2 -2
- package/dist/security/capabilities.js +3 -0
- package/dist/server.js +2 -0
- package/dist/tools/caption-authoring.d.ts +37 -1
- package/dist/tools/caption-authoring.js +58 -1
- package/dist/tools/export.js +27 -7
- package/dist/tools/keyframes.d.ts +2 -1
- package/dist/tools/keyframes.js +9 -6
- package/dist/tools/markers.js +12 -3
- package/dist/tools/media-watch.d.ts +62 -6
- package/dist/tools/media-watch.js +181 -33
- package/dist/tools/project.js +19 -5
- package/dist/tools/reaction-shorts.d.ts +277 -0
- package/dist/tools/reaction-shorts.js +101 -0
- package/dist/tools/recovery.d.ts +5 -1
- package/dist/tools/recovery.js +141 -29
- package/dist/tools/rhythm-plans.d.ts +1 -1
- package/dist/tools/shorts-intelligence.d.ts +2 -2
- package/dist/tools/speaker-layout.d.ts +2 -2
- package/dist/tools/transcript-word-edits.d.ts +4 -4
- package/dist/tools/uxp-advanced-workflows.js +1 -1
- package/dist/tools/uxp-workflows.js +1 -1
- package/dist/tools/uxp.d.ts +126 -3
- package/dist/tools/uxp.js +106 -14
- package/dist/workflows/agent-instructions.js +1 -0
- package/dist/workflows/catalog.d.ts +7 -2
- package/dist/workflows/catalog.js +6 -0
- package/dist/workflows/tool-metadata.js +3 -0
- package/dist/workflows/tool-packs.js +6 -0
- package/docs/supported-actions.md +18 -12
- package/package.json +5 -4
- package/public-product-manifest.json +7 -7
- package/scripts/generate-marketing-reference.mjs +2 -0
- package/scripts/verify-npm-package.mjs +2 -0
- package/uxp-plugin/advanced-workflows.cjs +22 -9
- package/uxp-plugin/commands.cjs +53 -4
- package/uxp-plugin/manifest.json +1 -1
- package/uxp-plugin/transcript.cjs +11 -1
- package/uxp-plugin/workflows.cjs +59 -24
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,51 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.16.0] - 2026-09-16
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Local reaction-Shorts planners: `plan_reaction_captions` stacks overlapping
|
|
14
|
+
speaker colors without guessing unknown speakers, `plan_short_subscribe_cta`
|
|
15
|
+
places a mid-video subscribe overlay, and `plan_short_export_folder` names a
|
|
16
|
+
series folder to create before export. Different labeled speakers may now
|
|
17
|
+
overlap in a word timeline. (#543)
|
|
18
|
+
- Guarded Speech-to-Text start, caption style guidance, and UXP transcription
|
|
19
|
+
language options. (#513, #515)
|
|
20
|
+
- Install collision-defense identity output for `--version` and `--doctor`, plus
|
|
21
|
+
a verified npm installation guide and package-identity checks. (#516, #533,
|
|
22
|
+
#534)
|
|
23
|
+
- Recorded workflow evidence and a source-linked Premiere MCP comparison. (#532)
|
|
24
|
+
- Interactive cinematic landing: 3D timeline, draggable trims, a program
|
|
25
|
+
monitor, public-page styling, studio install/Name-check callout, Ahrefs
|
|
26
|
+
verification, and a female-narration advertisement. (#517–#521, #524–#531,
|
|
27
|
+
#546)
|
|
28
|
+
- Bounded Fly landing events now go to PostHog as well as Google Analytics,
|
|
29
|
+
without autocapture, session replay, or person profiles. (#522)
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Host-reported tool crashes and false verification for clip markers, MOGRT JSON
|
|
34
|
+
values, first transcript import, FCP XML destination checks, and UXP tree
|
|
35
|
+
IDs. (#544)
|
|
36
|
+
- Security audit findings: HTTP and filesystem work is bounded, and bridge
|
|
37
|
+
directories fail closed when ownership, symlinks, or ancestor replacement
|
|
38
|
+
rights are untrusted. (#545)
|
|
39
|
+
- AME handoff tools require a saved project so Same as Project preset
|
|
40
|
+
destinations cannot resolve against a scratch folder. (#535)
|
|
41
|
+
- Homepage overflow and cinematic timeline replay after the landing merge.
|
|
42
|
+
(#523)
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- Creating a UXP preset sequence now requires explicit confirmation. (#542)
|
|
47
|
+
- The Claude Desktop bundle no longer requires a UXP token for CEP-only setups.
|
|
48
|
+
- Homepage experiment assignment is exposed at first paint. (#514)
|
|
49
|
+
- Removed the throwaway `uxp-spike` directory. (#520)
|
|
50
|
+
|
|
51
|
+
Automated checks do not establish licensed Premiere or After Effects playback or
|
|
52
|
+
rendered-output verification.
|
|
53
|
+
|
|
9
54
|
## [1.15.2] - 2026-09-14
|
|
10
55
|
|
|
11
56
|
### Fixed
|
package/README.md
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
|
|
11
11
|
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
12
|
|
|
13
|
-
[Website](https://premiere-pro-mcp.com/) · [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/)
|
|
13
|
+
[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
14
|
|
|
15
|
-
Development source:
|
|
15
|
+
Development source: 373 core tools across 54 modules, 4 resources, and 18 guided workflows. A connected UXP host adds 95 capability-gated tools.
|
|
16
16
|
|
|
17
17
|
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
18
|
|
|
@@ -104,13 +104,13 @@ and repository before configuring a client. The new
|
|
|
104
104
|
VS Code, or Codex settings that point directly to this installation. It is a
|
|
105
105
|
feature included in v1.15.1 and later.
|
|
106
106
|
|
|
107
|
-
The current source exposes
|
|
107
|
+
The current source exposes 373 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 95 documented, capability-gated tools without replacing the production CEP bridge.
|
|
108
108
|
|
|
109
109
|
<a id="latest-release"></a>
|
|
110
110
|
|
|
111
|
-
### Latest release: 1.
|
|
111
|
+
### Latest release: 1.16.0
|
|
112
112
|
|
|
113
|
-
The published v1.15.
|
|
113
|
+
The published v1.15.2 npm artifact contains **369 core tools**, 367 in its default profile,
|
|
114
114
|
and 460 with a compatible UXP connection. The development catalog above can include
|
|
115
115
|
unreleased work. See the [versioned facts and package provenance](https://premiere-pro-mcp.com/facts/).
|
|
116
116
|
|
|
@@ -160,7 +160,7 @@ if the connection is unavailable.
|
|
|
160
160
|
local Premiere processes. See the generated [supported action catalog](docs/supported-actions.md)
|
|
161
161
|
for individual capability and verification contracts.
|
|
162
162
|
|
|
163
|
-
See the [v1.
|
|
163
|
+
See the [v1.16.0 release notes](https://github.com/leancoderkavy/premiere-pro-mcp/releases/tag/v1.16.0)
|
|
164
164
|
for complete details. Live installation in Premiere Pro still requires host verification.
|
|
165
165
|
|
|
166
166
|
### Current MCP protocol support
|
|
@@ -205,11 +205,33 @@ their bins, media rules, and organization rules before a facility uses one.
|
|
|
205
205
|
|
|
206
206
|
## Quick Start
|
|
207
207
|
|
|
208
|
+
> ### Install the published package (verify the name)
|
|
209
|
+
>
|
|
210
|
+
> ```bash
|
|
211
|
+
> npm i -g premiere-pro-mcp@1.16.0
|
|
212
|
+
> ```
|
|
213
|
+
>
|
|
214
|
+
> 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:
|
|
215
|
+
>
|
|
216
|
+
> | Check | Expected |
|
|
217
|
+
> | --- | --- |
|
|
218
|
+
> | Package name | `premiere-pro-mcp` (not `adobe-premiere-pro-mcp`) |
|
|
219
|
+
> | Version | `1.16.0` |
|
|
220
|
+
> | Homepage / repo | https://premiere-pro-mcp.com/ · https://github.com/leancoderkavy/premiere-pro-mcp |
|
|
221
|
+
>
|
|
222
|
+
> ```bash
|
|
223
|
+
> npm list -g premiere-pro-mcp
|
|
224
|
+
> premiere-pro-mcp --version
|
|
225
|
+
> npm view premiere-pro-mcp homepage repository.url
|
|
226
|
+
> ```
|
|
227
|
+
>
|
|
228
|
+
> Then continue with `--install-cep`, `--doctor`, and the read-only `verify_premiere_connection` prompt.
|
|
229
|
+
|
|
208
230
|
### Easiest supported path: Claude Desktop
|
|
209
231
|
|
|
210
|
-
1. Download the current [Claude Desktop bundle (`.mcpb`)](https://github.com/leancoderkavy/premiere-pro-mcp/releases/download/v1.
|
|
232
|
+
1. Download the current [Claude Desktop bundle (`.mcpb`)](https://github.com/leancoderkavy/premiere-pro-mcp/releases/download/v1.16.0/premiere-pro-mcp-1.16.0.mcpb).
|
|
211
233
|
2. In Claude Desktop, open **Settings > Extensions > Advanced settings > Install Extension**, select the downloaded bundle, and restart Claude Desktop.
|
|
212
|
-
3. Download the separate [signed Premiere connector (`.zxp`)](https://github.com/leancoderkavy/premiere-pro-mcp/releases/download/v1.
|
|
234
|
+
3. Download the separate [signed Premiere connector (`.zxp`)](https://github.com/leancoderkavy/premiere-pro-mcp/releases/download/v1.16.0/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.
|
|
213
235
|
4. Restart Premiere, open a project, then open **Window > Extensions > MCP for Adobe Premiere Pro**.
|
|
214
236
|
5. In Claude, enter: `Safely check my Premiere connection with verify_premiere_connection. Make no changes.`
|
|
215
237
|
|
|
@@ -728,7 +750,7 @@ The MCP server can accept a local UXP panel connection and invoke the UXP comman
|
|
|
728
750
|
PREMIERE_UXP_TOKEN="replace-with-a-long-random-secret" premiere-pro-mcp
|
|
729
751
|
```
|
|
730
752
|
|
|
731
|
-
|
|
753
|
+
Generate that secret yourself and enter the same value in the UXP panel (**Window → UXP Plugins**, not the CEP Connector). CEP-only setups do not use a token. 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.
|
|
732
754
|
|
|
733
755
|
When enabled, MCP discovery includes 91 capability-gated UXP additions. The first expansion covers effects, deterministic timeline selection, selection batches, scene detection, proxy/ingest, relink, metadata, color conformance, read-only Premiere/After Effects environment inspection, Source Monitor audition, storage, and least-privilege workspace access. The second adds Project-panel selection, marker CRUD, single-transaction undoable beat-grid marker application, bin organization, sequence settings, imports, typed effect parameters/keyframes, track-item transforms, atomic J/L split edits, SequenceEditor timeline edits, sequence lifecycle, and AME encoding. The third wave begins with a redacted event journal, conservative AME terminal receipts, explicit host-readiness gates, safe multi-project sessions, lease-based growing-media control, namespaced workflow checkpoints, bounded media-health maintenance, caption-aware track mute state, transactional source trim/framing, guarded sequence-range updates, guarded source-media start timing, and guarded source-media frame-rate/pixel-aspect overrides documented in [the third-wave workflow matrix](docs/third-wave-uxp-workflows.md). The bounded migration surface also includes a non-ripple selected-item lift, native video-transition listing and guarded transactions, native active and explicit-GUID sequence timing inspection, opt-in installed-MOGRT directory inspection without filesystem enumeration, bounded native timeline structure inspection with opt-in source IDs, classification, and broad content category, guarded sequence display-format updates, a capped native Project-panel tree, a double-read Project-panel insertion-bin snapshot, guarded empty/default sequence creation, bounded native Project-panel schema/item-column metadata inspection, guarded direct Project-panel metadata replacements with exact state/readback guards, guarded typed Project metadata schema-field creation with non-field-level readback, guarded direct updates to three named application preferences, guarded transcript JSON replacement for one exact source clip, direct active-track-item identity readback, guarded source-only slips, guarded contiguous three-item slides, guarded append-only timeline duplicates, guarded contiguous same-track ripple deletes, guarded source-project-item color labels resolved from one timeline coordinate, guarded explicit-sequence preview-frame rectangle updates, explicit opt-in source-media provenance paths with bounded double resolution, bounded source-proxy readiness with an explicit attached-path disclosure, guarded static effect-parameter PointF x/y updates, bounded effect-parameter descriptor catalogs, bounded project/sequence Object Mask audits, native FrameRate/TickTime frame-alignment inspection with caller-owned inputs and tick readback, and native TickTime arithmetic over caller-owned canonical tick strings; it does not claim direct empty-track create/delete, global redo support, playback proof, Object Mask counts or visual validity, an atomic Project-panel metadata compare-and-set, app-preference Undo, installed-template availability or compatibility, imported transcript persistence, override-presence/clear semantics, path existence, source lineage or rights, proxy compatibility, general or keyframed effect-parameter value readback, rendered-frame correctness, linked-item sync, or licensed-host validation. A separate [hybrid benchmark gate](docs/uxp-hybrid-benchmark.md) keeps native acceleration disabled until reproducible cross-platform evidence exists. See also [the first stable workflow matrix](docs/uxp-stable-workflows.md), [the next-ten workflow matrix](docs/uxp-next-ten-workflows.md), [the guarded-slip workflow notes](docs/uxp-slip-workflows.md), [the guarded-slide workflow notes](docs/uxp-slide-workflows.md), [the guarded-duplicate workflow notes](docs/uxp-clone-workflows.md), and [the guarded-ripple-delete workflow notes](docs/uxp-ripple-delete-workflows.md). 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.
|
|
734
756
|
|
|
@@ -837,11 +859,11 @@ reports revision-bound notes, VFX state, change impact and turnover exceptions.
|
|
|
837
859
|
It performs local inspection; host edits and exports use separate guarded tools.
|
|
838
860
|
See [usage, example and remaining execution adapters](docs/film-editorial-workflows.md).
|
|
839
861
|
|
|
840
|
-
## Tools (
|
|
862
|
+
## Tools (373 core total; 371 under the default profile; 466 with a connected UXP bridge)
|
|
841
863
|
|
|
842
864
|
The [complete supported-actions catalog](docs/supported-actions.md) lists every
|
|
843
865
|
registered core tool, the two tools restricted behind explicit `unsafe-script`
|
|
844
|
-
authority, and all
|
|
866
|
+
authority, and all 95 authenticated UXP additions with their current action or mode
|
|
845
867
|
values. It is generated from the same MCP registration surface returned to clients;
|
|
846
868
|
the tables below are a shorter workflow-oriented overview.
|
|
847
869
|
|
|
@@ -1058,6 +1080,14 @@ than presenting UI-only operations as available tools.
|
|
|
1058
1080
|
| `build_caption_artifact` | Word-grouped SRT/VTT captions with karaoke timestamps, emphasis markup, speaker prefixes, and style presets, written inside an approved workspace |
|
|
1059
1081
|
| `check_caption_safe_zone` | Overlap check for caption and graphic rectangles against approximate platform UI zones, with a suggested clear position |
|
|
1060
1082
|
|
|
1083
|
+
### Reaction Shorts (3)
|
|
1084
|
+
|
|
1085
|
+
| Tool | Description |
|
|
1086
|
+
| :--- | :---------- |
|
|
1087
|
+
| `plan_reaction_captions` | Stacked speaker-colored caption plan from a word timeline and explicit palette; flash words merge, overlaps stack, unknown speakers stay uncolored |
|
|
1088
|
+
| `plan_short_subscribe_cta` | Brief subscribe overlay placed about two-thirds through a Short, after an optional hook, with brand-safe styling notes |
|
|
1089
|
+
| `plan_short_export_folder` | Series-named export path under an approved Shorts root; create the folder when it is missing and keep Cafe and Watch Club assets separate |
|
|
1090
|
+
|
|
1061
1091
|
### Transcript Word Edits (4)
|
|
1062
1092
|
|
|
1063
1093
|
| Tool | Description |
|
|
@@ -1233,6 +1263,10 @@ user/device authorization are implemented.
|
|
|
1233
1263
|
| `MCP_MAX_REQUESTS_PER_SOCKET` | Requests permitted on one keep-alive socket | `100` |
|
|
1234
1264
|
| `MCP_MAX_CONCURRENT_REQUESTS` | In-flight authenticated MCP request ceiling | `8` |
|
|
1235
1265
|
| `MCP_MAX_CONCURRENT_STREAMS` | Open authenticated SSE stream ceiling; isolated from operation capacity | `32` |
|
|
1266
|
+
| `MCP_MAX_CONCURRENT_LANDING_DOCUMENTS` | Concurrent HTML reads, nonce injection and asynchronous compression; excess work returns `503` | `4` |
|
|
1267
|
+
| `MCP_LANDING_MAX_HTML_BYTES` | Maximum source HTML document bytes | `4194304` (4 MiB) |
|
|
1268
|
+
| `MCP_LANDING_HTML_CACHE_BYTES` | Source HTML cache memory budget; must be at least the document byte limit | `16777216` (16 MiB) |
|
|
1269
|
+
| `PREMIERE_MCP_PROJECT_BACKUP_MAX_BYTES` | Positive integer byte budget for one project backup | `2147483648` (2 GiB) |
|
|
1236
1270
|
| `MCP_RATE_LIMIT_PER_MINUTE` | Per-credential token-bucket refill rate | `120` |
|
|
1237
1271
|
| `MCP_RATE_LIMIT_BURST` | Per-credential short burst allowance | `30` |
|
|
1238
1272
|
| `MCP_MAX_RATE_LIMIT_KEYS` | In-memory rate-limit identity ceiling | `2048` |
|
|
@@ -1265,7 +1299,7 @@ premiere-pro-mcp/
|
|
|
1265
1299
|
├── src/
|
|
1266
1300
|
│ ├── index.ts # Entry point — stdio transport setup
|
|
1267
1301
|
│ ├── http-server.ts # Entry point — HTTP/SSE transport (Fly.io / remote)
|
|
1268
|
-
│ ├── server.ts # MCP server — registers
|
|
1302
|
+
│ ├── server.ts # MCP server — registers 373 tools, filtered by authority profile
|
|
1269
1303
|
│ ├── bridge/
|
|
1270
1304
|
│ │ ├── file-bridge.ts # File-based IPC (write .jsx, poll .json)
|
|
1271
1305
|
│ │ └── script-builder.ts # ExtendScript generator with ES3 helpers
|
|
@@ -1350,9 +1384,36 @@ by default. Enable them only by setting
|
|
|
1350
1384
|
upstream rate limit and request-size limit too; process-local counters do not protect a multi-machine deployment.
|
|
1351
1385
|
- **The landing CSP uses a per-response nonce for scripts**, and its static assets use explicit cache policies.
|
|
1352
1386
|
Keep the server in front of the exported landing so those controls are not bypassed by a separate static host.
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1387
|
+
Source HTML is cached as immutable build output; restart the HTTP process after replacing
|
|
1388
|
+
an export. Each response still gets a fresh nonce. HEAD requests skip body rendering.
|
|
1389
|
+
- **Media scans yield between asynchronous filesystem operations** and bound total traversal:
|
|
1390
|
+
25,000 entries, 5,000 matching files, 2,000 directories, depth 32, a 1,000-directory
|
|
1391
|
+
pending queue, and a cooperative five-second budget. A stalled OS call can exceed that
|
|
1392
|
+
elapsed budget. Inspect `scan_incomplete` and `scan_limit_reasons` before treating a
|
|
1393
|
+
watch baseline as complete; import previews also report `incomplete`.
|
|
1394
|
+
- **Project backups stream their copy and checksum work** with a configurable byte budget.
|
|
1395
|
+
Only one backup runs per process; concurrent requests fail promptly. Failed or cancelled
|
|
1396
|
+
copies are removed without replacing an existing backup.
|
|
1397
|
+
- **The Docker runner uses the unprivileged `node` user (UID 1000).** Custom bridge or
|
|
1398
|
+
context volume mounts must be writable by that user and private to the operator.
|
|
1399
|
+
The default context directory is under `/home/node/.local/state/premiere-pro-mcp`.
|
|
1400
|
+
- Dependency audits run in CI for both lockfiles. Keep local `.env*`, `.npmrc`, and
|
|
1401
|
+
private key/certificate files out of commits and Docker build contexts; only
|
|
1402
|
+
placeholder `.env.example` and `.env.template` files are eligible for Git tracking.
|
|
1403
|
+
- Both CEP panels validate the bridge directory before writing a heartbeat or polling
|
|
1404
|
+
commands. Symlinks and directories owned by another user are refused. On POSIX,
|
|
1405
|
+
directories writable by group/other users are refused by both the server and panels;
|
|
1406
|
+
tightening permissions alone cannot make previously staged commands trustworthy.
|
|
1407
|
+
On Windows, only the current user, SYSTEM and Administrators may have write access,
|
|
1408
|
+
including inherited grants. ACL inspection failures also stop startup. Ancestors must
|
|
1409
|
+
also prevent other users from replacing the bridge path; a private child inside a
|
|
1410
|
+
broadly writable parent is insufficient (POSIX sticky temp directories are supported).
|
|
1411
|
+
Choose a new directory under a private user-owned location if the connector rejects
|
|
1412
|
+
a shared temp folder, and configure the same path on the server and panel. Do not
|
|
1413
|
+
copy pending commands from the rejected folder into the new one.
|
|
1414
|
+
Windows checks invoke PowerShell synchronously before publishing commands, with a
|
|
1415
|
+
five-second timeout and 64 KiB output cap; this adds command latency rather than
|
|
1416
|
+
caching a permission decision that could become stale.
|
|
1356
1417
|
- There is a 500 KB script size limit, and a small regex check that rejects `eval()`,
|
|
1357
1418
|
`new Function()`, and `System.callSystem()` in tool-generated scripts. **This is a guard
|
|
1358
1419
|
rail, not a sandbox** — it is trivially bypassable and is not a security boundary. Do not
|
|
@@ -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.
|
|
2
|
+
<ExtensionManifest Version="7.0" ExtensionBundleId="com.mcp.aftereffects.bridge" ExtensionBundleVersion="1.16.0" ExtensionBundleName="MCP for Adobe After Effects">
|
|
3
3
|
<ExtensionList>
|
|
4
|
-
<Extension Id="com.mcp.aftereffects.bridge.panel" Version="1.
|
|
4
|
+
<Extension Id="com.mcp.aftereffects.bridge.panel" Version="1.16.0"/>
|
|
5
5
|
</ExtensionList>
|
|
6
6
|
<ExecutionEnvironment>
|
|
7
7
|
<HostList>
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
(function (root, factory) {
|
|
2
|
+
var api = factory();
|
|
3
|
+
if (typeof module !== "undefined" && module.exports) module.exports = api;
|
|
4
|
+
root.MCPBridgeDirectorySecurity = api;
|
|
5
|
+
}(typeof globalThis !== "undefined" ? globalThis : this, function () {
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
var WINDOWS_ACL_SCRIPT = [
|
|
9
|
+
'$ErrorActionPreference = "Stop"',
|
|
10
|
+
'$path = [Environment]::GetEnvironmentVariable("PREMIERE_MCP_ACL_PATH", "Process")',
|
|
11
|
+
'if ([string]::IsNullOrWhiteSpace($path)) { throw "Bridge path environment variable is missing" }',
|
|
12
|
+
'$acl = [System.IO.Directory]::GetAccessControl($path)',
|
|
13
|
+
'$current = [System.Security.Principal.WindowsIdentity]::GetCurrent().User.Value',
|
|
14
|
+
'$initialize = [Environment]::GetEnvironmentVariable("PREMIERE_MCP_ACL_INITIALIZE", "Process") -eq "1"',
|
|
15
|
+
'$trustedAncestors = @($current, "S-1-5-18", "S-1-5-32-544", "S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464")',
|
|
16
|
+
'$replacement = [System.Security.AccessControl.FileSystemRights]::DeleteSubdirectoriesAndFiles -bor [System.Security.AccessControl.FileSystemRights]::Delete -bor [System.Security.AccessControl.FileSystemRights]::ChangePermissions -bor [System.Security.AccessControl.FileSystemRights]::TakeOwnership',
|
|
17
|
+
'$unsafeAncestors = @()',
|
|
18
|
+
'$ancestor = (New-Object System.IO.DirectoryInfo($path)).Parent',
|
|
19
|
+
'while ($null -ne $ancestor) {',
|
|
20
|
+
' $ancestorAttributes = [System.IO.File]::GetAttributes($ancestor.FullName)',
|
|
21
|
+
' if (($ancestorAttributes -band [System.IO.FileAttributes]::ReparsePoint) -ne 0) { $unsafeAncestors += [pscustomobject]@{ sid = ""; path = $ancestor.FullName; reason = "reparse_point" } }',
|
|
22
|
+
' $ancestorAcl = [System.IO.Directory]::GetAccessControl($ancestor.FullName)',
|
|
23
|
+
' $ancestorOwner = $ancestorAcl.GetOwner([System.Security.Principal.SecurityIdentifier]).Value',
|
|
24
|
+
' if ($ancestorOwner -notin $trustedAncestors) { $unsafeAncestors += [pscustomobject]@{ sid = $ancestorOwner; path = $ancestor.FullName; reason = "owner" } }',
|
|
25
|
+
' $unsafeAncestors += @($ancestorAcl.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier]) | Where-Object {',
|
|
26
|
+
' $_.AccessControlType -eq [System.Security.AccessControl.AccessControlType]::Allow -and (($_.PropagationFlags -band [System.Security.AccessControl.PropagationFlags]::InheritOnly) -eq 0) -and (($_.FileSystemRights -band $replacement) -ne 0) -and $_.IdentityReference.Value -notin $trustedAncestors',
|
|
27
|
+
' } | ForEach-Object { [pscustomobject]@{ sid = $_.IdentityReference.Value; path = $ancestor.FullName; reason = "replacement_rights" } })',
|
|
28
|
+
' $ancestor = $ancestor.Parent',
|
|
29
|
+
'}',
|
|
30
|
+
'if ($initialize) {',
|
|
31
|
+
' if ($unsafeAncestors.Count -ne 0) { throw "Bridge directory ancestry is unsafe" }',
|
|
32
|
+
' $acl.SetAccessRuleProtection($true, $false)',
|
|
33
|
+
' $inheritance = [System.Security.AccessControl.InheritanceFlags]::ContainerInherit -bor [System.Security.AccessControl.InheritanceFlags]::ObjectInherit',
|
|
34
|
+
' $propagation = [System.Security.AccessControl.PropagationFlags]::None',
|
|
35
|
+
' foreach ($sid in @($current, "S-1-5-18", "S-1-5-32-544")) {',
|
|
36
|
+
' $identity = New-Object System.Security.Principal.SecurityIdentifier($sid)',
|
|
37
|
+
' $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($identity, [System.Security.AccessControl.FileSystemRights]::FullControl, $inheritance, $propagation, [System.Security.AccessControl.AccessControlType]::Allow)',
|
|
38
|
+
' [void]$acl.AddAccessRule($rule)',
|
|
39
|
+
' }',
|
|
40
|
+
' [System.IO.Directory]::SetAccessControl($path, $acl)',
|
|
41
|
+
' $acl = [System.IO.Directory]::GetAccessControl($path)',
|
|
42
|
+
'}',
|
|
43
|
+
'$owner = $acl.GetOwner([System.Security.Principal.SecurityIdentifier]).Value',
|
|
44
|
+
'$mutating = [System.Security.AccessControl.FileSystemRights]::WriteData -bor [System.Security.AccessControl.FileSystemRights]::AppendData -bor [System.Security.AccessControl.FileSystemRights]::WriteExtendedAttributes -bor [System.Security.AccessControl.FileSystemRights]::DeleteSubdirectoriesAndFiles -bor [System.Security.AccessControl.FileSystemRights]::WriteAttributes -bor [System.Security.AccessControl.FileSystemRights]::Delete -bor [System.Security.AccessControl.FileSystemRights]::ChangePermissions -bor [System.Security.AccessControl.FileSystemRights]::TakeOwnership',
|
|
45
|
+
'$trusted = @($current, "S-1-5-18", "S-1-5-32-544")',
|
|
46
|
+
'$unsafe = @($acl.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier]) | Where-Object {',
|
|
47
|
+
' $_.AccessControlType -eq [System.Security.AccessControl.AccessControlType]::Allow -and (($_.FileSystemRights -band $mutating) -ne 0)',
|
|
48
|
+
'} | ForEach-Object {',
|
|
49
|
+
' if ($_.IdentityReference.Value -notin $trusted) { [pscustomobject]@{ sid = $_.IdentityReference.Value; isInherited = [bool]$_.IsInherited } }',
|
|
50
|
+
'})',
|
|
51
|
+
'[pscustomobject]@{ ownerSid = $owner; currentUserSid = $current; unsafeWriteAces = $unsafe; unsafeAncestorEntries = $unsafeAncestors } | ConvertTo-Json -Compress',
|
|
52
|
+
].join("\n");
|
|
53
|
+
|
|
54
|
+
function createBridgeDirectorySecurity(runtime) {
|
|
55
|
+
if (!runtime || !runtime.fs || !runtime.path) {
|
|
56
|
+
throw new Error("Bridge directory security requires filesystem and path runtimes");
|
|
57
|
+
}
|
|
58
|
+
var fs = runtime.fs;
|
|
59
|
+
var path = runtime.path;
|
|
60
|
+
var platform = runtime.platform || (runtime.process && runtime.process.platform) || "";
|
|
61
|
+
|
|
62
|
+
function inspectWindowsAcl(directory, initialize) {
|
|
63
|
+
if (typeof runtime.inspectWindowsAcl === "function") {
|
|
64
|
+
return runtime.inspectWindowsAcl(directory, initialize);
|
|
65
|
+
}
|
|
66
|
+
if (!runtime.childProcess || typeof runtime.childProcess.execFileSync !== "function") {
|
|
67
|
+
throw new Error("Windows ACL inspection is unavailable");
|
|
68
|
+
}
|
|
69
|
+
if (!runtime.Buffer || typeof runtime.Buffer.from !== "function") {
|
|
70
|
+
throw new Error("Windows ACL command encoding is unavailable");
|
|
71
|
+
}
|
|
72
|
+
var commandEnvironment = {};
|
|
73
|
+
var sourceEnvironment = runtime.process && runtime.process.env ? runtime.process.env : {};
|
|
74
|
+
Object.keys(sourceEnvironment).forEach(function (key) {
|
|
75
|
+
commandEnvironment[key] = sourceEnvironment[key];
|
|
76
|
+
});
|
|
77
|
+
commandEnvironment.PREMIERE_MCP_ACL_PATH = directory;
|
|
78
|
+
commandEnvironment.PREMIERE_MCP_ACL_INITIALIZE = initialize ? "1" : "0";
|
|
79
|
+
var encodedCommand = runtime.Buffer.from(WINDOWS_ACL_SCRIPT, "utf16le").toString("base64");
|
|
80
|
+
var raw = runtime.childProcess.execFileSync(
|
|
81
|
+
"powershell.exe",
|
|
82
|
+
["-NoProfile", "-NonInteractive", "-EncodedCommand", encodedCommand],
|
|
83
|
+
{
|
|
84
|
+
encoding: "utf8",
|
|
85
|
+
windowsHide: true,
|
|
86
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
87
|
+
timeout: 5000,
|
|
88
|
+
maxBuffer: 64 * 1024,
|
|
89
|
+
env: commandEnvironment,
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
return JSON.parse(String(raw).trim());
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function currentUid() {
|
|
96
|
+
if (typeof runtime.currentUid === "number") return runtime.currentUid;
|
|
97
|
+
if (runtime.process && typeof runtime.process.getuid === "function") {
|
|
98
|
+
return runtime.process.getuid();
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function validateDirectoryStatus(directory, entry) {
|
|
104
|
+
if (!entry || typeof entry.isSymbolicLink !== "function" || typeof entry.isDirectory !== "function") {
|
|
105
|
+
throw new Error("Bridge directory metadata could not be verified");
|
|
106
|
+
}
|
|
107
|
+
if (entry.isSymbolicLink()) {
|
|
108
|
+
throw new Error("Bridge directory must not be a symbolic link or junction: " + directory);
|
|
109
|
+
}
|
|
110
|
+
if (!entry.isDirectory()) {
|
|
111
|
+
throw new Error("Bridge path is not a directory: " + directory);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function validatePosixAncestors(directory, uid) {
|
|
116
|
+
if (typeof path.dirname !== "function") {
|
|
117
|
+
throw new Error("Bridge directory ancestry could not be verified");
|
|
118
|
+
}
|
|
119
|
+
var paths = [directory];
|
|
120
|
+
if (typeof fs.realpathSync === "function") {
|
|
121
|
+
var canonical = fs.realpathSync(directory);
|
|
122
|
+
if (canonical !== directory) paths.push(canonical);
|
|
123
|
+
}
|
|
124
|
+
var checked = {};
|
|
125
|
+
paths.forEach(function (candidate) {
|
|
126
|
+
var ancestor = path.dirname(candidate);
|
|
127
|
+
while (ancestor && !checked[ancestor]) {
|
|
128
|
+
checked[ancestor] = true;
|
|
129
|
+
var entry = fs.lstatSync(ancestor);
|
|
130
|
+
if (entry.uid !== 0 && entry.uid !== uid) {
|
|
131
|
+
throw new Error("Bridge path has an untrusted replaceable ancestor " + ancestor);
|
|
132
|
+
}
|
|
133
|
+
if (!entry.isSymbolicLink()) {
|
|
134
|
+
if (!entry.isDirectory()) throw new Error("Bridge ancestor is not a directory: " + ancestor);
|
|
135
|
+
var groupReplaceable = (entry.mode & 0o030) === 0o030;
|
|
136
|
+
var otherReplaceable = (entry.mode & 0o003) === 0o003;
|
|
137
|
+
var sticky = (entry.mode & 0o1000) !== 0;
|
|
138
|
+
if ((groupReplaceable || otherReplaceable) && !sticky) {
|
|
139
|
+
throw new Error("Bridge path has a replaceable ancestor " + ancestor);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
var parent = path.dirname(ancestor);
|
|
143
|
+
if (parent === ancestor) break;
|
|
144
|
+
ancestor = parent;
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function ensurePrivateBridgeDirectory(directory) {
|
|
150
|
+
if (typeof directory !== "string" || !directory.trim()) {
|
|
151
|
+
throw new Error("Bridge directory must be a non-empty path");
|
|
152
|
+
}
|
|
153
|
+
var resolved = path.resolve(directory.trim());
|
|
154
|
+
var createdPath = fs.mkdirSync(resolved, { recursive: true, mode: 0o700 });
|
|
155
|
+
var newlyCreated = typeof createdPath === "string";
|
|
156
|
+
|
|
157
|
+
var entry = fs.lstatSync(resolved);
|
|
158
|
+
validateDirectoryStatus(resolved, entry);
|
|
159
|
+
|
|
160
|
+
if (platform === "win32") {
|
|
161
|
+
var acl;
|
|
162
|
+
try {
|
|
163
|
+
acl = inspectWindowsAcl(resolved, newlyCreated);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
"Could not verify the Windows ACL for bridge directory " + resolved + ": " +
|
|
167
|
+
(error && error.message ? error.message : String(error))
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
if (!acl || !acl.ownerSid || !acl.currentUserSid || acl.ownerSid !== acl.currentUserSid) {
|
|
171
|
+
throw new Error("Bridge directory is not owned by the current Windows user: " + resolved);
|
|
172
|
+
}
|
|
173
|
+
var unsafe = Array.isArray(acl.unsafeWriteAces)
|
|
174
|
+
? acl.unsafeWriteAces
|
|
175
|
+
: acl.unsafeWriteAces
|
|
176
|
+
? [acl.unsafeWriteAces]
|
|
177
|
+
: [];
|
|
178
|
+
if (unsafe.length > 0) {
|
|
179
|
+
var unsafeSids = unsafe.map(function (ace) { return ace && ace.sid ? ace.sid : "unknown"; });
|
|
180
|
+
throw new Error(
|
|
181
|
+
"Bridge directory grants write access to untrusted identities (" + unsafeSids.join(", ") + "): " + resolved
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
var unsafeAncestors = Array.isArray(acl.unsafeAncestorEntries)
|
|
185
|
+
? acl.unsafeAncestorEntries
|
|
186
|
+
: acl.unsafeAncestorEntries
|
|
187
|
+
? [acl.unsafeAncestorEntries]
|
|
188
|
+
: [];
|
|
189
|
+
if (unsafeAncestors.length > 0) {
|
|
190
|
+
throw new Error(
|
|
191
|
+
"Bridge path has a replaceable ancestor " + unsafeAncestors[0].path +
|
|
192
|
+
" (" + unsafeAncestors[0].sid + ")"
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
if (newlyCreated && fs.readdirSync(resolved).length > 0) {
|
|
196
|
+
throw new Error("Bridge directory rejected because unexpected contents appeared during creation: " + resolved);
|
|
197
|
+
}
|
|
198
|
+
return resolved;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
var uid = currentUid();
|
|
202
|
+
if (uid === null || typeof entry.uid !== "number") {
|
|
203
|
+
throw new Error("Could not verify bridge directory ownership on platform " + platform);
|
|
204
|
+
}
|
|
205
|
+
if (entry.uid !== uid) {
|
|
206
|
+
throw new Error("Bridge directory is owned by another user: " + resolved);
|
|
207
|
+
}
|
|
208
|
+
if (!newlyCreated && (entry.mode & 0o022) !== 0) {
|
|
209
|
+
throw new Error("Bridge directory was writable by other users before validation: " + resolved);
|
|
210
|
+
}
|
|
211
|
+
validatePosixAncestors(resolved, uid);
|
|
212
|
+
if ((entry.mode & 0o077) !== 0) {
|
|
213
|
+
fs.chmodSync(resolved, 0o700);
|
|
214
|
+
entry = fs.lstatSync(resolved);
|
|
215
|
+
validateDirectoryStatus(resolved, entry);
|
|
216
|
+
if (entry.uid !== uid) {
|
|
217
|
+
throw new Error("Bridge directory ownership changed during validation: " + resolved);
|
|
218
|
+
}
|
|
219
|
+
if ((entry.mode & 0o077) !== 0) {
|
|
220
|
+
throw new Error("Bridge directory permissions are not owner-only: " + resolved);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return resolved;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return { ensurePrivateBridgeDirectory: ensurePrivateBridgeDirectory };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
createBridgeDirectorySecurity: createBridgeDirectorySecurity,
|
|
231
|
+
windowsAclScript: WINDOWS_ACL_SCRIPT,
|
|
232
|
+
};
|
|
233
|
+
}));
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
<small id="bridgeHelp">This must match AFTER_EFFECTS_MCP_TEMP_DIR when that environment variable is set for your MCP client.</small>
|
|
23
23
|
</main>
|
|
24
24
|
<script src="CSInterface.js"></script>
|
|
25
|
+
<script src="bridge-directory-security.cjs"></script>
|
|
25
26
|
<script src="main.js"></script>
|
|
26
27
|
</body>
|
|
27
28
|
</html>
|
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
var fs = nodeRequire("fs");
|
|
7
7
|
var path = nodeRequire("path");
|
|
8
8
|
var os = nodeRequire("os");
|
|
9
|
+
var nodeProcess = nodeRequire("process");
|
|
10
|
+
var childProcess = nodeRequire("child_process");
|
|
11
|
+
var bridgeDirectorySecurity = MCPBridgeDirectorySecurity.createBridgeDirectorySecurity({
|
|
12
|
+
fs: fs,
|
|
13
|
+
path: path,
|
|
14
|
+
platform: os.platform(),
|
|
15
|
+
process: nodeProcess,
|
|
16
|
+
childProcess: childProcess,
|
|
17
|
+
Buffer: Buffer,
|
|
18
|
+
});
|
|
9
19
|
var pollTimer = null;
|
|
10
20
|
var heartbeatTimer = null;
|
|
11
21
|
var running = false;
|
|
@@ -101,8 +111,13 @@
|
|
|
101
111
|
function start() {
|
|
102
112
|
tempDir = document.getElementById("tempDir").value.trim();
|
|
103
113
|
if (!tempDir) { setStatus("Set a bridge directory", false); return; }
|
|
104
|
-
try {
|
|
105
|
-
|
|
114
|
+
try {
|
|
115
|
+
tempDir = bridgeDirectorySecurity.ensurePrivateBridgeDirectory(tempDir);
|
|
116
|
+
document.getElementById("tempDir").value = tempDir;
|
|
117
|
+
} catch (error) {
|
|
118
|
+
setStatus("Connector needs attention: " + error.message, false);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
106
121
|
running = true;
|
|
107
122
|
heartbeat();
|
|
108
123
|
if (pollTimer) clearInterval(pollTimer);
|
|
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.
|
|
2
|
+
<ExtensionManifest Version="7.0" ExtensionBundleId="com.mcp.premiere.bridge" ExtensionBundleVersion="1.16.0" ExtensionBundleName="MCP for Adobe Premiere Pro">
|
|
3
3
|
<ExtensionList>
|
|
4
|
-
<Extension Id="com.mcp.premiere.bridge.panel" Version="1.
|
|
5
|
-
<Extension Id="com.mcp.premiere.bridge.headless" Version="1.
|
|
4
|
+
<Extension Id="com.mcp.premiere.bridge.panel" Version="1.16.0"/>
|
|
5
|
+
<Extension Id="com.mcp.premiere.bridge.headless" Version="1.16.0"/>
|
|
6
6
|
</ExtensionList>
|
|
7
7
|
<ExecutionEnvironment>
|
|
8
8
|
<HostList>
|