@writepanda/mcp 1.216.0 → 1.218.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 +481 -42
  2. package/package.json +1 -1
package/bin/server.mjs CHANGED
@@ -626,8 +626,15 @@ const MOTION_ELEMENT_PROPS = {
626
626
  sound: {
627
627
  type: "string",
628
628
  enum: ["auto", "none", "pop", "whoosh", "hit", "tick", "bell"],
629
- description: "Role for project_compose_soundtrack.",
629
+ description: "Role for project_compose_soundtrack (not a sound of its own; that is soundUrl).",
630
630
  },
631
+ soundUrl: {
632
+ type: ["string", "null"],
633
+ description:
634
+ "The element's own sound, kept on it through moves and trims: bundled:sound/<id> or a path; null/none = silent. Omit = default graphic sound (click on chip/steps/count/lowerThird/highlight/endCard/sticker, or the workspace default).",
635
+ },
636
+ soundVolume: { type: "number", description: "0-1." },
637
+ soundBeats: { type: "boolean", description: "steps: play soundUrl as each item steps in." },
631
638
  zIndex: {
632
639
  type: "number",
633
640
  description:
@@ -815,18 +822,18 @@ const TOOLS = [
815
822
  {
816
823
  name: "system_get_transcription_provider",
817
824
  description:
818
- "Transcription provider (local default, deepgram, elevenlabs; ready = cloud ones with a key). Local is weak on Tamil / Telugu / Kannada / Malayalam.",
825
+ "Transcription provider (local default, apple, deepgram, elevenlabs; ready = cloud ones with a key + whether Apple speech runs here; apple.languages = what Apple can do on this Mac, fillers:false = leaves out um/uh). Local is weak on Tamil / Telugu / Kannada / Malayalam; Apple doesn't support them.",
819
826
  inputSchema: { type: "object", properties: {} },
820
827
  command: "system.get-transcription-provider",
821
828
  },
822
829
  {
823
830
  name: "system_set_transcription_provider",
824
831
  description:
825
- "Set the transcription provider. Cloud needs its key saved (else falls back to local), uploads audio and bills the user: ASK first.",
832
+ "Set the transcription provider. apple = Apple's on-device speech (Macs; macOS 26 + Apple silicon: 4-9x faster than local for English/German/Spanish/French/Italian/Portuguese/Chinese/Japanese/Korean, keeps fewer fillers; no Tamil/Telugu/Kannada/Malayalam), needs a chosen language, only when the user wants it. Cloud needs its key saved (else falls back to local), uploads audio and bills the user: ASK first.",
826
833
  inputSchema: {
827
834
  type: "object",
828
835
  properties: {
829
- provider: { type: "string", enum: ["local", "deepgram", "elevenlabs"] },
836
+ provider: { type: "string", enum: ["local", "apple", "deepgram", "elevenlabs"] },
830
837
  },
831
838
  required: ["provider"],
832
839
  },
@@ -917,7 +924,7 @@ const TOOLS = [
917
924
  {
918
925
  name: "connector_list",
919
926
  description:
920
- "List MCP connectors and their state. If the one needed isn't connected, tell the user its howToConnect line; you cannot connect connectors.",
927
+ "List MCP connectors and their state. If the one needed isn't connected, tell the user its howToConnect line; you cannot connect connectors. status \"connected, last check failed\" (connected:false, lastCheck with reason, host and fix) means PandaStudio's own last use of it failed.",
921
928
  inputSchema: {
922
929
  type: "object",
923
930
  properties: {
@@ -1085,7 +1092,7 @@ const TOOLS = [
1085
1092
  {
1086
1093
  name: "workspace_capture_brand",
1087
1094
  description:
1088
- "Capture a brand kit (colours, fonts, logo, screenshots) from a website. ASYNC: { jobId }; first run is slow.",
1095
+ "Capture a brand kit (colours, fonts, logo, screenshots) from a website using the app’s bundled browser. ASYNC: { jobId }; requires site network access, no npm CLI setup.",
1089
1096
  inputSchema: {
1090
1097
  type: "object",
1091
1098
  properties: {
@@ -1136,12 +1143,39 @@ const TOOLS = [
1136
1143
  motionBlurAmount: { type: "number" },
1137
1144
  },
1138
1145
  },
1146
+ graphicSound: {
1147
+ type: ["object", "null"],
1148
+ description: "Default graphic sound for new graphics.",
1149
+ properties: {
1150
+ soundUrl: { type: ["string", "null"] },
1151
+ volume: { type: "number" },
1152
+ scope: { type: "string", enum: ["cards", "all"] },
1153
+ },
1154
+ },
1139
1155
  },
1140
1156
  },
1141
1157
  },
1142
1158
  },
1143
1159
  command: "workspace.set-project-defaults",
1144
1160
  },
1161
+ {
1162
+ name: "workspace_set_default_graphic_sound",
1163
+ description:
1164
+ "Set the sound NEW motion graphics and motion elements start with (existing ones keep theirs). none = silent; scope cards (default) or all; reset = built-in click on cards and callouts.",
1165
+ inputSchema: {
1166
+ type: "object",
1167
+ properties: {
1168
+ sound: {
1169
+ type: "string",
1170
+ description: 'bundled:sound/<id>, a bundled id, a path, or "none".',
1171
+ },
1172
+ volume: { type: "number", description: "0-1." },
1173
+ scope: { type: "string", enum: ["cards", "all"] },
1174
+ reset: { type: "boolean" },
1175
+ },
1176
+ },
1177
+ command: "workspace.set-default-graphic-sound",
1178
+ },
1145
1179
 
1146
1180
  // ── YouTube publishing (v1.19) ──────────────────────────────────
1147
1181
  // Connect Google accounts per workspace, publish exports, edit
@@ -1436,10 +1470,48 @@ const TOOLS = [
1436
1470
  },
1437
1471
  command: "project.show",
1438
1472
  },
1473
+ {
1474
+ name: "matte_status",
1475
+ description:
1476
+ "Read baked matte status: none, baking with percent, ready; engine (vision | rvm), quality, fallbackReason.",
1477
+ inputSchema: {
1478
+ type: "object",
1479
+ properties: {
1480
+ id: { type: "string" },
1481
+ path: { type: "string" },
1482
+ clip: { type: "string" },
1483
+ source: { type: "string", enum: ["camera", "main"] },
1484
+ },
1485
+ required: ["clip"],
1486
+ },
1487
+ command: "matte.status",
1488
+ },
1489
+ {
1490
+ name: "matte_bake",
1491
+ description:
1492
+ "Bake a camera/source matte. Returns jobId; use job_wait to await, job_cancel to cancel. engine auto = Apple Vision on macOS 12+ (about 1 s per video-second), RVM elsewhere; an explicit engine/quality becomes the source's matte and rebakes if different.",
1493
+ inputSchema: {
1494
+ type: "object",
1495
+ properties: {
1496
+ id: { type: "string" },
1497
+ path: { type: "string" },
1498
+ clip: { type: "string" },
1499
+ source: { type: "string", enum: ["camera", "main"] },
1500
+ engine: { type: "string", enum: ["auto", "vision", "rvm"] },
1501
+ quality: {
1502
+ type: "string",
1503
+ enum: ["accurate", "balanced"],
1504
+ description: "Apple Vision only. balanced is ~3x faster with softer edges.",
1505
+ },
1506
+ },
1507
+ required: ["id", "clip"],
1508
+ },
1509
+ command: "matte.bake",
1510
+ },
1439
1511
  {
1440
1512
  name: "project_read",
1441
1513
  description:
1442
- "Full project JSON plus clipStates (per clip: transcribed, audioCleaned), editedDurationMs and isInActiveWorkspace (false: tell the user before export). Clips: project.mainTrack.clips; regions: project.editor.*Regions. Pass project.revision as expectedRevision on writes.",
1514
+ "Full project JSON plus clipStates (per clip: transcribed, audioCleaned, matte.main/camera bake status), editedDurationMs and isInActiveWorkspace (false: tell the user before export). Clips: project.mainTrack.clips; regions: project.editor.*Regions. Pass project.revision as expectedRevision on writes.",
1443
1515
  inputSchema: {
1444
1516
  type: "object",
1445
1517
  properties: {
@@ -1455,7 +1527,8 @@ const TOOLS = [
1455
1527
  },
1456
1528
  {
1457
1529
  name: "project_new",
1458
- description: "Create a project; withMedia imports videos as clips.",
1530
+ description:
1531
+ "Create a project; withMedia imports videos as clips, or AUDIO files (MP3 / WAV / M4A / AAC / FLAC / OGG) as an audio-only podcast project. speakers = one audio file per speaker (host first): a multi-speaker audio clip synced by audio.",
1459
1532
  inputSchema: {
1460
1533
  type: "object",
1461
1534
  properties: {
@@ -1463,7 +1536,17 @@ const TOOLS = [
1463
1536
  parentProjectId: { type: "string" },
1464
1537
  originKind: { type: "string", enum: ["agent", "recipe"] },
1465
1538
  name: { type: "string" },
1466
- withMedia: { type: "array", items: { type: "string" } },
1539
+ withMedia: {
1540
+ type: "array",
1541
+ items: { type: "string" },
1542
+ description: "Videos, or audio files (an audio-only project). Not both.",
1543
+ },
1544
+ speakers: {
1545
+ type: "array",
1546
+ items: { type: "string" },
1547
+ description: "2-4 audio files, one per speaker, host first.",
1548
+ },
1549
+ autoSync: { type: "boolean", description: "speakers: sync by audio (default true)." },
1467
1550
  },
1468
1551
  required: ["name"],
1469
1552
  },
@@ -1472,17 +1555,23 @@ const TOOLS = [
1472
1555
  {
1473
1556
  name: "project_fork_from_shot",
1474
1557
  description:
1475
- "Fork an export's AI shot (entry.generatedShots) into a new 9:16 project over the shot's range; keeps timing edits, drops all overlays. revision_mismatch = source edited since: confirm, then force.",
1558
+ "Fork an AI shot into a new 9:16 project over the shot's range; keeps timing edits, drops all overlays. From an export: exportId + shotId (entry.generatedShots). From an unexported project: projectId + shotId (export_generate_shots with projectId), or projectId + sourceStartMs/sourceEndMs + title. revision_mismatch = source edited since export: confirm, then force.",
1476
1559
  inputSchema: {
1477
1560
  type: "object",
1478
1561
  properties: {
1479
1562
  folderId: { type: ["string", "null"], description: "Folder UUID" },
1480
- exportId: { type: "string" },
1563
+ exportId: { type: "string", description: "Export entry id (or use projectId)." },
1564
+ projectId: { type: "string", description: "Project id or path (no export needed)." },
1481
1565
  shotId: { type: "string" },
1566
+ sourceStartMs: {
1567
+ type: "number",
1568
+ description: "With projectId, no shotId: shot start on the SOURCE timeline (ms).",
1569
+ },
1570
+ sourceEndMs: { type: "number", description: "With projectId, no shotId: shot end (ms)." },
1571
+ title: { type: "string", description: "Title for a source-range fork." },
1482
1572
  aspectRatio: { type: "string", description: "Default 9:16." },
1483
1573
  force: { type: "boolean" },
1484
1574
  },
1485
- required: ["exportId", "shotId"],
1486
1575
  },
1487
1576
  command: "project.fork-from-shot",
1488
1577
  },
@@ -1503,13 +1592,13 @@ const TOOLS = [
1503
1592
  {
1504
1593
  name: "project_add_clip",
1505
1594
  description:
1506
- "Append or insert a video clip, or a still image shown for durationMs, on the main track (later content moves with it). atMs (edited time) inserts at that moment, splitting the clip under it; add replaceToMs to replace [atMs, replaceToMs) with the clip (e.g. a re-recorded take).",
1595
+ "Append or insert a video clip, an audio file (audio-only projects only), or a still image shown for durationMs, on the main track (later content moves with it). atMs (edited time) inserts at that moment, splitting the clip under it; add replaceToMs to replace [atMs, replaceToMs) with the clip (e.g. a re-recorded take).",
1507
1596
  inputSchema: {
1508
1597
  type: "object",
1509
1598
  properties: {
1510
1599
  id: { type: "string" },
1511
1600
  path: { type: "string" },
1512
- media: { type: "string", description: "Video or image path." },
1601
+ media: { type: "string", description: "Video, audio (audio-only project) or image path." },
1513
1602
  durationMs: { type: "number", description: "Images, default 5000." },
1514
1603
  fill: { type: "boolean", description: "Images: cover the frame (default true)." },
1515
1604
  webcam: { type: "string", description: "Guest source: a two-speaker podcast clip." },
@@ -1548,7 +1637,7 @@ const TOOLS = [
1548
1637
  {
1549
1638
  name: "project_add_podcast_clip",
1550
1639
  description:
1551
- "Combine 2-4 separate recordings (media = host, guests in order) into one podcast clip, auto-synced by audio.",
1640
+ "Combine 2-4 separate recordings (media = host, guests in order) into one podcast clip, auto-synced by audio. All audio files = an audio-only multi-speaker clip.",
1552
1641
  inputSchema: {
1553
1642
  type: "object",
1554
1643
  properties: {
@@ -1725,7 +1814,7 @@ const TOOLS = [
1725
1814
  {
1726
1815
  name: "project_add_motion_graphic",
1727
1816
  description:
1728
- "Place a motion graphic, video or image as an overlay, from fromJob (a render job) or file. Plays a click SFX unless soundUrl is null.",
1817
+ "Place a motion graphic, video or image as an overlay, from fromJob (a render job) or file. It owns its sound (moves with it): omit soundUrl for the default graphic sound (a click on card/callout templates or the workspace default), null = silent.",
1729
1818
  inputSchema: {
1730
1819
  type: "object",
1731
1820
  properties: {
@@ -1737,6 +1826,11 @@ const TOOLS = [
1737
1826
  atMs: { type: "number", description: "Default end of timeline." },
1738
1827
  soundUrl: { type: ["string", "null"], description: "bundled:sound/<id>; null = silent." },
1739
1828
  soundVolume: { type: "number", description: "0-1." },
1829
+ soundBeats: {
1830
+ type: "boolean",
1831
+ description:
1832
+ "Beat templates (flowchart, comparison, list, glow-steps): sound on each step.",
1833
+ },
1740
1834
  loop: { type: "boolean", description: "Else hold the last frame." },
1741
1835
  muted: { type: "boolean", description: "Own audio; default true." },
1742
1836
  volume: { type: "number", description: "0-1, when unmuted." },
@@ -2528,6 +2622,27 @@ const TOOLS = [
2528
2622
  },
2529
2623
  command: "project.track-focus-face",
2530
2624
  },
2625
+ {
2626
+ name: "project_track_region",
2627
+ description:
2628
+ "Track this area: a focus region follows whatever is inside its box (a moving window, a dragged card, a scrolling email) forward and backward from atMs, by correlation tracking; stops where the content is lost and holds there. Writes keyframes (tracking: area). Needs the media engine. Returns { keyframes, samples, lostForwardAtMs, lostBackwardAtMs }.",
2629
+ inputSchema: {
2630
+ type: "object",
2631
+ properties: {
2632
+ id: { type: "string" },
2633
+ path: { type: "string" },
2634
+ regionId: { type: "string", description: "editor.spotlightRegions[].id" },
2635
+ atMs: {
2636
+ type: "number",
2637
+ description: "Edited ms of the frame the box marks. Default the region start.",
2638
+ },
2639
+ stepMs: { type: "number", minimum: 20, maximum: 1000, description: "Default 66." },
2640
+ expectedRevision: { type: "number" },
2641
+ },
2642
+ required: ["regionId"],
2643
+ },
2644
+ command: "project.track-region",
2645
+ },
2531
2646
  {
2532
2647
  name: "project_set_overlay_mask",
2533
2648
  description:
@@ -2686,6 +2801,44 @@ const TOOLS = [
2686
2801
  },
2687
2802
  command: "project.apply-edit-plan",
2688
2803
  },
2804
+ {
2805
+ name: "audio_preview_mix",
2806
+ description:
2807
+ "Render final export audio only to temporary .m4a plus description and voice/music/SFX stem RMS/masking flags. Edited-time range, default whole project, max 10 minutes. Async: job.wait.",
2808
+ inputSchema: {
2809
+ type: "object",
2810
+ properties: {
2811
+ id: { type: "string" },
2812
+ fromMs: { type: "number", minimum: 0 },
2813
+ toMs: { type: "number", minimum: 0 },
2814
+ },
2815
+ required: ["id"],
2816
+ },
2817
+ command: "audio.preview-mix",
2818
+ },
2819
+ {
2820
+ name: "audio_describe",
2821
+ description:
2822
+ "Numeric audio evidence for a local audio/video path OR project id/range: loudness, true peak, clipping, silence, speech/music estimate, tempo/beats, attacks, spectral brightness and summary. No song/sound identification. Async: job.wait; first 10 minutes maximum.",
2823
+ inputSchema: {
2824
+ type: "object",
2825
+ properties: {
2826
+ path: { type: "string" },
2827
+ id: { type: "string" },
2828
+ fromMs: { type: "number", minimum: 0 },
2829
+ toMs: { type: "number", minimum: 0 },
2830
+ },
2831
+ oneOf: [
2832
+ {
2833
+ required: ["path"],
2834
+ not: { anyOf: [{ required: ["id"] }, { required: ["fromMs"] }, { required: ["toMs"] }] },
2835
+ },
2836
+ { required: ["id"], not: { required: ["path"] } },
2837
+ ],
2838
+ },
2839
+ command: "audio.describe",
2840
+ },
2841
+
2689
2842
  {
2690
2843
  name: "audio_probe",
2691
2844
  description:
@@ -2720,6 +2873,31 @@ const TOOLS = [
2720
2873
  },
2721
2874
  command: "project.set-region-sound",
2722
2875
  },
2876
+ {
2877
+ name: "project_set_graphic_sound",
2878
+ description:
2879
+ "Set the sound a graphic owns (motion graphic overlay or motion element): plays when it appears, or with beats on each step/item/card, and moves with it. none = silent.",
2880
+ inputSchema: {
2881
+ type: "object",
2882
+ properties: {
2883
+ id: { type: "string" },
2884
+ path: { type: "string" },
2885
+ overlayId: { type: "string", description: "Overlay id or motion element id." },
2886
+ sound: {
2887
+ type: "string",
2888
+ description: 'bundled:sound/<id>, a bundled id, a path, or "none".',
2889
+ },
2890
+ volume: { type: "number", description: "0-1." },
2891
+ beats: {
2892
+ type: "boolean",
2893
+ description: "Each beat (flowchart, comparison, list, glow-steps; steps elements).",
2894
+ },
2895
+ expectedRevision: { type: "number" },
2896
+ },
2897
+ required: ["overlayId"],
2898
+ },
2899
+ command: "project.set-graphic-sound",
2900
+ },
2723
2901
  {
2724
2902
  name: "project_add_trim",
2725
2903
  description: "Mark a span the exporter skips (manual cut).",
@@ -2774,6 +2952,24 @@ const TOOLS = [
2774
2952
  },
2775
2953
  command: "project.add-freeze-frame",
2776
2954
  },
2955
+ {
2956
+ name: "project_remove_pause",
2957
+ description:
2958
+ "Remove inserted pauses (transcript_insert_pause / transcript_space_out) by regionId(s), by the word they follow (afterWordId), or all=true. Freeze frames and speed changes are never touched. Returns { removed, notFound }.",
2959
+ inputSchema: {
2960
+ type: "object",
2961
+ properties: {
2962
+ id: { type: "string" },
2963
+ path: { type: "string" },
2964
+ regionId: { type: "string" },
2965
+ regionIds: { type: "array", items: { type: "string" } },
2966
+ afterWordId: { type: "string" },
2967
+ all: { type: "boolean" },
2968
+ expectedRevision: { type: "number" },
2969
+ },
2970
+ },
2971
+ command: "project.remove-pause",
2972
+ },
2777
2973
  {
2778
2974
  name: "project_add_reverse",
2779
2975
  description:
@@ -3141,7 +3337,7 @@ const TOOLS = [
3141
3337
  patch: {
3142
3338
  type: "object",
3143
3339
  description:
3144
- "e.g. { depth: 4 }; null clears a value. zIndex (overlay / annotation / fx): stack order, higher on top; overlays, annotations and front motion elements share one stack at group base + zIndex (B-roll 0, overlays 1000, annotations 2000, motion elements 3000).",
3340
+ "e.g. { depth: 4 }. Annotation: fontSize, fontFamily, textColor (alias color), backgroundColor, fontWeight, fontStyle, textDecoration, textAlign, flat or nested under style/styles; merges into canonical style, preserving other fields. Flat wins over styles, then style. null clears a value. zIndex (overlay / annotation / fx): stack order, higher on top; overlays, annotations and front motion elements share one stack at group base + zIndex (B-roll 0, overlays 1000, annotations 2000, motion elements 3000).",
3145
3341
  },
3146
3342
  expectedRevision: { type: "number" },
3147
3343
  },
@@ -3397,7 +3593,8 @@ const TOOLS = [
3397
3593
  },
3398
3594
  {
3399
3595
  name: "project_set_wallpaper",
3400
- description: "Set the background wallpaper.",
3596
+ description:
3597
+ "Set the background: a wallpaper value, or source=desktop (a kept still of the user's desktop wallpaper) / source=edges (Match video edges: a soft gradient from the video's border colours, slowly following the content; same in preview and export).",
3401
3598
  inputSchema: {
3402
3599
  type: "object",
3403
3600
  properties: {
@@ -3405,14 +3602,66 @@ const TOOLS = [
3405
3602
  path: { type: "string" },
3406
3603
  wallpaper: {
3407
3604
  type: "string",
3408
- description: "Bundled id (e.g. gradient-blue) or 'none'.",
3605
+ description:
3606
+ "Bundled id (e.g. gradient-blue), color, CSS gradient or 'none'. Omit with source.",
3409
3607
  },
3608
+ source: { type: "string", enum: ["desktop", "edges"] },
3410
3609
  expectedRevision: { type: "number" },
3411
3610
  },
3412
- required: ["wallpaper"],
3413
3611
  },
3414
3612
  command: "project.set-wallpaper",
3415
3613
  },
3614
+ {
3615
+ name: "project_set_cover_art",
3616
+ description:
3617
+ "Audio-only project: cover art image (square; shown on the audio canvas, embedded in MP3 / M4A exports). image='none' removes it.",
3618
+ inputSchema: {
3619
+ type: "object",
3620
+ properties: {
3621
+ id: { type: "string" },
3622
+ path: { type: "string" },
3623
+ image: { type: "string", description: "Absolute PNG / JPEG / WebP path, or 'none'." },
3624
+ expectedRevision: { type: "number" },
3625
+ },
3626
+ required: ["image"],
3627
+ },
3628
+ command: "project.set-cover-art",
3629
+ },
3630
+ {
3631
+ name: "project_set_episode_info",
3632
+ description:
3633
+ "Audio-only project: show name, episode title and number (canvas titles + exported ID3 / M4A tags). Empty string clears.",
3634
+ inputSchema: {
3635
+ type: "object",
3636
+ properties: {
3637
+ id: { type: "string" },
3638
+ path: { type: "string" },
3639
+ show: { type: "string" },
3640
+ title: { type: "string" },
3641
+ episode: { type: "string" },
3642
+ expectedRevision: { type: "number" },
3643
+ },
3644
+ },
3645
+ command: "project.set-episode-info",
3646
+ },
3647
+ {
3648
+ name: "project_set_audio_stage",
3649
+ description:
3650
+ "Audio-only project canvas: style waveform (cover art + live waveform + speaker) or cover (still), accent #rrggbb, showTitle, showSpeaker.",
3651
+ inputSchema: {
3652
+ type: "object",
3653
+ properties: {
3654
+ id: { type: "string" },
3655
+ path: { type: "string" },
3656
+ style: { type: "string", enum: ["waveform", "cover"] },
3657
+ accent: { type: "string", description: "#rrggbb or 'none'." },
3658
+ showTitle: { type: "boolean" },
3659
+ showSpeaker: { type: "boolean" },
3660
+ expectedRevision: { type: "number" },
3661
+ },
3662
+ },
3663
+ command: "project.set-audio-stage",
3664
+ },
3416
3665
  {
3417
3666
  name: "project_set_style",
3418
3667
  description:
@@ -3428,6 +3677,11 @@ const TOOLS = [
3428
3677
  motionBlurAmount: { type: "number", description: "0-100." },
3429
3678
  showBlur: { type: "boolean", description: "Blurred background." },
3430
3679
  cursorScale: { type: "number", description: "0-5, 0 = off." },
3680
+ cursorStyle: {
3681
+ type: "string",
3682
+ enum: ["classic", "modern", "bold", "soft"],
3683
+ description: "Drawn-cursor style set (needs cursorScale > 0).",
3684
+ },
3431
3685
  cursorHideIdle: { type: "boolean" },
3432
3686
  cursorIdleSeconds: { type: "number", description: "0.5-10, default 2." },
3433
3687
  cursorHideDuringZoom: { type: "boolean" },
@@ -3804,13 +4058,13 @@ const TOOLS = [
3804
4058
  {
3805
4059
  name: "system_set_transcription_preferences",
3806
4060
  description:
3807
- "Save workspace language and provider atomically. Confirm consent to upload audio for cloud. Cloud cannot use auto.",
4061
+ "Save workspace language and provider atomically. Confirm consent to upload audio for cloud. Cloud and apple cannot use auto.",
3808
4062
  inputSchema: {
3809
4063
  type: "object",
3810
4064
  required: ["language", "provider"],
3811
4065
  properties: {
3812
4066
  language: { type: "string" },
3813
- provider: { type: "string", enum: ["local", "deepgram", "elevenlabs"] },
4067
+ provider: { type: "string", enum: ["local", "apple", "deepgram", "elevenlabs"] },
3814
4068
  },
3815
4069
  },
3816
4070
  command: "system.set-transcription-preferences",
@@ -3829,12 +4083,13 @@ const TOOLS = [
3829
4083
  language: {
3830
4084
  type: "string",
3831
4085
  description:
3832
- "Per-run spoken language name (english, tamil, etc.). Cloud cannot use auto.",
4086
+ "Per-run spoken language name (english, tamil, etc.). Cloud and apple cannot use auto.",
3833
4087
  },
3834
4088
  provider: {
3835
4089
  type: "string",
3836
- enum: ["local", "deepgram", "elevenlabs"],
3837
- description: "Per-run provider. Audio is sent to the selected cloud provider.",
4090
+ enum: ["local", "apple", "deepgram", "elevenlabs"],
4091
+ description:
4092
+ "Per-run provider. apple stays on this Mac. Audio is sent to the selected cloud provider.",
3838
4093
  },
3839
4094
  force: { type: "boolean", description: "Re-transcribe clips that already have words." },
3840
4095
  },
@@ -3896,9 +4151,58 @@ const TOOLS = [
3896
4151
  },
3897
4152
  command: "transcript.insert-words",
3898
4153
  },
4154
+ {
4155
+ name: "transcript_insert_pause",
4156
+ description:
4157
+ "Add a natural pause after a word: ms of extra time filled with the take's own room tone (silence only if the take has no pause anywhere); video holds the frame. Everything after moves later. A pause already after that word gets the new length. Can't go inside a speed change. Returns { regionId, anchorMs, roomTone, updated }.",
4158
+ inputSchema: {
4159
+ type: "object",
4160
+ properties: {
4161
+ id: { type: "string" },
4162
+ path: { type: "string" },
4163
+ afterWordId: {
4164
+ type: "string",
4165
+ description: "Word the pause follows (or its transcript_get prefix).",
4166
+ },
4167
+ sourceMs: { type: "number", description: "Or pause at this SOURCE ms instead." },
4168
+ ms: {
4169
+ type: "number",
4170
+ minimum: 30,
4171
+ maximum: 60000,
4172
+ description: "Pause length, output ms (250, 500, 1000, 2000 typical).",
4173
+ },
4174
+ expectedRevision: { type: "number" },
4175
+ },
4176
+ required: ["ms"],
4177
+ },
4178
+ command: "transcript.insert-pause",
4179
+ },
4180
+ {
4181
+ name: "transcript_space_out",
4182
+ description:
4183
+ "Natural pacing for TTS / ElevenLabs narration that runs sentences together: tops up the gap after commas, sentences and at paragraph / speaker changes to the preset's target with room-tone pauses (natural: 0.15 / 0.42 / 0.7 s; tight: 0.12 / 0.35 / 0.6; relaxed: 0.2 / 0.5 / 0.8). Never shortens a gap; rerunning changes nothing. dryRun returns the plan (pauses, addedMs, items) without writing. One revision.",
4184
+ inputSchema: {
4185
+ type: "object",
4186
+ properties: {
4187
+ id: { type: "string" },
4188
+ path: { type: "string" },
4189
+ preset: {
4190
+ type: "string",
4191
+ enum: ["natural", "relaxed", "tight"],
4192
+ description: "Default natural.",
4193
+ },
4194
+ fromMs: { type: "number", description: "Edited-time window start." },
4195
+ toMs: { type: "number", description: "Edited-time window end (exclusive)." },
4196
+ dryRun: { type: "boolean" },
4197
+ expectedRevision: { type: "number" },
4198
+ },
4199
+ },
4200
+ command: "transcript.space-out",
4201
+ },
3899
4202
  {
3900
4203
  name: "transcript_restore_words",
3901
- description: "Restore deleted words (removes their time from the covering cuts).",
4204
+ description:
4205
+ "Restore cut words (removes their time from every covering cut, silence cuts included). wordsRestored counts words that were cut and now are not; notCut lists ids that were not cut.",
3902
4206
  inputSchema: {
3903
4207
  type: "object",
3904
4208
  properties: {
@@ -3988,7 +4292,7 @@ const TOOLS = [
3988
4292
  {
3989
4293
  name: "transcript_remove_silences",
3990
4294
  description:
3991
- "Remove measured quiet runs with a 50ms decay hold. Returns snapped/skipped/warnings; snapToAudio=false uses legacy timing.",
4295
+ "Remove measured quiet runs inside transcript pauses (never into a word) with a 50ms decay hold. Counts only visible time (nothing under a head trim or existing cut). Returns snapped/skipped/warnings; snapToAudio=false uses legacy timing.",
3992
4296
  inputSchema: {
3993
4297
  type: "object",
3994
4298
  properties: {
@@ -4011,30 +4315,76 @@ const TOOLS = [
4011
4315
  },
4012
4316
 
4013
4317
  // ── audio ───────────────────────────────────────────────────────
4318
+ {
4319
+ name: "audio_enhance",
4320
+ description:
4321
+ "Enhance voice: THE audio cleanup step (one call). Runs the studio chain on speech, per source (a podcast's host and each guest separately): noise removal, coughs/bumps out of pauses, mouth-click repair, broadcast tone, boom control, level riding (lifts very quiet speech too), de-esser, quieter pauses, -16 LUFS / -1 dBTP. strength: natural (default), light (gentle tidy), strong (assertive broadcast). keepBackground true for vlogs / ambience where the room is part of the video (no noise removal, event remover or pause expander; level, EQ and loudness still run). Enhance voice already removes room echo with the Studio AI model; only use echo when the result reports the model did not run (fallback) (denoiser deepfilternet3, or keepBackground); when the model runs, echo is skipped and the result says echoSkipped: ai-model-removes-echo (relay echoSkippedReason, do not retry). restore true = Restore voice quality (TF-Restormer) for thin, muffled or call-quality mics only (Apple silicon; slow; never on a good mic). reference = a clip id or file whose voice tone to match. off true restores the original audio. The noise-removal engine is automatic. Every run processes the original media, so running again replaces the result (never stacks). ASYNC: { jobId }, job_wait; the result reports before/after loudness (LUFS, LRA, true peak, speech over pauses, tone deviation) and the engine per source: tell the user.",
4322
+ inputSchema: {
4323
+ type: "object",
4324
+ properties: {
4325
+ id: { type: "string" },
4326
+ path: { type: "string" },
4327
+ clipId: { type: "string", description: "Only this clip. Default: every clip." },
4328
+ source: {
4329
+ type: "string",
4330
+ description: '"all" (default), "host", or a guest speaker id ("guest", "guest-2").',
4331
+ },
4332
+ strength: {
4333
+ type: "string",
4334
+ enum: ["light", "natural", "strong"],
4335
+ default: "natural",
4336
+ },
4337
+ keepBackground: {
4338
+ type: "boolean",
4339
+ default: false,
4340
+ description: "Keep the room sound (vlogs, ambience).",
4341
+ },
4342
+ echo: {
4343
+ type: "boolean",
4344
+ default: false,
4345
+ description:
4346
+ "Reduce room echo. Enhance voice already removes room echo with the Studio AI model; only use echo when the result reports the model did not run (fallback). Skipped (echoSkipped) whenever the model runs.",
4347
+ },
4348
+ restore: {
4349
+ type: "boolean",
4350
+ default: false,
4351
+ description: "Restore voice quality for a thin / low-bandwidth mic (TF-Restormer).",
4352
+ },
4353
+ reference: { type: "string", description: "Clip id or audio/video file to sound like." },
4354
+ off: { type: "boolean", description: "Remove the enhancement (original audio)." },
4355
+ preset: {
4356
+ type: "string",
4357
+ enum: ["voice", "noise-reduction", "podcast", "off"],
4358
+ description: "Deprecated (2.1). Use strength / keepBackground / off.",
4359
+ },
4360
+ },
4361
+ },
4362
+ command: "audio.enhance",
4363
+ },
4014
4364
  {
4015
4365
  name: "audio_clean",
4016
4366
  description:
4017
- "Denoise each clip’s audio, optionally reducing room echo. Speech below -30 LUFS is boosted to -16 LUFS / -1.5 dBTP by default; warnings explain boosts. Original kept; audio_reset_clean restores it. ASYNC: { jobId }, job_wait. Concurrent saves are merged and retried (three attempts); exhausted conflicts report code revision_conflict / status 409.",
4367
+ "Deprecated alias of audio_enhance strength light (noise removal, gentle level and tone, -16 LUFS), echo true adds Reduce echo only when the Studio AI model does not run (it already removes room echo; the result then says echoSkipped); clips already enhanced are skipped unless clipId is given. Prefer audio_enhance. ASYNC: { jobId }, job_wait.",
4018
4368
  inputSchema: {
4019
4369
  type: "object",
4020
4370
  properties: {
4021
4371
  id: { type: "string" },
4022
4372
  path: { type: "string" },
4023
4373
  clipId: { type: "string" },
4024
- echo: { type: "boolean", description: "Only when the user mentions echo / reverb." },
4025
- normalize: {
4374
+ echo: {
4026
4375
  type: "boolean",
4027
- description: "Default true: boost very quiet speech. Set false to opt out.",
4376
+ description:
4377
+ "Enhance voice already removes room echo with the Studio AI model; only use echo when the result reports the model did not run (fallback).",
4028
4378
  },
4379
+ normalize: { type: "boolean", description: "Ignored (quiet speech is always lifted)." },
4029
4380
  },
4030
4381
  },
4031
4382
  command: "audio.clean",
4032
4383
  },
4033
-
4034
4384
  {
4035
4385
  name: "audio_normalize",
4036
4386
  description:
4037
- "Normalize a clip’s active audio without denoising. Default -16 LUFS, -1.5 dBTP ceiling. Silent audio skipped with warnings. Original kept. ASYNC: job_wait.",
4387
+ "Loudness only: normalize a clip's audio to targetLufs (default -16, -1.5 dBTP ceiling) with no other processing. An enhanced clip is skipped (audio_enhance already sets -16 LUFS); prefer audio_enhance. Silent audio skipped with warnings. Original kept. ASYNC: job_wait.",
4038
4388
  inputSchema: {
4039
4389
  type: "object",
4040
4390
  properties: {
@@ -4050,7 +4400,7 @@ const TOOLS = [
4050
4400
  {
4051
4401
  name: "audio_reset_clean",
4052
4402
  description:
4053
- "Restore original clip audio by clearing cleaned/normalized audio and echo metadata. Optional clipId; otherwise all clips. Keeps generated files. Synchronous.",
4403
+ "Alias of audio_enhance off: restore the original audio on one clip (clipId) or all clips, every podcast speaker included. Keeps generated files. Synchronous.",
4054
4404
  inputSchema: {
4055
4405
  type: "object",
4056
4406
  properties: { id: { type: "string" }, path: { type: "string" }, clipId: { type: "string" } },
@@ -4312,10 +4662,32 @@ const TOOLS = [
4312
4662
  fps: { type: "number", description: "1-60, default 30." },
4313
4663
  systemAudio: { type: "boolean", description: "Mic is never captured." },
4314
4664
  countdownSeconds: { type: "number", description: "0-10, default 0." },
4665
+ cleanDesktop: {
4666
+ type: "boolean",
4667
+ description:
4668
+ "Hide desktop icons for a whole-display recording (restored on stop/crash/quit). Default: the user's setting. Result tip = ask the user to turn on Do Not Disturb.",
4669
+ },
4315
4670
  },
4316
4671
  },
4317
4672
  command: "recording.start",
4318
4673
  },
4674
+ {
4675
+ name: "recording_get_clean_desktop",
4676
+ description: "Clean-desktop recording setting. Returns { cleanDesktop, supported }.",
4677
+ inputSchema: { type: "object", properties: {} },
4678
+ command: "recording.get-clean-desktop",
4679
+ },
4680
+ {
4681
+ name: "recording_set_clean_desktop",
4682
+ description:
4683
+ "Turn clean desktop on/off: hide desktop icons while a whole display is recorded (macOS Finder desktop, Windows shell toggle); always restored. On by default. Returns { cleanDesktop }.",
4684
+ inputSchema: {
4685
+ type: "object",
4686
+ properties: { enabled: { type: "boolean" } },
4687
+ required: ["enabled"],
4688
+ },
4689
+ command: "recording.set-clean-desktop",
4690
+ },
4319
4691
  {
4320
4692
  name: "recording_get_countdown",
4321
4693
  description: "Recorder HUD countdown preference (not recording_start's).",
@@ -4337,7 +4709,7 @@ const TOOLS = [
4337
4709
  {
4338
4710
  name: "recording_stop",
4339
4711
  description:
4340
- "Stop the recording and (by default) create a project from it. Returns { screenPath, projectId }; tell the user any warnings.",
4712
+ "Stop the recording and (by default) create a project from it. Returns { screenPath, projectId }; tell the user any warnings and health events (e.g. it stopped early on a nearly full disk).",
4341
4713
  inputSchema: {
4342
4714
  type: "object",
4343
4715
  properties: {
@@ -4347,6 +4719,28 @@ const TOOLS = [
4347
4719
  },
4348
4720
  command: "recording.stop",
4349
4721
  },
4722
+ {
4723
+ name: "recording_unfinished",
4724
+ description:
4725
+ "Recordings that started but never finished (crash, force-quit, power loss). Recover with recording_recover.",
4726
+ inputSchema: { type: "object", properties: {} },
4727
+ command: "recording.unfinished",
4728
+ },
4729
+ {
4730
+ name: "recording_recover",
4731
+ description:
4732
+ "Rebuild an unfinished recording (screen, mic, camera, cursor, aligned) and by default create a project. Tell the user any warnings.",
4733
+ inputSchema: {
4734
+ type: "object",
4735
+ properties: {
4736
+ recordingId: { type: "number", description: "From recording_unfinished." },
4737
+ name: { type: "string" },
4738
+ createProject: { type: "boolean", description: "Default true." },
4739
+ },
4740
+ required: ["recordingId"],
4741
+ },
4742
+ command: "recording.recover",
4743
+ },
4350
4744
  {
4351
4745
  name: "media_generate_presenter",
4352
4746
  description:
@@ -4750,7 +5144,7 @@ const TOOLS = [
4750
5144
  {
4751
5145
  name: "media_import",
4752
5146
  description:
4753
- "Download an https video, image or audio file (e.g. expiring generated-media URLs); returns { path, kind }. 2 GB max.",
5147
+ "Download an https video, image or audio file (e.g. expiring generated-media URLs); returns { path, kind }. 2 GB max. An audio file (kind audio) becomes an audio-only podcast project with project_new withMedia.",
4754
5148
  inputSchema: {
4755
5149
  type: "object",
4756
5150
  properties: {
@@ -5332,7 +5726,8 @@ const TOOLS = [
5332
5726
  },
5333
5727
  {
5334
5728
  name: "llm_generate_timestamps",
5335
- description: "Generate YouTube chapter timestamps from the transcript.",
5729
+ description:
5730
+ "Generate YouTube chapter timestamps for the edited video: edited-timeline times from the words that survive the cuts (first 0:00, each >= 10 s, all before the end); the local LLM only writes labels.",
5336
5731
  inputSchema: {
5337
5732
  type: "object",
5338
5733
  properties: {
@@ -5348,7 +5743,7 @@ const TOOLS = [
5348
5743
  {
5349
5744
  name: "export_start",
5350
5745
  description:
5351
- "Render the project to MP4. ASYNC: { jobId, outputPath, quality }; job_wait. Tell the user any warning and the result's loudness before / after. Free trial: draft (720p) only (system_status license.exportQuality); standard/high/ultra fail with quality_requires_license.",
5746
+ "Render the project to MP4, or audio only (format mp3 / wav / m4a: same mix, loudness-normalised, tagged, cover art embedded). Audio-only projects as video: audiogram=true (waveform + captions) or cover, aspectRatio, rangeStartMs/rangeEndMs for a clip. ASYNC: { jobId, outputPath }; job_wait. Tell the user any warning and the result's loudness before / after. Free trial: draft (720p) only (system_status license.exportQuality); standard/high/ultra fail with quality_requires_license.",
5352
5747
  inputSchema: {
5353
5748
  type: "object",
5354
5749
  properties: {
@@ -5369,6 +5764,27 @@ const TOOLS = [
5369
5764
  type: ["number", "string"],
5370
5765
  description: "30 | 60 | source | auto; omit = project setting.",
5371
5766
  },
5767
+ format: {
5768
+ type: "string",
5769
+ enum: ["mp4", "mp3", "wav", "m4a"],
5770
+ description: "mp4 video (default) or audio only.",
5771
+ },
5772
+ bitrate: { type: "number", description: "MP3 128 | 192 (default) | 320 kbps; M4A 96-320." },
5773
+ channels: { type: "string", enum: ["stereo", "mono"] },
5774
+ audiogram: {
5775
+ type: ["boolean", "string"],
5776
+ description: "Audio-only project as video: true / waveform (audiogram) | cover.",
5777
+ },
5778
+ aspectRatio: { type: "string", description: "This export only: 1:1 | 9:16 | 16:9." },
5779
+ rangeStartMs: { type: "number", description: "Clip start, edited ms." },
5780
+ rangeEndMs: { type: "number", description: "Clip end, edited ms." },
5781
+ captions: {
5782
+ type: "boolean",
5783
+ description: "Force captions on (default true for audiogram).",
5784
+ },
5785
+ title: { type: "string", description: "Audio tag: episode title." },
5786
+ show: { type: "string", description: "Audio tag: show name." },
5787
+ episode: { type: "string", description: "Audio tag: episode number." },
5372
5788
  },
5373
5789
  },
5374
5790
  command: "export.start",
@@ -5524,13 +5940,22 @@ const TOOLS = [
5524
5940
  {
5525
5941
  name: "export_generate_shots",
5526
5942
  description:
5527
- "Suggest 3-5 short clips from an export's transcript (local AI, 1-2 min); shot ids feed project_fork_from_shot. On model_missing ask the user to download it (Settings, AI Model).",
5943
+ "Suggest 3-5 short clips (local AI, 1-2 min) from an export's transcript (id) or straight from a transcribed project that was never exported (projectId); shot ids feed project_fork_from_shot with the same exportId or projectId. Project shots also carry sourceStartMs/sourceEndMs. On model_missing ask the user to download it (Settings, AI Model).",
5528
5944
  inputSchema: {
5529
5945
  type: "object",
5530
5946
  properties: {
5531
- id: { type: "string", description: "Export entry id." },
5947
+ id: { type: "string", description: "Export entry id (or use projectId)." },
5948
+ projectId: {
5949
+ type: "string",
5950
+ description: "Project id or path: find shots in its transcript.",
5951
+ },
5952
+ render: {
5953
+ type: "boolean",
5954
+ description:
5955
+ "Audio-only project: also export each shot as a 9:16 audiogram Short (ASYNC jobId).",
5956
+ },
5957
+ aspectRatio: { type: "string", description: "With render: 9:16 (default) | 1:1 | 16:9." },
5532
5958
  },
5533
- required: ["id"],
5534
5959
  },
5535
5960
  command: "export.generate-shots",
5536
5961
  },
@@ -5750,6 +6175,20 @@ const TOOLS = [
5750
6175
  command: "timeline.edited-to-source",
5751
6176
  },
5752
6177
 
6178
+ ...["read", "search"].map((action) => ({
6179
+ name: `agent_session_${action}`,
6180
+ description:
6181
+ action === "read"
6182
+ ? "Read saved agent conversation with tool-call status."
6183
+ : "Search saved Home and project conversations, newest first.",
6184
+ inputSchema: {
6185
+ type: "object",
6186
+ properties:
6187
+ action === "read" ? { sessionId: { type: "string" } } : { query: { type: "string" } },
6188
+ ...(action === "read" ? { required: ["sessionId"] } : {}),
6189
+ },
6190
+ command: `agent.session-${action}`,
6191
+ })),
5753
6192
  // ── escape hatch ────────────────────────────────────────────────
5754
6193
  // Non-UI admin / diagnostic verbs have no static schema, to keep the
5755
6194
  // per-turn tool list small; agents find them with system_list_commands and
@@ -5804,7 +6243,7 @@ Before an editing pass, read the skill's "Which tool for which moment" section (
5804
6243
  3. Fix misheard names / brands with transcript_find_replace (transcript_insert_words for dropped words).
5805
6244
  4. transcript_find_issues, then transcript_delete_words on each duplicate take / false start (keep the LAST take; keep severity "low" ones).
5806
6245
  5. transcript_remove_silences.
5807
- 6. audio_clean clips not yet cleaned.
6246
+ 6. audio_enhance (Enhance voice) on clips not yet enhanced: strength natural; keepBackground true for vlogs / ambience; no echo (Enhance voice already removes room echo with the Studio AI model; only use echo when the result reports the model did not run (fallback)).
5808
6247
  7. The style, after steps 2-5 (they shift the timeline). The user's named style, recipe, creator or a saved memory wins. Otherwise call recipe_pick: it returns the recipes that fit this project's format and footage, each with pickWhen, plus defaultId. Read the transcript, choose the ONE candidate whose pickWhen matches what is said (defaultId when none clearly fits), do its "before" step if any, then recipe_apply_style + recipe_render and follow the prompt: the recipe decides the style (captions, pacing, kinds of moves, music, transitions). Graphics inside it are designed by you: a named template is the reference look; author your own (motion_render_html, same palette and type) when a beat needs something the template can't show. (An app without recipe_pick, 2.0.5 and older: recipe_list with format short or long, and choose by description and footage.) Tell the user which recipe and why in one line and name one or two alternatives. Only when no candidate exists or the user asked for a plain clean-up: captions (caption_toggle, template glowStack), a few motion graphics, emphasis zooms.
5809
6248
  8. B-roll, last: pick the moments yourself from what's said (any language), check them with project_check_broll, show the user your moments and ASK: their own images, generated on their image connector (only if one is connected; it uses their credits), or no B-roll. Never generate or invent images without their answer. Place what they give with project_add_broll. Nobody to ask: skip it and say so.
5810
6249
  9. llm_generate_title / description / timestamps, then preview_show and ask before export_start. Never export before the user has seen the preview.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@writepanda/mcp",
3
- "version": "1.216.0",
3
+ "version": "1.218.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",