@writepanda/mcp 1.62.0 → 1.69.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 (2) hide show
  1. package/bin/server.mjs +94 -42
  2. package/package.json +1 -1
package/bin/server.mjs CHANGED
@@ -561,6 +561,37 @@ const TOOLS = [
561
561
  },
562
562
  command: "project.new",
563
563
  },
564
+ {
565
+ name: "project_fork_from_shot",
566
+ description:
567
+ "Fork an exported video's AI-generated shot into a new 9:16 project. Carries forward the source project's timing surgery (trim regions, transcript edits, speed regions, per-clip transcribed/audioCleaned status) but STRIPS every overlay row — zooms, FX, motion graphics, captions, transitions, lower thirds, annotations, clip-transforms, background music — because those were sized for 16:9 and don't fit a vertical short. The new project is cropped to the shot's [startMs, endMs] range, switched to 9:16, and ready to receive Shorts-native graphics/captions on top. Pass exportId + shotId from export.list (the shot lives under `entry.generatedShots[]`); the verb looks up the source project path, runs the fork, writes the new file, and returns its id + path. If the source project has been edited since export (revision drift) the verb returns `revision_mismatch` with the live revision; confirm with the user and retry with `force: true`. Returns { id, path, name, aspectRatio, sourceProjectPath, sourceProjectRevision, liveRevision, project }.",
568
+ inputSchema: {
569
+ type: "object",
570
+ properties: {
571
+ exportId: {
572
+ type: "string",
573
+ description: "Export library entry id (from export.list).",
574
+ },
575
+ shotId: {
576
+ type: "string",
577
+ description:
578
+ "Shot id (from the export entry's `generatedShots` array; produced by generate-shorts).",
579
+ },
580
+ aspectRatio: {
581
+ type: "string",
582
+ description:
583
+ "Target aspect. Defaults to '9:16' (the right answer for almost every shot generator output).",
584
+ },
585
+ force: {
586
+ type: "boolean",
587
+ description:
588
+ "Set true to fork even when the source project has drifted since export. Without it, drift returns revision_mismatch and the caller must confirm with the user first.",
589
+ },
590
+ },
591
+ required: ["exportId", "shotId"],
592
+ },
593
+ command: "project.fork-from-shot",
594
+ },
564
595
  {
565
596
  name: "project_open",
566
597
  description:
@@ -575,7 +606,7 @@ const TOOLS = [
575
606
  command: "project.open",
576
607
  },
577
608
 
578
- // ── compose: clips, motion graphics, FX, lower-thirds ──────────
609
+ // ── compose: clips, motion graphics, FX ────────────────────────
579
610
  {
580
611
  name: "project_add_clip",
581
612
  description:
@@ -786,54 +817,71 @@ const TOOLS = [
786
817
  src: { type: "string", description: "Direct path/URL (alternative to fxId)" },
787
818
  atMs: { type: "number" },
788
819
  durationMs: { type: "number", description: "Default 2500ms for bundled FX" },
820
+ speed: {
821
+ type: "number",
822
+ description:
823
+ "Playback-speed multiplier for the looping FX (0.25-4, default 1). 2 = twice as fast, 0.5 = half. Speeds up / slows down a film burn, light leak, etc. without re-rendering.",
824
+ },
789
825
  expectedRevision: { type: "number" },
790
826
  },
791
827
  required: ["atMs"],
792
828
  },
793
829
  command: "project.add-fx",
794
830
  },
831
+ {
832
+ name: "project_add_transition",
833
+ description:
834
+ "Place a transition at a cut between two clips. Drops a bundled transition overlay (a full-frame transparent WebM that goes opaque at its midpoint) CENTERED on atMs, so the opaque peak masks the cut and reads as a scene change. No render step. Pass atMs = the cut time (a clip boundary from project_read). Ids (asset_list_transitions): fade-black, fade-white, flash, light-sweep, film-burn, glitch.",
835
+ inputSchema: {
836
+ type: "object",
837
+ properties: {
838
+ id: { type: "string" },
839
+ path: { type: "string" },
840
+ transitionId: { type: "string", description: "Bundled id, e.g. fade-black. From asset_list_transitions." },
841
+ file: { type: "string", description: "Custom transition video (path / file:// URL). Use instead of transitionId." },
842
+ atMs: { type: "number", description: "Edited-timeline CUT time to center the transition on." },
843
+ durationMs: { type: "number", description: "Total length. Default 1000 (native length of the bundled set)." },
844
+ expectedRevision: { type: "number" },
845
+ },
846
+ required: ["atMs"],
847
+ },
848
+ command: "project.add-transition",
849
+ },
795
850
  {
796
851
  name: "project_add_lower_third",
797
- description: "Drop a lower-third name plate onto the timeline.",
852
+ description:
853
+ "One-call lower third: renders an lt-* nameplate motion template (hyperframes) with your name/title and places it on the timeline as a transparent overlay at atMs. ASYNC — returns { jobId, outputPath }; call job_wait — it resolves once the region is PLACED (result: { path, revision, outputPath, durationMs }), no separate project_add_motion_graphic call needed. Designs (see motion_list, category lower-third): lt-vox-marker (default, highlighter swipe), lt-broadcast-bar (news chyron), lt-glass-card (frosted), lt-kinetic-stack (bold word-by-word), lt-minimal-line (editorial), lt-corner-plate (documentary bracket + kicker slot), lt-accent-slab (knockout name), lt-avatar-pill (creator pill), lt-typewriter-tag (mono typing), lt-gradient-sweep (gradient glow).",
798
854
  inputSchema: {
799
855
  type: "object",
800
856
  properties: {
801
857
  id: { type: "string" },
802
858
  path: { type: "string" },
803
- content: { type: "string", description: "Primary text (name)" },
804
- subtitle: { type: "string", description: "Secondary text (title)" },
805
- atMs: { type: "number" },
806
- durationMs: { type: "number", description: "Default 5000ms" },
807
- designType: {
859
+ name: { type: "string", description: "Primary text (person/brand name)" },
860
+ title: { type: "string", description: "Secondary line (role / handle / tagline)" },
861
+ templateId: {
808
862
  type: "string",
809
- description:
810
- "slash-reveal | center-stack | split-horizontal | name-bar | border-frame | minimal-underline | box-reveal | corner-brackets. Default: slash-reveal.",
863
+ description: "lt-* design id. Default lt-vox-marker.",
811
864
  },
812
- accentColor: { type: "string", description: "Hex accent/background color" },
813
- textColor: { type: "string", description: "Hex text color. Default #ffffff" },
814
- backgroundColor: {
865
+ atMs: { type: "number", description: "Edited timeline time" },
866
+ aspectRatio: {
815
867
  type: "string",
816
- description: "Hex background fill. Default matches accentColor",
868
+ description: "16:9 | 9:16 | 1:1. Default 16:9 — match the project's aspect.",
817
869
  },
818
- backgroundRadius: {
819
- type: "number",
820
- description: "Corner radius in pixels. Default 8",
870
+ slots: {
871
+ type: "object",
872
+ description:
873
+ "Optional extra slot overrides (colors like accentColor/inkColor, or kicker for lt-corner-plate). Keys from motion_list. name/title args win over same-key entries.",
821
874
  },
822
- fontSize: { type: "number", description: "Primary text font size in px. Default 28" },
823
- fontFamily: { type: "string", description: "CSS font family name. Default Inter" },
824
875
  soundUrl: {
825
876
  type: "string",
826
877
  description:
827
- 'SFX played when the lower third enters. Bundled id (bundled:sound/<id>). Default bundled:sound/mouse-click. Pass "none" for silent.',
828
- },
829
- soundVolume: {
830
- type: "number",
831
- description: "Sound volume 0–1. Default 0.8.",
878
+ 'SFX on enter. Bundled id (bundled:sound/<id>). Default bundled:sound/mouse-click. Pass "none" for silent.',
832
879
  },
880
+ soundVolume: { type: "number", description: "Sound volume 0–1. Default 1." },
833
881
  anchorSourceMs: {
834
882
  type: "number",
835
883
  description:
836
- "Anchor the lower third to a SOURCE-time moment. Pass when atMs was derived from a transcript word's startMs — the lower third re-anchors automatically on subsequent transcript edits. Without it, the lower third drifts after any trim. Omit for free-floating placement.",
884
+ "Anchor to a SOURCE-time moment. Pass when atMs comes from a transcript word's startMs so the region survives later trims.",
837
885
  },
838
886
  anchorSourceEndMs: {
839
887
  type: "number",
@@ -841,7 +889,7 @@ const TOOLS = [
841
889
  },
842
890
  expectedRevision: { type: "number" },
843
891
  },
844
- required: ["content", "atMs"],
892
+ required: ["name", "atMs"],
845
893
  },
846
894
  command: "project.add-lower-third",
847
895
  },
@@ -893,7 +941,7 @@ const TOOLS = [
893
941
  {
894
942
  name: "project_set_region_sound",
895
943
  description:
896
- 'Set or clear the sound effect on an existing region (zoom / motionGraphic / lowerThird / fx). Use to swap the default zoom swoosh, add a ding to a motion graphic, or mute a lower-third click. Pass soundUrl="none" to clear.',
944
+ 'Set or clear the sound effect on an existing region (zoom / motionGraphic / fx). Use to swap the default zoom swoosh, add a ding to a motion graphic, or mute an effect sound. Pass soundUrl="none" to clear.',
897
945
  inputSchema: {
898
946
  type: "object",
899
947
  properties: {
@@ -902,7 +950,7 @@ const TOOLS = [
902
950
  regionId: { type: "string", description: "Target region id" },
903
951
  regionType: {
904
952
  type: "string",
905
- enum: ["zoom", "motionGraphic", "lowerThird", "fx"],
953
+ enum: ["zoom", "motionGraphic", "fx"],
906
954
  description: "Region kind that owns the id",
907
955
  },
908
956
  soundUrl: {
@@ -1117,7 +1165,7 @@ const TOOLS = [
1117
1165
  {
1118
1166
  name: "project_move_clip",
1119
1167
  description:
1120
- "Reorder a clip on the main track. `clipId` identifies the clip; `toIndex` is its new 0-based position after the move. Project-level regions (zooms, trims, speeds, annotations, FX, lower-thirds, media overlays, clip transforms) automatically migrate to follow their original clips so the user-perceived edits stay attached to the same content — a zoom that was 'into the explanation in clip 2' is still pointing at the same explanation after clip 2 moves to position 0. Per-clip transcripts ride on the clips themselves and don't need separate migration.",
1168
+ "Reorder a clip on the main track. `clipId` identifies the clip; `toIndex` is its new 0-based position after the move. Project-level regions (zooms, trims, speeds, annotations, FX, media overlays, clip transforms) automatically migrate to follow their original clips so the user-perceived edits stay attached to the same content — a zoom that was 'into the explanation in clip 2' is still pointing at the same explanation after clip 2 moves to position 0. Per-clip transcripts ride on the clips themselves and don't need separate migration.",
1121
1169
  inputSchema: {
1122
1170
  type: "object",
1123
1171
  properties: {
@@ -1140,7 +1188,7 @@ const TOOLS = [
1140
1188
  {
1141
1189
  name: "project_remove_region",
1142
1190
  description:
1143
- "Delete an existing region (zoom, trim, speed, annotation, fx, lower-third, overlay, clip-transform, audio-overlay) by its id. Use project_read to find region ids — visual regions live under editor.*Regions; audio overlays live under audioOverlays[]. If the region belongs to a link group (designed-segment pair: a clip-transform parking the camera into one half + the panel motion-graphic on the other half), deleting either half also removes its peer so the pair stays coherent. Returns the updated project.",
1191
+ "Delete an existing region (zoom, trim, speed, annotation, fx, overlay, clip-transform, audio-overlay) by its id. Use project_read to find region ids — visual regions live under editor.*Regions; audio overlays live under audioOverlays[]. If the region belongs to a link group (designed-segment pair: a clip-transform parking the camera into one half + the panel motion-graphic on the other half), deleting either half also removes its peer so the pair stays coherent. Returns the updated project.",
1144
1192
  inputSchema: {
1145
1193
  type: "object",
1146
1194
  properties: {
@@ -1149,7 +1197,7 @@ const TOOLS = [
1149
1197
  regionType: {
1150
1198
  type: "string",
1151
1199
  description:
1152
- "zoom | trim | speed | annotation | fx | lower-third | overlay | clip-transform | audio-overlay",
1200
+ "zoom | trim | speed | annotation | fx | overlay | clip-transform | audio-overlay",
1153
1201
  },
1154
1202
  regionId: { type: "string", description: "id of the region to delete" },
1155
1203
  expectedRevision: { type: "number" },
@@ -1180,7 +1228,7 @@ const TOOLS = [
1180
1228
  {
1181
1229
  name: "project_update_region",
1182
1230
  description:
1183
- "Patch fields on an existing region without replacing it. Accepts any subset of the region's own properties — only the supplied fields are changed, everything else is preserved. Works on zoom (depth, focusX, focusY), trim (startMs, endMs), speed (startMs, endMs, speed), lower-third (content, subtitle, atMs, durationMs, designType, accentColor, textColor, backgroundColor, backgroundRadius, fontSize, fontFamily), fx (atMs, durationMs), annotation, overlay, clip-transform (startMs, endMs, preset, transitionMs), and audio-overlay (startMs, endMs, sourceStartMs, volume — lets you drag-trim an audio overlay without removing it). Time shifts on a link-grouped region (e.g. one half of a designed segment) propagate to every peer in the group so the pair stays coherent.",
1231
+ "Patch fields on an existing region without replacing it. Accepts any subset of the region's own properties — only the supplied fields are changed, everything else is preserved. Works on zoom (depth, focusX, focusY), trim (startMs, endMs), speed (startMs, endMs, speed), fx (atMs, durationMs, opacity, blendMode, speed), annotation, overlay, clip-transform (startMs, endMs, preset, transitionMs), and audio-overlay (startMs, endMs, sourceStartMs, volume — lets you drag-trim an audio overlay without removing it). Time shifts on a link-grouped region (e.g. one half of a designed segment) propagate to every peer in the group so the pair stays coherent.",
1184
1232
  inputSchema: {
1185
1233
  type: "object",
1186
1234
  properties: {
@@ -1189,13 +1237,13 @@ const TOOLS = [
1189
1237
  regionType: {
1190
1238
  type: "string",
1191
1239
  description:
1192
- "zoom | trim | speed | annotation | fx | lower-third | overlay | clip-transform | audio-overlay",
1240
+ "zoom | trim | speed | annotation | fx | overlay | clip-transform | audio-overlay",
1193
1241
  },
1194
1242
  regionId: { type: "string", description: "id of the region to update" },
1195
1243
  patch: {
1196
1244
  type: "object",
1197
1245
  description:
1198
- "Fields to update (partial). E.g. { depth: 4 } to change zoom depth, { content: 'New Name' } on a lower-third, or { startMs: 2000, endMs: 15000, sourceStartMs: 4000 } to shift + trim an audio overlay (play seconds 4–17 of the source from the 2s mark on the timeline).",
1246
+ "Fields to update (partial). E.g. { depth: 4 } to change zoom depth, or { startMs: 2000, endMs: 15000, sourceStartMs: 4000 } to shift + trim an audio overlay (play seconds 4–17 of the source from the 2s mark on the timeline).",
1199
1247
  },
1200
1248
  expectedRevision: { type: "number" },
1201
1249
  },
@@ -1713,7 +1761,7 @@ const TOOLS = [
1713
1761
  {
1714
1762
  name: "caption_set_template",
1715
1763
  description:
1716
- "Pick a caption template: classic, modern, minimal, bold, spotlight, boxed, neon, colored, texture, or editorial. `texture` fills large uppercase words with a flowing texture mask (lava/marble/metal/wood/concrete/rock) — pick the texture via caption_set_style `texture` (defaults to lava). `editorial` is a magazine-emphasis style: the word being spoken right now renders large (and takes an accent color) while the rest of the line shrinks, so one big word sweeps across the line in time with the speech.",
1764
+ "Pick a caption template: classic, modern, minimal, bold, spotlight, boxed, neon, colored, or editorial. `editorial` is a magazine-emphasis style: the word being spoken right now renders large (and takes an accent color) while the rest of the line shrinks, so one big word sweeps across the line in time with the speech.",
1717
1765
  inputSchema: {
1718
1766
  type: "object",
1719
1767
  properties: {
@@ -1730,7 +1778,6 @@ const TOOLS = [
1730
1778
  "boxed",
1731
1779
  "neon",
1732
1780
  "colored",
1733
- "texture",
1734
1781
  "editorial",
1735
1782
  ],
1736
1783
  },
@@ -1744,7 +1791,7 @@ const TOOLS = [
1744
1791
  {
1745
1792
  name: "caption_set_style",
1746
1793
  description:
1747
- "Override specific style properties of the caption overlay without changing the template. All fields are optional — pass only what you want to change. positionY controls vertical placement (0=top, 100=bottom, default 85). fontSize accepts CSS values like '28px' or '1.5em'.",
1794
+ "Override specific style properties of the caption overlay without changing the template. All fields are optional — pass only what you want to change. positionY controls vertical placement (0=top, 100=bottom, default 85). fontSize is in REM (e.g. '2.6rem'), matching the editor's Font size slider; it is clamped to 1.0–5.0rem (a px value is converted to rem), so you can't exceed the editor's max.",
1748
1795
  inputSchema: {
1749
1796
  type: "object",
1750
1797
  properties: {
@@ -1767,11 +1814,9 @@ const TOOLS = [
1767
1814
  },
1768
1815
  strokeColor: { type: "string", description: "Text stroke/outline color" },
1769
1816
  strokeWidth: { type: "number", description: "Text stroke width in px" },
1770
- fontSize: { type: "string", description: "CSS font-size value, e.g. '28px'" },
1771
- texture: {
1817
+ fontSize: {
1772
1818
  type: "string",
1773
- enum: ["lava", "marble", "metal", "wood", "concrete", "rock"],
1774
- description: "Texture for the 'texture' caption template. Ignored by other templates.",
1819
+ description: "Font size in REM, e.g. '2.6rem'. Clamped to 1.0–5.0rem (the editor's slider range).",
1775
1820
  },
1776
1821
  expectedRevision: { type: "number" },
1777
1822
  },
@@ -2060,6 +2105,13 @@ const TOOLS = [
2060
2105
  inputSchema: { type: "object", properties: {} },
2061
2106
  command: "asset.list-fx",
2062
2107
  },
2108
+ {
2109
+ name: "asset_list_transitions",
2110
+ description:
2111
+ "List every bundled transition (id, title, category, durationSeconds). Use the id with project_add_transition, centered on a cut.",
2112
+ inputSchema: { type: "object", properties: {} },
2113
+ command: "asset.list-transitions",
2114
+ },
2063
2115
  {
2064
2116
  name: "asset_list_music",
2065
2117
  description:
@@ -2121,7 +2173,7 @@ const TOOLS = [
2121
2173
  {
2122
2174
  name: "export_start",
2123
2175
  description:
2124
- "Render the project to MP4 via the same Tier-3 PixiJS pipeline the editor's Export Video button uses (v1.24+ — convergence release). When an editor window is already open on the target project, the agent reuses it. Otherwise the agent spawns a hidden editor window for the duration of the render and closes it after. ASYNC — returns { jobId, outputPath }; call job_wait. Honours every region/style/caption/FX/lower-third/motion-graphic in the project. Video overlays (motion graphics added via project_add_motion_graphic) — opaque MP4 and transparent WebM/VP9+alpha — composite inline in the export pipeline; no extra call needed.",
2176
+ "Render the project to MP4 via the same Tier-3 PixiJS pipeline the editor's Export Video button uses (v1.24+ — convergence release). When an editor window is already open on the target project, the agent reuses it. Otherwise the agent spawns a hidden editor window for the duration of the render and closes it after. ASYNC — returns { jobId, outputPath }; call job_wait. Honours every region/style/caption/FX/motion-graphic in the project. Video overlays (motion graphics added via project_add_motion_graphic) — opaque MP4 and transparent WebM/VP9+alpha — composite inline in the export pipeline; no extra call needed.",
2125
2177
  inputSchema: {
2126
2178
  type: "object",
2127
2179
  properties: {
@@ -2348,7 +2400,7 @@ const TOOLS = [
2348
2400
  const server = new Server(
2349
2401
  {
2350
2402
  name: "pandastudio",
2351
- version: "1.58.0",
2403
+ version: "1.69.0",
2352
2404
  },
2353
2405
  {
2354
2406
  capabilities: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@writepanda/mcp",
3
- "version": "1.62.0",
3
+ "version": "1.69.0",
4
4
  "description": "Model Context Protocol server for PandaStudio. Exposes the desktop video editor's automation surface to Cursor, Continue, Cline, Claude Desktop, and any MCP-compliant client.",
5
5
  "keywords": [
6
6
  "pandastudio",