@writepanda/mcp 1.76.0 → 1.81.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 +136 -17
  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",
@@ -1126,12 +1162,17 @@ const TOOLS = [
1126
1162
  {
1127
1163
  name: "project_delete",
1128
1164
  description:
1129
- "Permanently delete a project file from disk. There is no trash — this is irreversible. Use project_list to confirm the id before deleting.",
1165
+ "Permanently delete a project file from disk. There is no trash — this is irreversible. By default the original source recording is KEPT (only the project is removed). Pass deleteRecording=true to ALSO permanently delete the project's original recording file(s) from disk. Use project_list to confirm the id before deleting.",
1130
1166
  inputSchema: {
1131
1167
  type: "object",
1132
1168
  properties: {
1133
1169
  id: { type: "string" },
1134
1170
  path: { type: "string" },
1171
+ deleteRecording: {
1172
+ type: "boolean",
1173
+ description:
1174
+ "Also permanently delete the project's original source recording file(s). Default false. IRREVERSIBLE.",
1175
+ },
1135
1176
  },
1136
1177
  },
1137
1178
  command: "project.delete",
@@ -1692,6 +1733,26 @@ const TOOLS = [
1692
1733
  },
1693
1734
  command: "transcript.delete-words",
1694
1735
  },
1736
+ {
1737
+ name: "transcript_restore_words",
1738
+ description:
1739
+ "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.",
1740
+ inputSchema: {
1741
+ type: "object",
1742
+ properties: {
1743
+ id: { type: "string" },
1744
+ path: { type: "string" },
1745
+ wordIds: {
1746
+ type: "array",
1747
+ items: { type: "string" },
1748
+ description: "Word IDs (from transcript_get) that are currently deleted.",
1749
+ },
1750
+ expectedRevision: { type: "number" },
1751
+ },
1752
+ required: ["wordIds"],
1753
+ },
1754
+ command: "transcript.restore-words",
1755
+ },
1695
1756
  {
1696
1757
  name: "transcript_remove_fillers",
1697
1758
  description:
@@ -1758,7 +1819,7 @@ const TOOLS = [
1758
1819
  find: {
1759
1820
  type: "string",
1760
1821
  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.",
1822
+ '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
1823
  },
1763
1824
  replace: { type: "string", description: "Replacement text (replaces the whole phrase)" },
1764
1825
  expectedRevision: { type: "number" },
@@ -1882,7 +1943,8 @@ const TOOLS = [
1882
1943
  strokeWidth: { type: "number", description: "Text stroke width in px" },
1883
1944
  fontSize: {
1884
1945
  type: "string",
1885
- description: "Font size in REM, e.g. '2.6rem'. Clamped to 1.0–5.0rem (the editor's slider range).",
1946
+ description:
1947
+ "Font size in REM, e.g. '2.6rem'. Clamped to 1.0–5.0rem (the editor's slider range).",
1886
1948
  },
1887
1949
  expectedRevision: { type: "number" },
1888
1950
  },
@@ -1937,6 +1999,45 @@ const TOOLS = [
1937
1999
  },
1938
2000
  command: "media.generate-image",
1939
2001
  },
2002
+ {
2003
+ name: "media_generate_narration",
2004
+ description:
2005
+ "Generate a voiceover / narration audio clip via Replicate TTS, for promo videos and explainers. Project-agnostic; writes audio to <userData>/narration/ and returns { audioPath, durationMs, model, voice, predictionId }. Canonical workflow: media_generate_narration → project_add_audio (place at startMs, pass the returned durationMs so the overlay is sized to the speech). Models: 'elevenlabs-v3' (default, most expressive — embed inline delivery tags in the text like [excited] or [whispers]) | 'gemini-flash-tts' (30 voices, strong multilingual; 'style' sets tone) | 'minimax-turbo' (fast; 'style' maps to an emotion, 'speed' 0.5-2). Voices — gemini e.g. Kore/Puck/Charon; minimax e.g. Friendly_Person/Wise_Woman/Casual_Guy; omit voice for the model default. Requires the user's Replicate API key (Settings → Integrations).",
2006
+ inputSchema: {
2007
+ type: "object",
2008
+ properties: {
2009
+ text: {
2010
+ type: "string",
2011
+ description:
2012
+ "The script to speak. For elevenlabs-v3 you can embed delivery tags inline, e.g. 'Welcome [excited] to the future of editing.'",
2013
+ },
2014
+ model: {
2015
+ type: "string",
2016
+ description: "'elevenlabs-v3' (default) | 'gemini-flash-tts' | 'minimax-turbo'.",
2017
+ },
2018
+ voice: {
2019
+ type: "string",
2020
+ description:
2021
+ "Voice id/name for the chosen model (gemini: Kore/Puck/...; minimax: Friendly_Person/...). Omit for the model default.",
2022
+ },
2023
+ language: {
2024
+ type: "string",
2025
+ description: "Optional BCP-47 language hint, e.g. 'en-US', 'es-ES'.",
2026
+ },
2027
+ style: {
2028
+ type: "string",
2029
+ description:
2030
+ "Optional tone/emotion hint (e.g. 'warm and upbeat'). Maps to Gemini's style prompt and MiniMax's emotion enum.",
2031
+ },
2032
+ speed: {
2033
+ type: "number",
2034
+ description: "Optional playback speed for MiniMax (0.5-2, 1 = normal).",
2035
+ },
2036
+ },
2037
+ required: ["text"],
2038
+ },
2039
+ command: "media.generate-narration",
2040
+ },
1940
2041
  // `motion_generate` removed in v1.31.0 — see comment above the
1941
2042
  // motion-graphics section.
1942
2043
  {
@@ -2263,20 +2364,38 @@ const TOOLS = [
2263
2364
  {
2264
2365
  name: "export_generate_thumbnail",
2265
2366
  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.",
2367
+ "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
2368
  inputSchema: {
2268
2369
  type: "object",
2269
2370
  properties: {
2270
2371
  id: { type: "string", description: "Export entry id." },
2271
- prompt: {
2372
+ subject: {
2373
+ type: "string",
2374
+ description:
2375
+ "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.",
2376
+ },
2377
+ hook: {
2378
+ type: "string",
2379
+ description: "2-4 word overlay text (auto-uppercased), e.g. 'FIRST SALE'.",
2380
+ },
2381
+ reaction: {
2382
+ type: "string",
2383
+ description:
2384
+ "Facial reaction: excitement (default) | shock | delight | awe | curiosity | pride | determination | relief.",
2385
+ },
2386
+ layout: {
2272
2387
  type: "string",
2273
2388
  description:
2274
- "Optional explicit prompt. If omitted, the local LLM auto-writes one from the transcript.",
2389
+ "Composition: reaction-split (default) | subject-hero | before-after | big-face | product-only | versus.",
2390
+ },
2391
+ prompt: {
2392
+ type: "string",
2393
+ description: "Optional verbatim prompt. Overrides subject/hook/reaction/layout entirely.",
2275
2394
  },
2276
2395
  referenceImagePath: {
2277
2396
  type: "string",
2278
2397
  description:
2279
- "Optional reference image (absolute path or https URL) passed as gpt-image-2 `input_images`.",
2398
+ "Optional reference person image (absolute path or https URL) passed as gpt-image-2 `input_images`.",
2280
2399
  },
2281
2400
  quality: {
2282
2401
  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.81.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",