@writepanda/mcp 1.207.0 → 1.210.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 +140 -19
  2. package/package.json +1 -1
package/bin/server.mjs CHANGED
@@ -627,7 +627,11 @@ const MOTION_ELEMENT_PROPS = {
627
627
  enum: ["auto", "none", "pop", "whoosh", "hit", "tick", "bell"],
628
628
  description: "Role for project_compose_soundtrack.",
629
629
  },
630
- zIndex: { type: "number" },
630
+ zIndex: {
631
+ type: "number",
632
+ description:
633
+ "Stack order, higher on top (default 0). Front elements share one stack with media overlays and annotations at group base + zIndex (B-roll 0, overlays 1000, annotations 2000, front elements 3000), so by default they draw over both; -2500 drops one under the default overlays.",
634
+ },
631
635
  anchor: { type: "string", enum: ["auto", "free"] },
632
636
  };
633
637
  const TOOLS = [
@@ -635,7 +639,7 @@ const TOOLS = [
635
639
  {
636
640
  name: "system_status",
637
641
  description:
638
- "App version and license; automationGated true = trial expired, or the Solo plan (license.agentEditing.allowed false: no agent editing, relay license.agentEditing.message to the user), and every other tool fails; license.socialPublishing.allowed false = the plan has no Instagram/TikTok/social publishing (Creator, Team, Pro only). Call first.",
642
+ "App version and license; automationGated true = trial expired, or the Solo plan (license.agentEditing.allowed false: no agent editing, relay license.agentEditing.message to the user), and every other tool fails; license.socialPublishing.allowed false = the plan has no Instagram/TikTok/social publishing (Creator, Team, Pro only); license.exportQuality.allowed = the export_start qualities that work (free trial: draft 720p only; standard/high/ultra need a license). Call first.",
639
643
  inputSchema: { type: "object", properties: {} },
640
644
  command: "system.status",
641
645
  },
@@ -725,7 +729,7 @@ const TOOLS = [
725
729
  {
726
730
  name: "system_get_transcription_language",
727
731
  description:
728
- "Workspace transcription language (auto = Parakeet, English + 25 European languages).",
732
+ "Workspace transcription language. auto = Parakeet TDT v3, which detects the language itself across 25 languages, English included: Bulgarian, Croatian, Czech, Danish, Dutch, English, Estonian, Finnish, French, German, Greek, Hungarian, Italian, Latvian, Lithuanian, Maltese, Polish, Portuguese, Romanian, Russian, Slovak, Slovenian, Spanish, Swedish, Ukrainian. Anything else names a Whisper language.",
729
733
  inputSchema: { type: "object", properties: {} },
730
734
  command: "system.get-transcription-language",
731
735
  },
@@ -752,7 +756,7 @@ const TOOLS = [
752
756
  {
753
757
  name: "system_set_transcription_language",
754
758
  description:
755
- "Set the workspace transcription language. English = auto (english/en are saved as auto). Non-auto uses Whisper, a ~1.1 GB download: tell the user first (system.is-whisper-model-downloaded via pandastudio_call).",
759
+ "Set the workspace transcription language. auto = Parakeet TDT v3, which detects the language itself across 25 languages, English included: Bulgarian, Croatian, Czech, Danish, Dutch, English, Estonian, Finnish, French, German, Greek, Hungarian, Italian, Latvian, Lithuanian, Maltese, Polish, Portuguese, Romanian, Russian, Slovak, Slovenian, Spanish, Swedish, Ukrainian. For any of those (e.g. Romanian) use auto; their names are accepted and saved as auto. The other values use Whisper, a ~1.1 GB download: tell the user first (system.is-whisper-model-downloaded via pandastudio_call).",
756
760
  inputSchema: {
757
761
  type: "object",
758
762
  properties: {
@@ -762,6 +766,30 @@ const TOOLS = [
762
766
  "auto",
763
767
  "english",
764
768
  "en",
769
+ "bulgarian",
770
+ "croatian",
771
+ "czech",
772
+ "danish",
773
+ "dutch",
774
+ "estonian",
775
+ "finnish",
776
+ "french",
777
+ "german",
778
+ "greek",
779
+ "hungarian",
780
+ "italian",
781
+ "latvian",
782
+ "lithuanian",
783
+ "maltese",
784
+ "polish",
785
+ "portuguese",
786
+ "romanian",
787
+ "russian",
788
+ "slovak",
789
+ "slovenian",
790
+ "spanish",
791
+ "swedish",
792
+ "ukrainian",
765
793
  "chinese",
766
794
  "japanese",
767
795
  "korean",
@@ -1048,8 +1076,14 @@ const TOOLS = [
1048
1076
  },
1049
1077
  {
1050
1078
  name: "youtube_connect",
1051
- description: "Connect a YouTube account via browser sign-in (only when the user asks).",
1052
- inputSchema: { type: "object", properties: {} },
1079
+ description:
1080
+ "Connect a YouTube account via browser sign-in (only when the user asks). Pass email to reconnect an existing account. permissionIssue set = the user unticked a Google permission: relay its message.",
1081
+ inputSchema: {
1082
+ type: "object",
1083
+ properties: {
1084
+ email: { type: "string", description: "Reconnect: that account's email." },
1085
+ },
1086
+ },
1053
1087
  command: "youtube.connect",
1054
1088
  },
1055
1089
  {
@@ -1065,13 +1099,15 @@ const TOOLS = [
1065
1099
  },
1066
1100
  {
1067
1101
  name: "youtube_list_accounts",
1068
- description: "Connected YouTube accounts and channels (pick before export_publish_youtube).",
1102
+ description:
1103
+ "Connected YouTube accounts and channels (pick before export_publish_youtube). issues[] = accounts missing a Google permission (code youtube_permission_missing): relay message, follow howToFix (the user reconnects in Settings > YouTube, leaving every box ticked; you can't do the sign-in).",
1069
1104
  inputSchema: { type: "object", properties: {} },
1070
1105
  command: "youtube.list-accounts",
1071
1106
  },
1072
1107
  {
1073
1108
  name: "youtube_list_channels",
1074
- description: "Refresh an account's channel list from YouTube (bypasses cache).",
1109
+ description:
1110
+ "Refresh an account's channel list from YouTube (bypasses cache). Error code youtube_permission_missing = channel permission unticked on Google's screen: relay message, follow howToFix; canStillPublish true = publish without channelId.",
1075
1111
  inputSchema: {
1076
1112
  type: "object",
1077
1113
  properties: { accountId: { type: "string" } },
@@ -1082,7 +1118,7 @@ const TOOLS = [
1082
1118
  {
1083
1119
  name: "export_publish_youtube",
1084
1120
  description:
1085
- "Publish an export entry to YouTube. Slow. Check the entry's youtubeVideoId before republishing. Returns { videoUrl }.",
1121
+ "Publish an export entry to YouTube. Slow. Check the entry's youtubeVideoId before republishing. channelId optional (uploads go to the account's own channel; omit when channels can't be listed). Error code youtube_permission_missing: relay message, follow howToFix. Returns { videoUrl, channelId }.",
1086
1122
  inputSchema: {
1087
1123
  type: "object",
1088
1124
  properties: {
@@ -1096,7 +1132,7 @@ const TOOLS = [
1096
1132
  privacyStatus: { type: "string", enum: ["public", "unlisted", "private"] },
1097
1133
  setThumbnail: { type: "boolean" },
1098
1134
  },
1099
- required: ["id", "accountId", "channelId", "title"],
1135
+ required: ["id", "accountId", "title"],
1100
1136
  },
1101
1137
  command: "export.publish-youtube",
1102
1138
  },
@@ -1380,7 +1416,7 @@ const TOOLS = [
1380
1416
  {
1381
1417
  name: "project_add_clip",
1382
1418
  description:
1383
- "Append or insert a video clip, or a still image shown for durationMs, on the main track (later content moves with it). Mid-recording: project_split_clip, then atIndex = rightClipIndex.",
1419
+ "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).",
1384
1420
  inputSchema: {
1385
1421
  type: "object",
1386
1422
  properties: {
@@ -1391,6 +1427,14 @@ const TOOLS = [
1391
1427
  fill: { type: "boolean", description: "Images: cover the frame (default true)." },
1392
1428
  webcam: { type: "string", description: "Guest source: a two-speaker podcast clip." },
1393
1429
  atIndex: { type: "number", description: "0-based; omit = append." },
1430
+ atMs: {
1431
+ type: "number",
1432
+ description: "Insert at this edited ms (splits the clip there; not with atIndex).",
1433
+ },
1434
+ replaceToMs: {
1435
+ type: "number",
1436
+ description: "With atMs: cut [atMs, replaceToMs) (a trim) and put the clip there.",
1437
+ },
1394
1438
  expectedRevision: { type: "number" },
1395
1439
  },
1396
1440
  required: ["media"],
@@ -1433,6 +1477,66 @@ const TOOLS = [
1433
1477
  },
1434
1478
  command: "project.add-podcast-clip",
1435
1479
  },
1480
+ {
1481
+ name: "podcast_open_host",
1482
+ description: "Open or focus the live podcast host window. Call podcast_status for the session code and guest link once it loads.",
1483
+ inputSchema: {
1484
+ type: "object",
1485
+ properties: {
1486
+ sessionCode: { type: "string", description: "Resume an existing session." },
1487
+ projectPath: { type: "string", description: "Matching podcast project when resuming." },
1488
+ },
1489
+ },
1490
+ command: "podcast.open-host",
1491
+ },
1492
+ ...[
1493
+ ["status", "Read the live podcast session code, guest link, participants, recording, and sharing state."],
1494
+ ["start-recording", "Start recording the host and signal connected guests to record. Returns once recording has really started; fails with the reason otherwise."],
1495
+ ["stop-recording", "Stop recording and finalize podcast media. Check podcast_status for finalization and guest uploads."],
1496
+ ["start-screen-share", "Open the desktop picker to share and record the host screen during a podcast. Waits for the host to choose; fails if the picker is cancelled or capture fails."],
1497
+ ["stop-screen-share", "Stop and finalize the host's podcast screen share."],
1498
+ ["open-in-editor", "Open the finished podcast in the editor when guest media has arrived."],
1499
+ ].map(([noun, description]) => ({
1500
+ name: `podcast_${noun.replaceAll("-", "_")}`,
1501
+ description,
1502
+ inputSchema: { type: "object", properties: {} },
1503
+ command: `podcast.${noun}`,
1504
+ })),
1505
+ {
1506
+ name: "project_set_podcast_source_crop",
1507
+ description:
1508
+ "Frame one podcast camera or screen-share source independently. Use source=host|guest|guest-2|guest-3|share:0; normalized x,y,width,height. 0,0,1,1 resets the crop. Preview and export use the same framing.",
1509
+ inputSchema: {
1510
+ type: "object",
1511
+ properties: {
1512
+ id: { type: "string" },
1513
+ path: { type: "string" },
1514
+ clipId: { type: "string" },
1515
+ source: { type: "string", description: "host, guest, guest-2, guest-3, or share:<index>." },
1516
+ x: { type: "number", description: "Left edge, 0-1." },
1517
+ y: { type: "number", description: "Top edge, 0-1." },
1518
+ width: { type: "number", description: "Crop width, >0 to 1." },
1519
+ height: { type: "number", description: "Crop height, >0 to 1." },
1520
+ expectedRevision: { type: "number" },
1521
+ },
1522
+ required: ["clipId", "source", "x", "y", "width", "height"],
1523
+ },
1524
+ command: "project.set-podcast-source-crop",
1525
+ },
1526
+ {
1527
+ name: "project_list_podcast_screen_shares",
1528
+ description:
1529
+ "List the screen-share intervals shown on the podcast editor timeline. Returns speaker, clip-local and merged-source timing, edited timing after trims/speed, and whether each interval is visible. Read-only.",
1530
+ inputSchema: {
1531
+ type: "object",
1532
+ properties: {
1533
+ id: { type: "string" },
1534
+ path: { type: "string" },
1535
+ clipId: { type: "string", description: "Optional podcast clip filter." },
1536
+ },
1537
+ },
1538
+ command: "project.list-podcast-screen-shares",
1539
+ },
1436
1540
  {
1437
1541
  name: "project_set_participant_offset",
1438
1542
  description: "Nudge one guest's A/V sync on a podcast composite (host is the reference).",
@@ -2923,7 +3027,7 @@ const TOOLS = [
2923
3027
  {
2924
3028
  name: "project_update_region",
2925
3029
  description:
2926
- "Patch a region; unspecified fields are kept. Patchable: zoom (depth, focusX, focusY, focusMode static|auto), trim / speed (startMs, endMs, speed, rampIn, rampOut, preservePitch, freezeMs, reverse, reverseAudio), fx (atMs, durationMs, opacity, blendMode, speed), annotation, overlay (x, y, width, height in canvas %, layer, opacity, blendMode, muted, volume, loop, motionBlur), adjustment (startMs, endMs, blendMode), motion (motionBlur), clip-transform (startMs, endMs, preset, transitionMs, easing, bezier), background-effect (as project_add_background_effect), audio-overlay (startMs, endMs, sourceStartMs, volume 0-2, fadeIn, fadeOut).",
3030
+ "Patch a region; unspecified fields are kept. Patchable: zoom (depth, focusX, focusY, focusMode static|auto), trim / speed (startMs, endMs, speed, rampIn, rampOut, preservePitch, freezeMs, reverse, reverseAudio), fx (atMs, durationMs, opacity, blendMode, speed, zIndex), annotation (zIndex), overlay (x, y, width, height in canvas %, layer, opacity, blendMode, muted, volume, loop, motionBlur, zIndex), adjustment (startMs, endMs, blendMode), motion (motionBlur), clip-transform (startMs, endMs, preset, transitionMs, easing, bezier), background-effect (as project_add_background_effect), audio-overlay (startMs, endMs, sourceStartMs, volume 0-2, fadeIn, fadeOut).",
2927
3031
  inputSchema: {
2928
3032
  type: "object",
2929
3033
  properties: {
@@ -2932,7 +3036,11 @@ const TOOLS = [
2932
3036
  path: { type: "string" },
2933
3037
  regionType: { type: "string", enum: REGION_TYPES },
2934
3038
  regionId: { type: "string" },
2935
- patch: { type: "object", description: "e.g. { depth: 4 }; null clears a value." },
3039
+ patch: {
3040
+ type: "object",
3041
+ description:
3042
+ "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).",
3043
+ },
2936
3044
  expectedRevision: { type: "number" },
2937
3045
  },
2938
3046
  required: ["regionType", "regionId", "patch"],
@@ -3141,13 +3249,19 @@ const TOOLS = [
3141
3249
  },
3142
3250
  {
3143
3251
  name: "project_set_export_settings",
3144
- description: "Project export defaults (Export dialog and export_start).",
3252
+ description:
3253
+ "Project export defaults (Export dialog and export_start). quality good and source need a license: a free trial gets quality_requires_license, use medium.",
3145
3254
  inputSchema: {
3146
3255
  type: "object",
3147
3256
  properties: {
3148
3257
  id: { type: "string" },
3149
3258
  path: { type: "string" },
3150
- quality: { type: "string", enum: ["medium", "good", "source"] },
3259
+ quality: {
3260
+ type: "string",
3261
+ enum: ["medium", "good", "source"],
3262
+ description:
3263
+ "medium 720p, good 1080p, source = source resolution (good/source license only).",
3264
+ },
3151
3265
  format: { type: "string", enum: ["mp4"] },
3152
3266
  normalizeLoudness: {
3153
3267
  type: ["boolean", "string"],
@@ -3575,7 +3689,7 @@ const TOOLS = [
3575
3689
  {
3576
3690
  name: "transcript_transcribe",
3577
3691
  description:
3578
- "Transcribe clips with word timestamps (default: only clips without words). To REDO a transcript (wrong language, bad run) fix the language first, then force: true (all clips) or clipId. Word fixes are re-applied; tell the user about result droppedWordEdits[] (fixes that may need redoing). ASYNC: { jobId }, job_wait.",
3692
+ "Transcribe clips with word timestamps (default: only clips without words). To REDO a transcript (wrong language, bad run) fix the language first, then force: true (all clips) or clipId. Word fixes are re-applied; tell the user about result droppedWordEdits[] (fixes that may need redoing). Each clip is transcribed only over the part it plays (head trim to out-point, e.g. one half of a split; result transcribedRanges[]). ASYNC: { jobId }, job_wait.",
3579
3693
  inputSchema: {
3580
3694
  type: "object",
3581
3695
  properties: {
@@ -5043,14 +5157,19 @@ const TOOLS = [
5043
5157
  {
5044
5158
  name: "export_start",
5045
5159
  description:
5046
- "Render the project to MP4. ASYNC: { jobId, outputPath }; job_wait. Tell the user any warning and the result's loudness before / after.",
5160
+ "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.",
5047
5161
  inputSchema: {
5048
5162
  type: "object",
5049
5163
  properties: {
5050
5164
  id: { type: "string" },
5051
5165
  path: { type: "string" },
5052
5166
  outputPath: { type: "string" },
5053
- quality: { type: "string", enum: ["draft", "standard", "high", "ultra"] },
5167
+ quality: {
5168
+ type: "string",
5169
+ enum: ["draft", "standard", "high", "ultra"],
5170
+ description:
5171
+ "draft 720p, standard 1080p, high/ultra source resolution (standard and up: license only). Omit = high, or draft on a trial.",
5172
+ },
5054
5173
  normalizeLoudness: {
5055
5174
  type: ["boolean", "string"],
5056
5175
  description: "streaming / podcast / off; omit = project setting.",
@@ -5165,7 +5284,7 @@ const TOOLS = [
5165
5284
  {
5166
5285
  name: "clips_export",
5167
5286
  description:
5168
- "Export a clip set's finished clips to MP4 (all, or clipIds) into Movies/PandaStudio Clips/<video>/. Async: returns { jobId, outputDir }; job_wait for { exported[] }.",
5287
+ "Export a clip set's finished clips to MP4 (all, or clipIds) into Movies/PandaStudio Clips/<video>/, at standard quality (1080p; 720p on a free trial). Async: returns { jobId, outputDir }; job_wait for { exported[] }.",
5169
5288
  inputSchema: {
5170
5289
  type: "object",
5171
5290
  properties: {
@@ -5483,6 +5602,8 @@ const INSTRUCTIONS = `PandaStudio is a desktop video editor; these tools drive t
5483
5602
 
5484
5603
  Start with system_status. Unknown arguments are rejected. Verbs without a tool of their own (system_list_commands) go through pandastudio_call.
5485
5604
 
5605
+ Read-only tools (project_read, transcript_get / _search / _find_issues, audio_probe, project_render_frame / _render_sheet, timeline_*, ...) use the project open in the editor when you pass no id or path; edits always need the id.
5606
+
5486
5607
  Before an editing pass, read the skill's "Which tool for which moment" section (skill_read section="Which tool for which moment").
5487
5608
 
5488
5609
  "Edit my video" / "make this a Short" with no specifics = this pipeline, in order. Confirm scope once ("full polish, or just part of it?"), then run it all:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@writepanda/mcp",
3
- "version": "1.207.0",
3
+ "version": "1.210.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",