@writepanda/mcp 1.75.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 +97 -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",
@@ -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" },
@@ -1852,7 +1908,7 @@ const TOOLS = [
1852
1908
  {
1853
1909
  name: "caption_set_style",
1854
1910
  description:
1855
- "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.",
1911
+ "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). fontFamily sets the caption font (a system font like 'Georgia' or a loaded custom font; unloaded fonts fall back). 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.",
1856
1912
  inputSchema: {
1857
1913
  type: "object",
1858
1914
  properties: {
@@ -1863,6 +1919,11 @@ const TOOLS = [
1863
1919
  description: "Max words shown at once. Default varies by template.",
1864
1920
  },
1865
1921
  positionY: { type: "number", description: "Vertical position 0-100. Default 85." },
1922
+ fontFamily: {
1923
+ type: "string",
1924
+ description:
1925
+ "Caption font family, e.g. 'Inter', 'Georgia', or a loaded custom font name. Fonts not loaded in the app fall back to a system default.",
1926
+ },
1866
1927
  color: { type: "string", description: "Caption text color (hex/CSS)" },
1867
1928
  backgroundColor: { type: "string", description: "Caption background color" },
1868
1929
  highlightColor: {
@@ -1877,7 +1938,8 @@ const TOOLS = [
1877
1938
  strokeWidth: { type: "number", description: "Text stroke width in px" },
1878
1939
  fontSize: {
1879
1940
  type: "string",
1880
- 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).",
1881
1943
  },
1882
1944
  expectedRevision: { type: "number" },
1883
1945
  },
@@ -2258,20 +2320,38 @@ const TOOLS = [
2258
2320
  {
2259
2321
  name: "export_generate_thumbnail",
2260
2322
  description:
2261
- "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 }.",
2262
2324
  inputSchema: {
2263
2325
  type: "object",
2264
2326
  properties: {
2265
2327
  id: { type: "string", description: "Export entry id." },
2266
- 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: {
2338
+ type: "string",
2339
+ description:
2340
+ "Facial reaction: excitement (default) | shock | delight | awe | curiosity | pride | determination | relief.",
2341
+ },
2342
+ layout: {
2267
2343
  type: "string",
2268
2344
  description:
2269
- "Optional explicit prompt. If omitted, the local LLM auto-writes one from the transcript.",
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.",
2270
2350
  },
2271
2351
  referenceImagePath: {
2272
2352
  type: "string",
2273
2353
  description:
2274
- "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`.",
2275
2355
  },
2276
2356
  quality: {
2277
2357
  type: "string",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@writepanda/mcp",
3
- "version": "1.75.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",