premiere-pro-mcp 1.15.2 → 1.16.1

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 (94) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +111 -22
  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/client-notes.d.ts +116 -0
  14. package/dist/ai/client-notes.js +473 -0
  15. package/dist/ai/edl-export.d.ts +118 -0
  16. package/dist/ai/edl-export.js +324 -0
  17. package/dist/ai/multicam-switching.d.ts +125 -0
  18. package/dist/ai/multicam-switching.js +418 -0
  19. package/dist/ai/reaction-shorts.d.ts +126 -0
  20. package/dist/ai/reaction-shorts.js +473 -0
  21. package/dist/ai/short-form-candidates.js +3 -1
  22. package/dist/ai/word-timeline.d.ts +1 -1
  23. package/dist/ai/word-timeline.js +11 -2
  24. package/dist/bridge/file-bridge.d.ts +31 -0
  25. package/dist/bridge/file-bridge.js +157 -14
  26. package/dist/bridge/script-builder.js +362 -0
  27. package/dist/bridge/uxp-websocket-bridge.js +8 -1
  28. package/dist/diagnostics.d.ts +5 -1
  29. package/dist/diagnostics.js +11 -5
  30. package/dist/homepage-experiment.d.ts +2 -0
  31. package/dist/homepage-experiment.js +21 -0
  32. package/dist/http-security.js +3 -1
  33. package/dist/http-server.js +49 -12
  34. package/dist/index.js +8 -3
  35. package/dist/landing-documents.d.ts +38 -0
  36. package/dist/landing-documents.js +142 -0
  37. package/dist/resources/adobe-uxp-coverage.json +2 -2
  38. package/dist/resources/extendscript-reference.js +1 -1
  39. package/dist/security/capabilities.js +5 -0
  40. package/dist/server.js +6 -0
  41. package/dist/tools/advanced.d.ts +17 -0
  42. package/dist/tools/advanced.js +294 -22
  43. package/dist/tools/caption-authoring.d.ts +37 -1
  44. package/dist/tools/caption-authoring.js +58 -1
  45. package/dist/tools/competitor-gaps.js +4 -1
  46. package/dist/tools/edit-plans.js +1 -1
  47. package/dist/tools/editor-requests.d.ts +377 -0
  48. package/dist/tools/editor-requests.js +784 -0
  49. package/dist/tools/export.d.ts +2 -0
  50. package/dist/tools/export.js +63 -6
  51. package/dist/tools/keyframes.d.ts +2 -1
  52. package/dist/tools/keyframes.js +9 -6
  53. package/dist/tools/markers.js +12 -3
  54. package/dist/tools/media-watch.d.ts +62 -6
  55. package/dist/tools/media-watch.js +181 -33
  56. package/dist/tools/project.js +19 -5
  57. package/dist/tools/reaction-shorts.d.ts +277 -0
  58. package/dist/tools/reaction-shorts.js +101 -0
  59. package/dist/tools/recovery.d.ts +5 -1
  60. package/dist/tools/recovery.js +141 -29
  61. package/dist/tools/review-plans.d.ts +222 -0
  62. package/dist/tools/review-plans.js +97 -0
  63. package/dist/tools/rhythm-plans.d.ts +1 -1
  64. package/dist/tools/shorts-intelligence.d.ts +2 -2
  65. package/dist/tools/source-monitor.d.ts +6 -0
  66. package/dist/tools/source-monitor.js +16 -4
  67. package/dist/tools/speaker-layout.d.ts +2 -2
  68. package/dist/tools/spot-workflows.js +2 -1
  69. package/dist/tools/timeline.d.ts +6 -0
  70. package/dist/tools/timeline.js +112 -76
  71. package/dist/tools/transcript-word-edits.d.ts +4 -4
  72. package/dist/tools/uxp-advanced-workflows.js +1 -1
  73. package/dist/tools/uxp-workflows.js +2 -2
  74. package/dist/tools/uxp.d.ts +126 -3
  75. package/dist/tools/uxp.js +106 -14
  76. package/dist/workflows/agent-instructions.js +1 -0
  77. package/dist/workflows/catalog.d.ts +7 -2
  78. package/dist/workflows/catalog.js +6 -0
  79. package/dist/workflows/tool-metadata.js +5 -0
  80. package/dist/workflows/tool-packs.js +15 -0
  81. package/docs/supported-actions.md +29 -15
  82. package/package.json +5 -4
  83. package/public-product-manifest.json +7 -7
  84. package/scripts/build-claude-desktop.mjs +30 -1
  85. package/scripts/generate-marketing-reference.mjs +2 -0
  86. package/scripts/validate-distribution.mjs +2 -2
  87. package/scripts/verify-npm-package.mjs +2 -0
  88. package/uxp-plugin/advanced-workflows.cjs +67 -13
  89. package/uxp-plugin/commands.cjs +53 -4
  90. package/uxp-plugin/dialogue-workflows.cjs +27 -1
  91. package/uxp-plugin/index.cjs +28 -1
  92. package/uxp-plugin/manifest.json +1 -1
  93. package/uxp-plugin/transcript.cjs +11 -1
  94. package/uxp-plugin/workflows.cjs +67 -28
package/CHANGELOG.md CHANGED
@@ -6,6 +6,102 @@ 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.1] - 2026-09-17
10
+
11
+ ### Added
12
+
13
+ - Editor-request tools on the production CEP bridge, each with preflight and
14
+ readback: `add_markers_batch` (up to 200 verified sequence or clip markers per
15
+ call for beat grids, chapters, silence reviews, and client notes),
16
+ `select_clips_by_pattern` (every-Nth selection with offset, name/regex,
17
+ duration, range, track, and enabled filters), `navigate_playhead`
18
+ (start/end/in/out/work-area/edit/marker/frame stepping),
19
+ `create_sequence_checkpoint` and `list_sequence_checkpoints` (named
20
+ `[checkpoint]` sequence clones plus a diff-ready snapshot), and
21
+ `export_sequence_edl` (CMX 3600 EDL generated from timeline readback with
22
+ drop-frame support, reel mapping, M2 motion lines, and self-validation through
23
+ the existing CMX parser, returned inline or written inside an approved
24
+ workspace).
25
+ - Local review planners: `plan_client_notes_checklist` turns pasted reviewer
26
+ feedback into a categorized, prioritized checklist with timecodes, ranges,
27
+ approvals, questions, and an `add_markers_batch` payload;
28
+ `plan_multicam_angle_switches` plans active-speaker angle cuts for stacked
29
+ camera tracks with minimum holds, crosstalk cover shots, lead-in cuts,
30
+ periodic cutaways, razor times, per-camera enable ranges, and markers.
31
+ - The `essential`, `inspection`, `delivery`, and `assistant-edit` tool packs
32
+ include the relevant new tools; both planners are classified as `inspect`
33
+ authority. See [docs/editor-requests.md](docs/editor-requests.md) for the
34
+ community and competitor evidence and verification boundaries.
35
+
36
+ ### Fixed
37
+
38
+ - `insert_from_source`, `add_to_timeline`, `add_to_timeline_batch`, and
39
+ `apply_edit_plan` insert operations no longer report success after
40
+ `Sequence.insertClip` ripples only the named tracks. The public DOM has no
41
+ sync-lock API; these tools now read QE `isSyncLocked()`, razor spanning clips
42
+ on locked neighbours, shift later clips with `__writeClipSpan`, and verify
43
+ the result. Mid-clip inserts on a target track are treated as a
44
+ split-plus-insert (two new items), not a failure. Default `scope` is
45
+ `sync_locked` and requires the target sequence to be active so QE razors the
46
+ same timeline. A razor that does not split a spanning neighbour fails closed.
47
+ Pass `target_tracks` to opt in to the old target-only ripple; that path still
48
+ verifies the named pair, honors DOM `Track.isLocked()`, and warns that other
49
+ tracks may desync. If QE or lock state is unavailable the sync-locked path
50
+ refuses before mutating. `apply_spot_workflow_plan` uses `target_tracks`
51
+ because it inserts then trims to the planned duration; a sync-locked ripple
52
+ would shift overlays and music beds by the untrimmed source length. (#562)
53
+ - `ripple_delete` now closes the gap on the clip's own track and every QE
54
+ sync-locked track, and refuses when a locked neighbour would be left
55
+ straddling the hole. (#561)
56
+
57
+ Automated checks do not establish licensed Premiere or After Effects playback or
58
+ rendered-output verification.
59
+
60
+ ## [1.16.0] - 2026-09-16
61
+
62
+ ### Added
63
+
64
+ - Local reaction-Shorts planners: `plan_reaction_captions` stacks overlapping
65
+ speaker colors without guessing unknown speakers, `plan_short_subscribe_cta`
66
+ places a mid-video subscribe overlay, and `plan_short_export_folder` names a
67
+ series folder to create before export. Different labeled speakers may now
68
+ overlap in a word timeline. (#543)
69
+ - Guarded Speech-to-Text start, caption style guidance, and UXP transcription
70
+ language options. (#513, #515)
71
+ - Install collision-defense identity output for `--version` and `--doctor`, plus
72
+ a verified npm installation guide and package-identity checks. (#516, #533,
73
+ #534)
74
+ - Recorded workflow evidence and a source-linked Premiere MCP comparison. (#532)
75
+ - Interactive cinematic landing: 3D timeline, draggable trims, a program
76
+ monitor, public-page styling, studio install/Name-check callout, Ahrefs
77
+ verification, and a female-narration advertisement. (#517–#521, #524–#531,
78
+ #546)
79
+ - Bounded Fly landing events now go to PostHog as well as Google Analytics,
80
+ without autocapture, session replay, or person profiles. (#522)
81
+
82
+ ### Fixed
83
+
84
+ - Host-reported tool crashes and false verification for clip markers, MOGRT JSON
85
+ values, first transcript import, FCP XML destination checks, and UXP tree
86
+ IDs. (#544)
87
+ - Security audit findings: HTTP and filesystem work is bounded, and bridge
88
+ directories fail closed when ownership, symlinks, or ancestor replacement
89
+ rights are untrusted. (#545)
90
+ - AME handoff tools require a saved project so Same as Project preset
91
+ destinations cannot resolve against a scratch folder. (#535)
92
+ - Homepage overflow and cinematic timeline replay after the landing merge.
93
+ (#523)
94
+
95
+ ### Changed
96
+
97
+ - Creating a UXP preset sequence now requires explicit confirmation. (#542)
98
+ - The Claude Desktop bundle no longer requires a UXP token for CEP-only setups.
99
+ - Homepage experiment assignment is exposed at first paint. (#514)
100
+ - Removed the throwaway `uxp-spike` directory. (#520)
101
+
102
+ Automated checks do not establish licensed Premiere or After Effects playback or
103
+ rendered-output verification.
104
+
9
105
  ## [1.15.2] - 2026-09-14
10
106
 
11
107
  ### 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: 381 core tools across 56 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,14 +104,14 @@ 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 381 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.1
112
112
 
113
- The published v1.15.1 npm artifact contains **369 core tools**, 367 in its default profile,
114
- and 460 with a compatible UXP connection. The development catalog above can include
113
+ The published v1.16.0 npm artifact contains **373 core tools**, 371 in its default profile,
114
+ and 466 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
 
117
117
  ### Try a bounded workflow
@@ -132,7 +132,7 @@ if the connection is unavailable.
132
132
 
133
133
  - **Editorial planning:** film evidence, transcript cleanup, caption authoring, shorts and chapter planning, rhythm and speaker layouts, timeline QA, and platform delivery plans.
134
134
  - **Cross-app handoff:** capability-aware workflow routes and guarded After Effects render-to-Premiere handoff.
135
- - **Editing correctness:** verified readback and explicit capability or committed-but-unverified failures across transition, import, effects, source identity, and track operations.
135
+ - **Editing correctness:** verified readback and explicit capability or committed-but-unverified failures across transition, import, effects, source identity, and track operations. Insert edits ripple QE sync-locked tracks instead of silently desyncing neighbours.
136
136
 
137
137
  - **MOGRT studio:** an optional, separate After Effects CEP connector can
138
138
  author approval-gated title, callout, quote, and social recipes; constrain
@@ -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.1 release notes](https://github.com/leancoderkavy/premiere-pro-mcp/releases/tag/v1.16.1)
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.1
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.1` |
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.1/premiere-pro-mcp-1.16.1.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.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.
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 (381 core total; 379 under the default profile; 474 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
 
@@ -961,7 +983,7 @@ Premiere Pro 26.x host.
961
983
  | `get_value_at_time` | Query interpolated value at any time |
962
984
  | `set_color_value` | Set color properties on effects |
963
985
 
964
- ### Export & Encoding (16)
986
+ ### Export & Encoding (17)
965
987
 
966
988
  | Tool | Description |
967
989
  | :--- | :---------- |
@@ -970,13 +992,17 @@ Premiere Pro 26.x host.
970
992
  | `verify_delivery_file` | Verify output size and calculate SHA-256/SHA-512 checksums |
971
993
  | `capture_frame` | Export frame as PNG, return as base64 image |
972
994
  | `export_as_fcp_xml` / `export_aaf` / `export_omf` | Interchange formats |
995
+ | `export_sequence_edl` | CMX 3600 EDL for one track, generated from timeline readback and self-validated (cuts, reels, drop/non-drop timecode, M2 lines for retimed clips) |
973
996
  | `encode_project_item` / `encode_file` | Direct encoding |
974
997
  | `start_batch_encode` | Start render queue |
975
998
 
976
- Premiere's documented automation surfaces do not currently expose OTIO or EDL
977
- interchange, Render and Replace, cloud publishing, or Content Credentials export
978
- configuration. `get_capabilities` reports these delivery gaps explicitly rather
979
- than presenting UI-only operations as available tools.
999
+ Premiere's documented automation surfaces do not currently expose OTIO or native
1000
+ EDL interchange, Render and Replace, cloud publishing, or Content Credentials
1001
+ export configuration. `export_sequence_edl` therefore builds the CMX 3600 list
1002
+ locally from the clips Premiere reads back and validates it with the same parser
1003
+ used by `inspect_cmx3600_edl`; it is not a Premiere-native export.
1004
+ `get_capabilities` reports the remaining delivery gaps explicitly rather than
1005
+ presenting UI-only operations as available tools.
980
1006
 
981
1007
  ### Source Monitor & Playback (7 + 4)
982
1008
 
@@ -1030,6 +1056,30 @@ than presenting UI-only operations as available tools.
1030
1056
  | `diff_sequence_snapshots` | Added / removed / moved / trimmed / retimed / renamed / enabled changes between two sequence snapshots with frame deltas and EDL-like lines |
1031
1057
  | `audit_timeline_health` | Health score with flash-frame, gap, overlap, repeated-shot, coverage, overlength, and speed findings plus review-frame suggestions |
1032
1058
 
1059
+ ### Editor Requests: markers, selection, checkpoints, navigation (5)
1060
+
1061
+ | Tool | Description |
1062
+ | :--- | :---------- |
1063
+ | `add_markers_batch` | Up to 200 sequence or clip markers in one verified request; feed it beat grids from `detect_beats`, chapters from `plan_chapter_markers`, or client notes from `plan_client_notes_checklist` |
1064
+ | `select_clips_by_pattern` | "Select every other clip": every-Nth selection with offset, name/regex, duration, range, track, and enabled filters, read back after selecting |
1065
+ | `create_sequence_checkpoint` / `list_sequence_checkpoints` | Named `[checkpoint]` sequence clone before a risky edit, plus a `diff_sequence_snapshots`-ready snapshot of the original |
1066
+ | `navigate_playhead` | Start, end, in/out, work area, next/previous edit or marker, or frame stepping with position readback |
1067
+
1068
+ ### Review and Conversation Planning (2)
1069
+
1070
+ | Tool | Description |
1071
+ | :--- | :---------- |
1072
+ | `plan_client_notes_checklist` | Pasted client or reviewer feedback → prioritized checklist (category, must/should/nice, approvals, questions, timecodes and ranges) and an `add_markers_batch` payload |
1073
+ | `plan_multicam_angle_switches` | Active-speaker angle switching for stacked camera tracks: minimum holds, crosstalk cover shots, lead-in cuts, cutaways, razor times, per-camera enable ranges, and markers |
1074
+
1075
+ Both planners are local and deterministic. Premiere exposes no scripting API
1076
+ for switching angles inside a multicam source sequence, so the multicam plan
1077
+ targets synced clips stacked on separate video tracks and is applied through
1078
+ `razor_all_tracks`, `select_clips_in_range`, `batch_enable_disable`, and
1079
+ `add_markers_batch` on a checkpointed sequence. See
1080
+ [editor requests](docs/editor-requests.md) for the community and competitor
1081
+ evidence behind these tools and their verification boundaries.
1082
+
1033
1083
  ### Speaker Layout (2)
1034
1084
 
1035
1085
  | Tool | Description |
@@ -1058,6 +1108,14 @@ than presenting UI-only operations as available tools.
1058
1108
  | `build_caption_artifact` | Word-grouped SRT/VTT captions with karaoke timestamps, emphasis markup, speaker prefixes, and style presets, written inside an approved workspace |
1059
1109
  | `check_caption_safe_zone` | Overlap check for caption and graphic rectangles against approximate platform UI zones, with a suggested clear position |
1060
1110
 
1111
+ ### Reaction Shorts (3)
1112
+
1113
+ | Tool | Description |
1114
+ | :--- | :---------- |
1115
+ | `plan_reaction_captions` | Stacked speaker-colored caption plan from a word timeline and explicit palette; flash words merge, overlaps stack, unknown speakers stay uncolored |
1116
+ | `plan_short_subscribe_cta` | Brief subscribe overlay placed about two-thirds through a Short, after an optional hook, with brand-safe styling notes |
1117
+ | `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 |
1118
+
1061
1119
  ### Transcript Word Edits (4)
1062
1120
 
1063
1121
  | Tool | Description |
@@ -1233,6 +1291,10 @@ user/device authorization are implemented.
1233
1291
  | `MCP_MAX_REQUESTS_PER_SOCKET` | Requests permitted on one keep-alive socket | `100` |
1234
1292
  | `MCP_MAX_CONCURRENT_REQUESTS` | In-flight authenticated MCP request ceiling | `8` |
1235
1293
  | `MCP_MAX_CONCURRENT_STREAMS` | Open authenticated SSE stream ceiling; isolated from operation capacity | `32` |
1294
+ | `MCP_MAX_CONCURRENT_LANDING_DOCUMENTS` | Concurrent HTML reads, nonce injection and asynchronous compression; excess work returns `503` | `4` |
1295
+ | `MCP_LANDING_MAX_HTML_BYTES` | Maximum source HTML document bytes | `4194304` (4 MiB) |
1296
+ | `MCP_LANDING_HTML_CACHE_BYTES` | Source HTML cache memory budget; must be at least the document byte limit | `16777216` (16 MiB) |
1297
+ | `PREMIERE_MCP_PROJECT_BACKUP_MAX_BYTES` | Positive integer byte budget for one project backup | `2147483648` (2 GiB) |
1236
1298
  | `MCP_RATE_LIMIT_PER_MINUTE` | Per-credential token-bucket refill rate | `120` |
1237
1299
  | `MCP_RATE_LIMIT_BURST` | Per-credential short burst allowance | `30` |
1238
1300
  | `MCP_MAX_RATE_LIMIT_KEYS` | In-memory rate-limit identity ceiling | `2048` |
@@ -1265,7 +1327,7 @@ premiere-pro-mcp/
1265
1327
  ├── src/
1266
1328
  │ ├── index.ts # Entry point — stdio transport setup
1267
1329
  │ ├── http-server.ts # Entry point — HTTP/SSE transport (Fly.io / remote)
1268
- │ ├── server.ts # MCP server — registers 369 tools, filtered by authority profile
1330
+ │ ├── server.ts # MCP server — registers 381 tools, filtered by authority profile
1269
1331
  │ ├── bridge/
1270
1332
  │ │ ├── file-bridge.ts # File-based IPC (write .jsx, poll .json)
1271
1333
  │ │ └── script-builder.ts # ExtendScript generator with ES3 helpers
@@ -1350,9 +1412,36 @@ by default. Enable them only by setting
1350
1412
  upstream rate limit and request-size limit too; process-local counters do not protect a multi-machine deployment.
1351
1413
  - **The landing CSP uses a per-response nonce for scripts**, and its static assets use explicit cache policies.
1352
1414
  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.
1415
+ Source HTML is cached as immutable build output; restart the HTTP process after replacing
1416
+ an export. Each response still gets a fresh nonce. HEAD requests skip body rendering.
1417
+ - **Media scans yield between asynchronous filesystem operations** and bound total traversal:
1418
+ 25,000 entries, 5,000 matching files, 2,000 directories, depth 32, a 1,000-directory
1419
+ pending queue, and a cooperative five-second budget. A stalled OS call can exceed that
1420
+ elapsed budget. Inspect `scan_incomplete` and `scan_limit_reasons` before treating a
1421
+ watch baseline as complete; import previews also report `incomplete`.
1422
+ - **Project backups stream their copy and checksum work** with a configurable byte budget.
1423
+ Only one backup runs per process; concurrent requests fail promptly. Failed or cancelled
1424
+ copies are removed without replacing an existing backup.
1425
+ - **The Docker runner uses the unprivileged `node` user (UID 1000).** Custom bridge or
1426
+ context volume mounts must be writable by that user and private to the operator.
1427
+ The default context directory is under `/home/node/.local/state/premiere-pro-mcp`.
1428
+ - Dependency audits run in CI for both lockfiles. Keep local `.env*`, `.npmrc`, and
1429
+ private key/certificate files out of commits and Docker build contexts; only
1430
+ placeholder `.env.example` and `.env.template` files are eligible for Git tracking.
1431
+ - Both CEP panels validate the bridge directory before writing a heartbeat or polling
1432
+ commands. Symlinks and directories owned by another user are refused. On POSIX,
1433
+ directories writable by group/other users are refused by both the server and panels;
1434
+ tightening permissions alone cannot make previously staged commands trustworthy.
1435
+ On Windows, only the current user, SYSTEM and Administrators may have write access,
1436
+ including inherited grants. ACL inspection failures also stop startup. Ancestors must
1437
+ also prevent other users from replacing the bridge path; a private child inside a
1438
+ broadly writable parent is insufficient (POSIX sticky temp directories are supported).
1439
+ Choose a new directory under a private user-owned location if the connector rejects
1440
+ a shared temp folder, and configure the same path on the server and panel. Do not
1441
+ copy pending commands from the rejected folder into the new one.
1442
+ Windows checks invoke PowerShell synchronously before publishing commands, with a
1443
+ five-second timeout and 64 KiB output cap; this adds command latency rather than
1444
+ caching a permission decision that could become stale.
1356
1445
  - There is a 500 KB script size limit, and a small regex check that rejects `eval()`,
1357
1446
  `new Function()`, and `System.callSystem()` in tool-generated scripts. **This is a guard
1358
1447
  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.1" 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.1"/>
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