@writepanda/mcp 1.66.0 → 1.72.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 +61 -12
  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 with a centered cover-crop so the landscape source FILLS the vertical frame (not letterboxed), 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:
@@ -661,7 +692,7 @@ const TOOLS = [
661
692
  {
662
693
  name: "project_add_designed_segment",
663
694
  description:
664
- "Create a 'designed segment' — the YouTube split where the host stays LIVE on one half and a motion-graphic panel fills the other (host right / panel left, like a Vox or MKBHD explainer beat). ONE atomic call places both pieces over the same time range with the same transition and source anchor, so they can't drift or mismatch: the camera is repositioned + cover-cropped into its half (a full-bleed split clip-transform) and a full-frame transparent panel graphic is composited on top. Stays in the live compositor — camera is full-quality and scrubbable, nothing baked. IMPORTANT: author the panel graphic as a FULL-FRAME (1920×1080) TRANSPARENT render via motion_render_html --transparent — paint the opaque panel + content on the panel side, leave the camera's half fully transparent (it reveals the repositioned host). See SKILL.md 'Designed segments'.",
695
+ "Create a 'designed segment' — the split where the host stays LIVE on one half and a motion-graphic panel fills the other. 16:9: host right / panel left, like a Vox or MKBHD explainer beat. 9:16 SHORTS: host in a top/bottom band, panel in the other band (cameraSide top|bottom). ONE atomic call places both pieces over the same time range with the same transition and source anchor, so they can't drift or mismatch: the camera is repositioned + cover-cropped into its half (a full-bleed split clip-transform) and a full-frame transparent panel graphic is composited on top. Stays in the live compositor — camera is full-quality and scrubbable, nothing baked. IMPORTANT: author the panel graphic as a FULL-FRAME TRANSPARENT render at the project aspect via motion_render_html --transparent (1920x1080 for 16:9, 1080x1920 for 9:16; the premium `paper-panel` and `vox-side-panel` templates are aspect-aware — render either at 9:16 and it reflows into a top/bottom band) — paint the opaque panel + content on the panel side, leave the camera's half fully transparent (it reveals the repositioned host). See SKILL.md 'Designed segments' and 'Editing a Short'.",
665
696
  inputSchema: {
666
697
  type: "object",
667
698
  properties: {
@@ -681,15 +712,15 @@ const TOOLS = [
681
712
  atMs: { type: "number", description: "Default: end of timeline" },
682
713
  cameraSide: {
683
714
  type: "string",
684
- enum: ["left", "right"],
715
+ enum: ["left", "right", "top", "bottom"],
685
716
  description:
686
- "Which half the CAMERA fills; the panel graphic fills the opposite half. 'right' is the canonical reference (host right, panel left).",
717
+ "Which half the CAMERA fills; the panel graphic fills the opposite half. 16:9: 'left'/'right' ('right' is the canonical reference — host right, panel left). 9:16 SHORTS: 'top'/'bottom' — the camera fills a horizontal band and the panel covers the other band (render a premium panel like `paper-panel` or `vox-side-panel` at 9:16; both reflow into a top/bottom band).",
687
718
  },
688
719
  cameraRatio: {
689
720
  type: "number",
690
721
  enum: [50, 55],
691
722
  description:
692
- "Camera's share of the frame width: 55 (default, the reference split) or 50 (balanced). Panel gets the remaining 45 or 50. Author the panel's opaque width to match (e.g. left 45% for cameraSide=right, cameraRatio=55).",
723
+ "Camera's share of the split axis (width for left/right, height for top/bottom): 55 (default for 16:9) or 50 (balanced — pair the 9:16 shorts template with 50). Panel gets the rest. Author the panel's opaque band to match (e.g. left 45% for cameraSide=right ratio=55; bottom 50% for cameraSide=top ratio=50).",
693
724
  },
694
725
  transitionMs: {
695
726
  type: "number",
@@ -1279,6 +1310,27 @@ const TOOLS = [
1279
1310
  },
1280
1311
  command: "project.set-crop",
1281
1312
  },
1313
+ {
1314
+ name: "project_set_focal_point",
1315
+ description:
1316
+ "Set or clear a clip's FACE FOCAL POINT (source-normalised 0-1, origin top-left). When set, cover-cropped layouts — a 9:16 fill or a top/bottom designed-segment band — bias toward this point instead of centering geometrically, so the face stays in frame. Omit clipId to apply to ALL clips (single-clip shorts). Pass clear=true (or omit x/y) to reset to centered. NOTE: the editor AUTO-DETECTS this for vertical talking-head clips when they open, so you rarely need this — use it to override the auto framing or when building a short fully headless.",
1317
+ inputSchema: {
1318
+ type: "object",
1319
+ properties: {
1320
+ id: { type: "string" },
1321
+ path: { type: "string" },
1322
+ clipId: {
1323
+ type: "string",
1324
+ description: "Target clip id (from project.read clipStates). Omit to apply to all clips.",
1325
+ },
1326
+ x: { type: "number", description: "Face center X, 0-1. Omit (with y) to clear." },
1327
+ y: { type: "number", description: "Face center Y, 0-1. Omit (with x) to clear." },
1328
+ clear: { type: "boolean", description: "Reset the focal point to geometric centering." },
1329
+ expectedRevision: { type: "number" },
1330
+ },
1331
+ },
1332
+ command: "project.set-focal-point",
1333
+ },
1282
1334
  {
1283
1335
  name: "project_set_export_settings",
1284
1336
  description:
@@ -1730,7 +1782,7 @@ const TOOLS = [
1730
1782
  {
1731
1783
  name: "caption_set_template",
1732
1784
  description:
1733
- "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.",
1785
+ "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.",
1734
1786
  inputSchema: {
1735
1787
  type: "object",
1736
1788
  properties: {
@@ -1747,7 +1799,6 @@ const TOOLS = [
1747
1799
  "boxed",
1748
1800
  "neon",
1749
1801
  "colored",
1750
- "texture",
1751
1802
  "editorial",
1752
1803
  ],
1753
1804
  },
@@ -1761,7 +1812,7 @@ const TOOLS = [
1761
1812
  {
1762
1813
  name: "caption_set_style",
1763
1814
  description:
1764
- "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'.",
1815
+ "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.",
1765
1816
  inputSchema: {
1766
1817
  type: "object",
1767
1818
  properties: {
@@ -1784,11 +1835,9 @@ const TOOLS = [
1784
1835
  },
1785
1836
  strokeColor: { type: "string", description: "Text stroke/outline color" },
1786
1837
  strokeWidth: { type: "number", description: "Text stroke width in px" },
1787
- fontSize: { type: "string", description: "CSS font-size value, e.g. '28px'" },
1788
- texture: {
1838
+ fontSize: {
1789
1839
  type: "string",
1790
- enum: ["lava", "marble", "metal", "wood", "concrete", "rock"],
1791
- description: "Texture for the 'texture' caption template. Ignored by other templates.",
1840
+ description: "Font size in REM, e.g. '2.6rem'. Clamped to 1.0–5.0rem (the editor's slider range).",
1792
1841
  },
1793
1842
  expectedRevision: { type: "number" },
1794
1843
  },
@@ -2372,7 +2421,7 @@ const TOOLS = [
2372
2421
  const server = new Server(
2373
2422
  {
2374
2423
  name: "pandastudio",
2375
- version: "1.66.0",
2424
+ version: "1.70.0",
2376
2425
  },
2377
2426
  {
2378
2427
  capabilities: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@writepanda/mcp",
3
- "version": "1.66.0",
3
+ "version": "1.72.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",