@writepanda/mcp 1.76.0 → 1.79.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 +91 -16
  2. package/package.json +1 -1
package/bin/server.mjs CHANGED
@@ -379,7 +379,10 @@ const TOOLS = [
379
379
  id: { type: "string", description: "Export entry id." },
380
380
  accountId: { type: "string" },
381
381
  channelId: { type: "string" },
382
- title: { type: "string", description: "Max 100 chars; < and > are stripped (YouTube rejects them)." },
382
+ title: {
383
+ type: "string",
384
+ description: "Max 100 chars; < and > are stripped (YouTube rejects them).",
385
+ },
383
386
  description: {
384
387
  type: "string",
385
388
  description:
@@ -420,7 +423,10 @@ const TOOLS = [
420
423
  properties: {
421
424
  accountId: { type: "string" },
422
425
  videoId: { type: "string" },
423
- title: { type: "string", description: "Max 100 chars; < and > are stripped (YouTube rejects them)." },
426
+ title: {
427
+ type: "string",
428
+ description: "Max 100 chars; < and > are stripped (YouTube rejects them).",
429
+ },
424
430
  description: {
425
431
  type: "string",
426
432
  description:
@@ -845,10 +851,22 @@ const TOOLS = [
845
851
  properties: {
846
852
  id: { type: "string" },
847
853
  path: { type: "string" },
848
- transitionId: { type: "string", description: "Bundled id, e.g. fade-black. From asset_list_transitions." },
849
- file: { type: "string", description: "Custom transition video (path / file:// URL). Use instead of transitionId." },
850
- atMs: { type: "number", description: "Edited-timeline CUT time to center the transition on." },
851
- durationMs: { type: "number", description: "Total length. Default 1000 (native length of the bundled set)." },
854
+ transitionId: {
855
+ type: "string",
856
+ description: "Bundled id, e.g. fade-black. From asset_list_transitions.",
857
+ },
858
+ file: {
859
+ type: "string",
860
+ description: "Custom transition video (path / file:// URL). Use instead of transitionId.",
861
+ },
862
+ atMs: {
863
+ type: "number",
864
+ description: "Edited-timeline CUT time to center the transition on.",
865
+ },
866
+ durationMs: {
867
+ type: "number",
868
+ description: "Total length. Default 1000 (native length of the bundled set).",
869
+ },
852
870
  expectedRevision: { type: "number" },
853
871
  },
854
872
  required: ["atMs"],
@@ -919,8 +937,16 @@ const TOOLS = [
919
937
  description:
920
938
  "1 (mild) - 6 (extreme). Default 2 (1.5×) — soft, modern feel. Use 3-4 for emphasis beats, 5-6 for dramatic punch-ins.",
921
939
  },
922
- focusX: { type: "number", description: "Normalised 0-1 horizontal center. Default 0.5. Ignored when followCursor is true." },
923
- focusY: { type: "number", description: "Normalised 0-1 vertical center. Default 0.5. Ignored when followCursor is true." },
940
+ focusX: {
941
+ type: "number",
942
+ description:
943
+ "Normalised 0-1 horizontal center. Default 0.5. Ignored when followCursor is true.",
944
+ },
945
+ focusY: {
946
+ type: "number",
947
+ description:
948
+ "Normalised 0-1 vertical center. Default 0.5. Ignored when followCursor is true.",
949
+ },
924
950
  followCursor: {
925
951
  type: "boolean",
926
952
  description:
@@ -1053,7 +1079,7 @@ const TOOLS = [
1053
1079
  {
1054
1080
  name: "project_add_clip_transform_region",
1055
1081
  description:
1056
- "Time-bounded transform on the main video clip between startMs and endMs. TWO families of preset: (1) cam-* render the single clip inside a sub-rect of the canvas, freeing the rest for motion graphics (use ONLY on camera-only / user-uploaded recordings; for screen recordings use cursor-telemetry zooms). (2) layout-* swap the WHOLE podcast composite for the window — this is how you change a PODCAST's layout over time WITHIN ONE recording (e.g. side-by-side intro, then host-only while they talk, then back). Outside the region the clip renders with its natural layout. Renderer interpolates over `transitionMs` (default 320ms) at each boundary so the camera/tiles smoothly resize.",
1082
+ "Time-bounded transform on the main video clip between startMs and endMs. TWO families of preset: (1) cam-* render the single clip inside a sub-rect of the canvas, freeing the rest for motion graphics (use ONLY on camera-only / user-uploaded recordings; for screen recordings use cursor-telemetry zooms). (2) podcast layouts swap the WHOLE podcast composite for the window — change which participant(s) are shown over time WITHIN ONE recording. For multi-party (host + up to 3 guests) use the PARTICIPANT-AWARE presets podcast-solo / podcast-pair / podcast-grid / podcast-screen-share with a `participants` list (e.g. show just guest-2, or Kamal+Vimal side by side). Outside the region the clip renders with its natural layout. Renderer interpolates over `transitionMs` (default 320ms) at each boundary so the tiles smoothly resize.",
1057
1083
  inputSchema: {
1058
1084
  type: "object",
1059
1085
  required: ["startMs", "endMs", "preset"],
@@ -1080,9 +1106,19 @@ const TOOLS = [
1080
1106
  "layout-podcast",
1081
1107
  "layout-host-full",
1082
1108
  "layout-guest-full",
1109
+ "podcast-solo",
1110
+ "podcast-pair",
1111
+ "podcast-grid",
1112
+ "podcast-screen-share",
1083
1113
  ],
1084
1114
  description:
1085
- "cam-* (camera reposition into a sub-rect): cam-bottom-half / cam-top-half (9:16); cam-right-portrait / cam-left-portrait (16:9 ~35% card); cam-right-portrait-sm (small ~25% host card — for a full-frame graphic background); cam-bottom-right-quarter / cam-bottom-left-quarter (corner card); cam-right-55/left-55/right-50/left-50 (designed splits). layout-* (PODCAST ONLY — whole-composite swap over time): layout-side-by-side, layout-podcast (two equal tiles), layout-host-full (speaker 1 only), layout-guest-full (speaker 2 only). IMPORTANT: layout-* presets only do anything on a PODCAST clip (kind='podcast' — a clip with BOTH a host and a guest source). On any other clip there is no second speaker, so they are a no-op; use cam-* there. Check clip.kind via project_read before using layout-*.",
1115
+ "cam-* (camera reposition into a sub-rect): cam-bottom-half / cam-top-half (9:16); cam-right-portrait / cam-left-portrait (16:9 ~35% card); cam-right-portrait-sm (small ~25% host card — for a full-frame graphic background); cam-bottom-right-quarter / cam-bottom-left-quarter (corner card); cam-right-55/left-55/right-50/left-50 (designed splits). PODCAST layouts (whole-composite swap over time, PODCAST clips only): the PARTICIPANT-AWARE family is podcast-solo (one person — pass participants=[who]), podcast-pair (two — participants=[left,right]), podcast-grid (everyone, or participants subset), podcast-screen-share (shared screen + participant strip). Legacy 2-person presets layout-side-by-side / layout-podcast / layout-host-full / layout-guest-full still work. IMPORTANT: podcast/layout-* only do anything on a PODCAST clip (kind='podcast'); on other clips use cam-*. Check clip.kind + participants via project_read first.",
1116
+ },
1117
+ participants: {
1118
+ type: "array",
1119
+ items: { type: "string" },
1120
+ description:
1121
+ "Ordered participant speaker ids for the participant-aware podcast-* presets: 'host' | 'guest' | 'guest-2' | 'guest-3'. podcast-solo→[who], podcast-pair→[left,right], podcast-grid/podcast-screen-share→the set to show (omit = everyone). Ignored by cam-* and legacy layout-* presets.",
1086
1122
  },
1087
1123
  transitionMs: {
1088
1124
  type: "number",
@@ -1692,6 +1728,26 @@ const TOOLS = [
1692
1728
  },
1693
1729
  command: "transcript.delete-words",
1694
1730
  },
1731
+ {
1732
+ name: "transcript_restore_words",
1733
+ description:
1734
+ "Restore previously deleted words — removes the trim region(s) covering them, undoing transcript_delete_words / transcript_remove_fillers / repeat removal. THIS is the editor's 'select struck-through words → Restore' workflow, callable from MCP. Silence-removal trims are left untouched.",
1735
+ inputSchema: {
1736
+ type: "object",
1737
+ properties: {
1738
+ id: { type: "string" },
1739
+ path: { type: "string" },
1740
+ wordIds: {
1741
+ type: "array",
1742
+ items: { type: "string" },
1743
+ description: "Word IDs (from transcript_get) that are currently deleted.",
1744
+ },
1745
+ expectedRevision: { type: "number" },
1746
+ },
1747
+ required: ["wordIds"],
1748
+ },
1749
+ command: "transcript.restore-words",
1750
+ },
1695
1751
  {
1696
1752
  name: "transcript_remove_fillers",
1697
1753
  description:
@@ -1758,7 +1814,7 @@ const TOOLS = [
1758
1814
  find: {
1759
1815
  type: "string",
1760
1816
  description:
1761
- "Phrase to search for. Case-insensitive; digits and punctuation are ignored on both sides, so \"than60\" and \"than\" both match the word \"than60\". A pure-number/punctuation phrase can't be targeted (it normalizes to empty) — include a letter word.",
1817
+ 'Phrase to search for. Case-insensitive; digits and punctuation are ignored on both sides, so "than60" and "than" both match the word "than60". A pure-number/punctuation phrase can\'t be targeted (it normalizes to empty) — include a letter word.',
1762
1818
  },
1763
1819
  replace: { type: "string", description: "Replacement text (replaces the whole phrase)" },
1764
1820
  expectedRevision: { type: "number" },
@@ -1882,7 +1938,8 @@ const TOOLS = [
1882
1938
  strokeWidth: { type: "number", description: "Text stroke width in px" },
1883
1939
  fontSize: {
1884
1940
  type: "string",
1885
- description: "Font size in REM, e.g. '2.6rem'. Clamped to 1.0–5.0rem (the editor's slider range).",
1941
+ description:
1942
+ "Font size in REM, e.g. '2.6rem'. Clamped to 1.0–5.0rem (the editor's slider range).",
1886
1943
  },
1887
1944
  expectedRevision: { type: "number" },
1888
1945
  },
@@ -2263,20 +2320,38 @@ const TOOLS = [
2263
2320
  {
2264
2321
  name: "export_generate_thumbnail",
2265
2322
  description:
2266
- "Generate a YouTube thumbnail for an export-library entry via Replicate's gpt-image-2 model. If `prompt` isn't supplied, the local LLM writes one from the transcript (an opinionated YouTube-thumbnail art-director prompt targeting 3:2 aspect, photorealistic or bold-graphic, high contrast, optional short overlay text). Requires the user's Replicate API key to be set in Settings → Integrations (the key is stored encrypted via OS keychain; PandaStudio never sees the user's OpenAI bill). Returns { imagePath, prompt, iterations } on success.",
2323
+ "Generate a YouTube thumbnail for an export-library entry via Replicate's gpt-image-2 model (3:2, photorealistic, high contrast, mobile-readable). Two ways to drive it: (1) pass `subject` (the topic-relevant hero image, 4-10 words) + `hook` (2-4 word overlay) and optionally `reaction`/`layout` — the prompt is assembled deterministically, no transcript needed; or (2) omit them and the local LLM suggests subject/hook/reaction/layout from the transcript. `prompt` overrides everything with a verbatim prompt. `referenceImagePath` is the person's face (passed as input_images). Requires the user's Replicate API key (Settings → Integrations; stored encrypted). Returns { imagePath, prompt, iterations }.",
2267
2324
  inputSchema: {
2268
2325
  type: "object",
2269
2326
  properties: {
2270
2327
  id: { type: "string", description: "Export entry id." },
2271
- prompt: {
2328
+ subject: {
2329
+ type: "string",
2330
+ description:
2331
+ "The topic-relevant hero image, described concretely (4-10 words), e.g. 'a phone showing a first $79 Stripe sale'. When set, no transcript/LLM is needed.",
2332
+ },
2333
+ hook: {
2334
+ type: "string",
2335
+ description: "2-4 word overlay text (auto-uppercased), e.g. 'FIRST SALE'.",
2336
+ },
2337
+ reaction: {
2272
2338
  type: "string",
2273
2339
  description:
2274
- "Optional explicit prompt. If omitted, the local LLM auto-writes one from the transcript.",
2340
+ "Facial reaction: excitement (default) | shock | delight | awe | curiosity | pride | determination | relief.",
2341
+ },
2342
+ layout: {
2343
+ type: "string",
2344
+ description:
2345
+ "Composition: reaction-split (default) | subject-hero | before-after | big-face | product-only | versus.",
2346
+ },
2347
+ prompt: {
2348
+ type: "string",
2349
+ description: "Optional verbatim prompt. Overrides subject/hook/reaction/layout entirely.",
2275
2350
  },
2276
2351
  referenceImagePath: {
2277
2352
  type: "string",
2278
2353
  description:
2279
- "Optional reference image (absolute path or https URL) passed as gpt-image-2 `input_images`.",
2354
+ "Optional reference person image (absolute path or https URL) passed as gpt-image-2 `input_images`.",
2280
2355
  },
2281
2356
  quality: {
2282
2357
  type: "string",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@writepanda/mcp",
3
- "version": "1.76.0",
3
+ "version": "1.79.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",