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.
Files changed (68) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +76 -15
  3. package/after-effects-cep-plugin/CSXS/manifest.xml +2 -2
  4. package/after-effects-cep-plugin/bridge-directory-security.cjs +233 -0
  5. package/after-effects-cep-plugin/index.html +1 -0
  6. package/after-effects-cep-plugin/main.js +17 -2
  7. package/artifacts/MCPBridgeCEP.zxp +0 -0
  8. package/cep-plugin/CSXS/manifest.xml +3 -3
  9. package/cep-plugin/bridge-directory-security.cjs +233 -0
  10. package/cep-plugin/index.html +3 -2
  11. package/cep-plugin/main.js +28 -9
  12. package/cep-plugin/updater.cjs +1 -1
  13. package/dist/ai/reaction-shorts.d.ts +126 -0
  14. package/dist/ai/reaction-shorts.js +473 -0
  15. package/dist/ai/short-form-candidates.js +3 -1
  16. package/dist/ai/word-timeline.d.ts +1 -1
  17. package/dist/ai/word-timeline.js +11 -2
  18. package/dist/bridge/file-bridge.d.ts +31 -0
  19. package/dist/bridge/file-bridge.js +157 -14
  20. package/dist/bridge/uxp-websocket-bridge.js +8 -1
  21. package/dist/diagnostics.d.ts +5 -1
  22. package/dist/diagnostics.js +11 -5
  23. package/dist/homepage-experiment.d.ts +2 -0
  24. package/dist/homepage-experiment.js +21 -0
  25. package/dist/http-security.js +3 -1
  26. package/dist/http-server.js +49 -12
  27. package/dist/index.js +8 -3
  28. package/dist/landing-documents.d.ts +38 -0
  29. package/dist/landing-documents.js +142 -0
  30. package/dist/resources/adobe-uxp-coverage.json +2 -2
  31. package/dist/security/capabilities.js +3 -0
  32. package/dist/server.js +2 -0
  33. package/dist/tools/caption-authoring.d.ts +37 -1
  34. package/dist/tools/caption-authoring.js +58 -1
  35. package/dist/tools/export.js +27 -7
  36. package/dist/tools/keyframes.d.ts +2 -1
  37. package/dist/tools/keyframes.js +9 -6
  38. package/dist/tools/markers.js +12 -3
  39. package/dist/tools/media-watch.d.ts +62 -6
  40. package/dist/tools/media-watch.js +181 -33
  41. package/dist/tools/project.js +19 -5
  42. package/dist/tools/reaction-shorts.d.ts +277 -0
  43. package/dist/tools/reaction-shorts.js +101 -0
  44. package/dist/tools/recovery.d.ts +5 -1
  45. package/dist/tools/recovery.js +141 -29
  46. package/dist/tools/rhythm-plans.d.ts +1 -1
  47. package/dist/tools/shorts-intelligence.d.ts +2 -2
  48. package/dist/tools/speaker-layout.d.ts +2 -2
  49. package/dist/tools/transcript-word-edits.d.ts +4 -4
  50. package/dist/tools/uxp-advanced-workflows.js +1 -1
  51. package/dist/tools/uxp-workflows.js +1 -1
  52. package/dist/tools/uxp.d.ts +126 -3
  53. package/dist/tools/uxp.js +106 -14
  54. package/dist/workflows/agent-instructions.js +1 -0
  55. package/dist/workflows/catalog.d.ts +7 -2
  56. package/dist/workflows/catalog.js +6 -0
  57. package/dist/workflows/tool-metadata.js +3 -0
  58. package/dist/workflows/tool-packs.js +6 -0
  59. package/docs/supported-actions.md +18 -12
  60. package/package.json +5 -4
  61. package/public-product-manifest.json +7 -7
  62. package/scripts/generate-marketing-reference.mjs +2 -0
  63. package/scripts/verify-npm-package.mjs +2 -0
  64. package/uxp-plugin/advanced-workflows.cjs +22 -9
  65. package/uxp-plugin/commands.cjs +53 -4
  66. package/uxp-plugin/manifest.json +1 -1
  67. package/uxp-plugin/transcript.cjs +11 -1
  68. 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: 369 core tools across 53 modules, 4 resources, and 17 guided workflows. A connected UXP host adds 93 capability-gated tools.
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 369 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 93 documented, capability-gated tools without replacing the production CEP bridge.
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.15.2
111
+ ### Latest release: 1.16.0
112
112
 
113
- The published v1.15.1 npm artifact contains **369 core tools**, 367 in its default profile,
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.15.1 release notes](https://github.com/leancoderkavy/premiere-pro-mcp/releases/tag/v1.15.1)
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.15.1/premiere-pro-mcp-1.15.1.mcpb).
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.15.1/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.
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
- Enter the same token in the UXP panel. The listener binds only to `127.0.0.1:7777`, authenticates the WebSocket upgrade, requires a versioned capability handshake, correlates concurrent requests, and fails pending work on timeout or disconnect. Set `PREMIERE_UXP_PORT` to use another loopback port.
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 (369 core total; 367 under the default profile; 460 with a connected UXP bridge)
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 91 authenticated UXP additions with their current action or mode
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 369 tools, filtered by authority profile
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
- - The bridge temp directory is created private to your user (mode `0700`), and the server
1354
- refuses to use one owned by another user — relevant on shared machines, where the CEP
1355
- panel would otherwise execute any `cmd_*.jsx` staged there.
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.15.2" ExtensionBundleName="MCP for Adobe After Effects">
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.15.2"/>
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 { fs.mkdirSync(tempDir, { recursive: true, mode: 0o700 }); }
105
- catch (error) { setStatus("Cannot create bridge directory", false); return; }
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.15.2" ExtensionBundleName="MCP for Adobe Premiere Pro">
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.15.2"/>
5
- <Extension Id="com.mcp.premiere.bridge.headless" Version="1.15.2"/>
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>