@writepanda/mcp 1.205.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 +203 -24
  2. package/package.json +1 -1
package/bin/server.mjs CHANGED
@@ -577,7 +577,7 @@ const MOTION_ELEMENT_PROPS = {
577
577
  content: {
578
578
  type: ["object", "string", "array"],
579
579
  description:
580
- 'keyword/chip/stamp/slam/behind/highlight { text } (or a bare string); count { value, to?, prefix?, suffix?, label?, decimals? } (value "20-30" = range); steps { items[], cues? (ms offsets from start) }; lowerThird { title, subtitle? (<=320 chars, wraps), look?: plate|plain|typewriter }; endCard { lines: [a, b] }; progress { label?, fromMs?, toMs? }; iconPop { emoji } | { icon }; frame { look: viewfinder | selection | corners, label? }; sticker { image: transparent PNG path, label? }; ticker { text (one story per line, <=2000 chars), label?, speed? (40-600 px/s) }; credits { title?, entries: [{ role?, name }] | "Role: Name" strings (<=200) }; crawl { title?, text (<=6000 chars), tilt? (0-80), turn? (-60..60) }. Any script, symbols and emoji draw.',
580
+ 'keyword/chip/stamp/slam/behind/highlight { text } (or a bare string); count { value, to?, prefix?, suffix?, label?, decimals? } (value "20-30" = range); steps { items[], cues? (ms offsets from start) }; lowerThird { title, subtitle? (<=320 chars, wraps), look?: plate|plain|typewriter, typeSpeed? (typewriter pace, 5-80 chars/s, default ~22) }; endCard { lines: [a, b] }; progress { label?, fromMs?, toMs? }; iconPop { emoji } | { icon }; frame { look: viewfinder | selection | corners, label? }; sticker { image: transparent PNG path, label? }; ticker { text (one story per line, <=2000 chars), label?, speed? (40-600 px/s) }; credits { title?, entries: [{ role?, name }] | "Role: Name" strings (<=200) }; crawl { title?, text (<=6000 chars), tilt? (0-80), turn? (-60..60) }. Any script, symbols and emoji draw.',
581
581
  },
582
582
  atMs: { type: "number", description: "Start, edited ms. Or wordId." },
583
583
  wordId: {
@@ -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, 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
  },
@@ -1300,7 +1336,8 @@ const TOOLS = [
1300
1336
  },
1301
1337
  {
1302
1338
  name: "project_current",
1303
- description: "The project open in the editor, or null.",
1339
+ description:
1340
+ "The project open in the editor, or null, plus the editor's LIVE playhead read at call time: playhead { ms, timeBase: 'edited', durationMs, inCut, playing } (null + playheadNote when unavailable). playhead.ms is EDITED (output) ms, what add-* atMs takes. Call it when the user says 'here' / 'at the playhead' / 'now': the chat context's playhead is from when the message was sent.",
1304
1341
  inputSchema: { type: "object", properties: {} },
1305
1342
  command: "project.current",
1306
1343
  },
@@ -1379,7 +1416,7 @@ const TOOLS = [
1379
1416
  {
1380
1417
  name: "project_add_clip",
1381
1418
  description:
1382
- "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).",
1383
1420
  inputSchema: {
1384
1421
  type: "object",
1385
1422
  properties: {
@@ -1390,6 +1427,14 @@ const TOOLS = [
1390
1427
  fill: { type: "boolean", description: "Images: cover the frame (default true)." },
1391
1428
  webcam: { type: "string", description: "Guest source: a two-speaker podcast clip." },
1392
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
+ },
1393
1438
  expectedRevision: { type: "number" },
1394
1439
  },
1395
1440
  required: ["media"],
@@ -1432,6 +1477,66 @@ const TOOLS = [
1432
1477
  },
1433
1478
  command: "project.add-podcast-clip",
1434
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
+ },
1435
1540
  {
1436
1541
  name: "project_set_participant_offset",
1437
1542
  description: "Nudge one guest's A/V sync on a podcast composite (host is the reference).",
@@ -1560,7 +1665,7 @@ const TOOLS = [
1560
1665
  {
1561
1666
  name: "project_add_designed_segment",
1562
1667
  description:
1563
- "Designed segment: host live on one half, a graphic panel on the other, in one call. The panel is a full-frame transparent render with the camera's half left empty (skill 'Designed segments').",
1668
+ "Designed segment: host live on one half, a graphic panel on the other, in one call. The panel is a full-frame transparent render with the camera's half left empty (skill_read file='motion-templates' section='designed segments').",
1564
1669
  inputSchema: {
1565
1670
  type: "object",
1566
1671
  properties: {
@@ -2922,7 +3027,7 @@ const TOOLS = [
2922
3027
  {
2923
3028
  name: "project_update_region",
2924
3029
  description:
2925
- "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).",
2926
3031
  inputSchema: {
2927
3032
  type: "object",
2928
3033
  properties: {
@@ -2931,7 +3036,11 @@ const TOOLS = [
2931
3036
  path: { type: "string" },
2932
3037
  regionType: { type: "string", enum: REGION_TYPES },
2933
3038
  regionId: { type: "string" },
2934
- 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
+ },
2935
3044
  expectedRevision: { type: "number" },
2936
3045
  },
2937
3046
  required: ["regionType", "regionId", "patch"],
@@ -3140,13 +3249,19 @@ const TOOLS = [
3140
3249
  },
3141
3250
  {
3142
3251
  name: "project_set_export_settings",
3143
- 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.",
3144
3254
  inputSchema: {
3145
3255
  type: "object",
3146
3256
  properties: {
3147
3257
  id: { type: "string" },
3148
3258
  path: { type: "string" },
3149
- 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
+ },
3150
3265
  format: { type: "string", enum: ["mp4"] },
3151
3266
  normalizeLoudness: {
3152
3267
  type: ["boolean", "string"],
@@ -3574,7 +3689,7 @@ const TOOLS = [
3574
3689
  {
3575
3690
  name: "transcript_transcribe",
3576
3691
  description:
3577
- "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.",
3578
3693
  inputSchema: {
3579
3694
  type: "object",
3580
3695
  properties: {
@@ -4520,6 +4635,24 @@ const TOOLS = [
4520
4635
  },
4521
4636
  command: "recipe.list",
4522
4637
  },
4638
+ {
4639
+ name: "recipe_prepare_reference",
4640
+ description:
4641
+ "Prepare evidence for learning a reference video's editing style, without editing any project. Async job: whole-video contact sheets, dense motion samples, transition candidates and mixed-audio excerpts. Inspect the evidence and save a portable reference-specific recipe; do not assume generic effects or exact source sound files.",
4642
+ inputSchema: {
4643
+ type: "object",
4644
+ properties: {
4645
+ file: {
4646
+ type: "string",
4647
+ description: "Absolute local video path (download links with media_download_url first).",
4648
+ },
4649
+ title: { type: "string" },
4650
+ sourceUrl: { type: "string" },
4651
+ },
4652
+ required: ["file"],
4653
+ },
4654
+ command: "recipe.prepare-reference",
4655
+ },
4523
4656
  {
4524
4657
  name: "recipe_pick",
4525
4658
  description:
@@ -4732,7 +4865,8 @@ const TOOLS = [
4732
4865
  },
4733
4866
  {
4734
4867
  name: "motion_craft",
4735
- description: "Launch / promo motion craft library: no id lists topics; id returns the doc.",
4868
+ description:
4869
+ "Launch / promo film craft library (story arcs, blueprints, GSAP rules, design presets): no id lists topics; id returns the doc. An unknown id fails with the closest ids and every valid id. Not the custom motion-graphic HTML contract: that is skill_read section='custom motion graphics'.",
4736
4870
  inputSchema: {
4737
4871
  type: "object",
4738
4872
  properties: {
@@ -4782,7 +4916,7 @@ const TOOLS = [
4782
4916
  {
4783
4917
  name: "motion_render_html",
4784
4918
  description:
4785
- "Render custom HTML to video when no template fits. Contract (skill 'Custom motion graphics'): a paused GSAP timeline at window.__timelines[<data-composition-id>] and a root with data-composition-id / width / height / duration. Lint and STATIC_RENDER failures must be fixed, never placed. ASYNC: { jobId }.",
4919
+ "Render custom HTML to video when no template fits. Contract (skill_read section='custom motion graphics'; render details in skill_read file='custom-html'): a paused GSAP timeline at window.__timelines[<data-composition-id>] and a root with data-composition-id / width / height / duration. Lint and STATIC_RENDER failures must be fixed, never placed. ASYNC: { jobId }.",
4786
4920
  inputSchema: {
4787
4921
  type: "object",
4788
4922
  properties: {
@@ -4862,6 +4996,44 @@ const TOOLS = [
4862
4996
  },
4863
4997
 
4864
4998
  // ── assets ──────────────────────────────────────────────────────
4999
+ {
5000
+ name: "asset_list_sound_patterns",
5001
+ description:
5002
+ "List shared motion sound recipes: counted entrances, scroll ratchets, slides, swipes and presses. Use asset_plan_sound_pattern with actual animation timings.",
5003
+ inputSchema: { type: "object", properties: { query: { type: "string" } } },
5004
+ command: "asset.list-sound-patterns",
5005
+ },
5006
+ {
5007
+ name: "asset_plan_sound_pattern",
5008
+ description:
5009
+ "Plan motion-synced sounds without writing a project. Returns cues for project_add_sound_cues; use a unique group per animation. One cue per actual event, variant rotation, quiet starting gain, tails bounded by settle. Preview below narration.",
5010
+ inputSchema: {
5011
+ type: "object",
5012
+ properties: {
5013
+ pattern: { type: "string", description: "Id from asset_list_sound_patterns." },
5014
+ atMs: { type: "integer", minimum: 0, description: "Edited-timeline motion start." },
5015
+ endMs: { type: "integer", description: "Edited-timeline motion end/settle." },
5016
+ eventOffsetsMs: {
5017
+ type: "array",
5018
+ items: { type: "integer", minimum: 0 },
5019
+ minItems: 1,
5020
+ maxItems: 500,
5021
+ description:
5022
+ "Actual entrance/crossing/gesture offsets relative to atMs; strictly increasing. Follow easing, not an assumed uniform stagger.",
5023
+ },
5024
+ volume: { type: "number", minimum: 0, maximum: 2 },
5025
+ sounds: {
5026
+ type: "array",
5027
+ items: { type: "string" },
5028
+ minItems: 1,
5029
+ maxItems: 500,
5030
+ description: "Optional alternate ids/paths, rotated.",
5031
+ },
5032
+ },
5033
+ required: ["pattern", "atMs", "endMs", "eventOffsetsMs"],
5034
+ },
5035
+ command: "asset.plan-sound-pattern",
5036
+ },
4865
5037
  {
4866
5038
  name: "asset_list_emoji",
4867
5039
  description: "List animated emoji for project_add_emoji.",
@@ -4985,14 +5157,19 @@ const TOOLS = [
4985
5157
  {
4986
5158
  name: "export_start",
4987
5159
  description:
4988
- "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.",
4989
5161
  inputSchema: {
4990
5162
  type: "object",
4991
5163
  properties: {
4992
5164
  id: { type: "string" },
4993
5165
  path: { type: "string" },
4994
5166
  outputPath: { type: "string" },
4995
- 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
+ },
4996
5173
  normalizeLoudness: {
4997
5174
  type: ["boolean", "string"],
4998
5175
  description: "streaming / podcast / off; omit = project setting.",
@@ -5107,7 +5284,7 @@ const TOOLS = [
5107
5284
  {
5108
5285
  name: "clips_export",
5109
5286
  description:
5110
- "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[] }.",
5111
5288
  inputSchema: {
5112
5289
  type: "object",
5113
5290
  properties: {
@@ -5425,6 +5602,8 @@ const INSTRUCTIONS = `PandaStudio is a desktop video editor; these tools drive t
5425
5602
 
5426
5603
  Start with system_status. Unknown arguments are rejected. Verbs without a tool of their own (system_list_commands) go through pandastudio_call.
5427
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
+
5428
5607
  Before an editing pass, read the skill's "Which tool for which moment" section (skill_read section="Which tool for which moment").
5429
5608
 
5430
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.205.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",