@writepanda/mcp 1.183.0 → 1.191.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 +505 -20
  2. package/package.json +1 -1
package/bin/server.mjs CHANGED
@@ -552,6 +552,69 @@ const ANCHOR_END_PROP = { type: "number", description: "Anchor end, SOURCE ms."
552
552
  const KEYFRAME_TARGET_DOC =
553
553
  "main = motion region (its target decides fields); overlay: x y scale rotation opacity backdropBlur keyStrength; annotation: x y scale rotation opacity; adjustment: effects, amount, lookIntensity; focus: x y width height roundness feathering blurAmount pixelSize maskOpacity; background-effect: amount strength; mask (regionId = overlay id): x y width height roundness feather expand.";
554
554
 
555
+ const MOTION_ELEMENT_TYPES = [
556
+ "keyword",
557
+ "chip",
558
+ "stamp",
559
+ "count",
560
+ "steps",
561
+ "slam",
562
+ "behind",
563
+ "lowerThird",
564
+ "highlight",
565
+ "endCard",
566
+ "progress",
567
+ "iconPop",
568
+ "frame",
569
+ "sticker",
570
+ ];
571
+ const MOTION_ELEMENT_PROPS = {
572
+ type: { type: "string", enum: MOTION_ELEMENT_TYPES },
573
+ content: {
574
+ type: ["object", "string", "array"],
575
+ description:
576
+ '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? }; endCard { lines: [a, b] }; progress { label?, fromMs?, toMs? }; iconPop { emoji } | { icon }; frame { look: viewfinder | selection | corners, label? }; sticker { image: transparent PNG path, label? }. Latin script only.',
577
+ },
578
+ atMs: { type: "number", description: "Start, edited ms. Or wordId." },
579
+ wordId: {
580
+ type: "string",
581
+ description: "Start on this transcript word; follows it through cuts.",
582
+ },
583
+ offsetMs: { type: "number", description: "Shift from atMs/wordId (may be negative)." },
584
+ durationMs: { type: "number", description: "Default endWordId, else the type's length." },
585
+ endWordId: { type: "string", description: "Run to the end of this word." },
586
+ cueWordIds: {
587
+ type: "array",
588
+ items: { type: "string" },
589
+ description: "steps: one word id per item.",
590
+ },
591
+ style: {
592
+ type: "object",
593
+ properties: {
594
+ family: { type: "string", enum: ["bold", "editorial", "clean", "playful", "paper"] },
595
+ accent: { type: "string" },
596
+ highlight: { type: "string" },
597
+ text: { type: "string" },
598
+ font: { type: "string" },
599
+ size: { type: "string", enum: ["s", "m", "l", "xl"] },
600
+ outline: { type: "boolean" },
601
+ },
602
+ description: "Default: the brand kit.",
603
+ },
604
+ zone: {
605
+ type: ["string", "object"],
606
+ description:
607
+ "auto (clear of the face) | top | upper | center | lower | bottom | { x, y, w? } (% of frame; x,y = centre).",
608
+ },
609
+ layer: { type: "string", enum: ["front", "behind"] },
610
+ sound: {
611
+ type: "string",
612
+ enum: ["auto", "none", "pop", "whoosh", "hit", "tick", "bell"],
613
+ description: "Role for project_compose_soundtrack.",
614
+ },
615
+ zIndex: { type: "number" },
616
+ anchor: { type: "string", enum: ["auto", "free"] },
617
+ };
555
618
  const TOOLS = [
556
619
  // ── system + discovery ──────────────────────────────────────────
557
620
  {
@@ -641,7 +704,7 @@ const TOOLS = [
641
704
  {
642
705
  name: "system_set_transcription_language",
643
706
  description:
644
- "Set the workspace transcription language. Non-auto uses Whisper, a ~1.1 GB download: tell the user first (system.is-whisper-model-downloaded via pandastudio_call).",
707
+ "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).",
645
708
  inputSchema: {
646
709
  type: "object",
647
710
  properties: {
@@ -649,6 +712,8 @@ const TOOLS = [
649
712
  type: "string",
650
713
  enum: [
651
714
  "auto",
715
+ "english",
716
+ "en",
652
717
  "chinese",
653
718
  "japanese",
654
719
  "korean",
@@ -1512,16 +1577,33 @@ const TOOLS = [
1512
1577
  },
1513
1578
  {
1514
1579
  name: "project_add_transition",
1515
- description: "Place a transition centred on a cut (atMs = a clip boundary).",
1580
+ description:
1581
+ "Place a transition centred on a cut (a clip boundary or a jump cut). kind overlay (fade-black, flash, glitch, torn-paper…) draws a WebM over the cut; kind native (zoom-blur, whip-left, whip-right, whip-up, spin) moves the footage itself (captions/graphics stay put), 350 ms default. Snaps atMs to a cut within snapMs (default 500) and places the transition's default sound.",
1516
1582
  inputSchema: {
1517
1583
  type: "object",
1518
1584
  properties: {
1519
1585
  id: { type: "string" },
1520
1586
  path: { type: "string" },
1521
- transitionId: { type: "string", description: "asset_list_transitions id." },
1522
- file: { type: "string", description: "Instead of transitionId." },
1523
- atMs: { type: "number" },
1524
- durationMs: { type: "number", description: "Default 1000." },
1587
+ transitionId: {
1588
+ type: "string",
1589
+ description: "asset_list_transitions id (overlay or native kind).",
1590
+ },
1591
+ file: { type: "string", description: "Custom overlay WebM, instead of transitionId." },
1592
+ atMs: { type: "number", description: "The cut (edited ms)." },
1593
+ durationMs: {
1594
+ type: "number",
1595
+ description: "Default: the catalog's (1000 overlays, 900 torn-paper, 350 native).",
1596
+ },
1597
+ snapMs: {
1598
+ type: "number",
1599
+ description: "Snap to the nearest cut within this many ms. Default 500; 0 = exact.",
1600
+ },
1601
+ sound: {
1602
+ type: "string",
1603
+ description:
1604
+ "Bundled sound id / path. Default: the transition's defaultSoundId. none = silent.",
1605
+ },
1606
+ soundVolume: { type: "number", description: "0-1." },
1525
1607
  expectedRevision: { type: "number" },
1526
1608
  },
1527
1609
  required: ["atMs"],
@@ -2477,13 +2559,14 @@ const TOOLS = [
2477
2559
  // ── compose: edit existing regions ─────────────────────────────
2478
2560
  {
2479
2561
  name: "project_remove_region",
2480
- description: "Delete a region by id (editor.*Regions or audioOverlays); linked peers go too.",
2562
+ description:
2563
+ "Delete a region by id (editor.*Regions, motion-element = editor.motionElements, or audioOverlays); linked peers go too.",
2481
2564
  inputSchema: {
2482
2565
  type: "object",
2483
2566
  properties: {
2484
2567
  id: { type: "string" },
2485
2568
  path: { type: "string" },
2486
- regionType: { type: "string", enum: REGION_TYPES },
2569
+ regionType: { type: "string", enum: [...REGION_TYPES, "motion-element"] },
2487
2570
  regionId: { type: "string" },
2488
2571
  expectedRevision: { type: "number" },
2489
2572
  },
@@ -2513,6 +2596,7 @@ const TOOLS = [
2513
2596
  "spotlight",
2514
2597
  "motion",
2515
2598
  "adjustment",
2599
+ "motion-element",
2516
2600
  "audio-overlay",
2517
2601
  ],
2518
2602
  },
@@ -2662,12 +2746,14 @@ const TOOLS = [
2662
2746
  },
2663
2747
  {
2664
2748
  name: "project_set_crop",
2665
- description: "Crop the main video's source frame (0-1 fractions). Default 0,0,1,1 (no crop).",
2749
+ description:
2750
+ "Crop the main video's source frame (0-1 fractions), e.g. to cut baked-in black bars. Without clipId it applies to the project and every clip (a clip's own crop, like auto-reframe's, wins otherwise). Default 0,0,1,1 (no crop).",
2666
2751
  inputSchema: {
2667
2752
  type: "object",
2668
2753
  properties: {
2669
2754
  id: { type: "string" },
2670
2755
  path: { type: "string" },
2756
+ clipId: { type: "string", description: "Crop only this clip." },
2671
2757
  x: { type: "number" },
2672
2758
  y: { type: "number" },
2673
2759
  width: { type: "number" },
@@ -3227,13 +3313,14 @@ const TOOLS = [
3227
3313
  {
3228
3314
  name: "transcript_transcribe",
3229
3315
  description:
3230
- "Transcribe clips with word timestamps. ASYNC: { jobId }, job_wait. Tell the user about result droppedWordEdits[] (fixes that may need redoing).",
3316
+ "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.",
3231
3317
  inputSchema: {
3232
3318
  type: "object",
3233
3319
  properties: {
3234
3320
  id: { type: "string" },
3235
3321
  path: { type: "string" },
3236
- clipId: { type: "string" },
3322
+ clipId: { type: "string", description: "Only this clip, even if already transcribed." },
3323
+ force: { type: "boolean", description: "Re-transcribe clips that already have words." },
3237
3324
  },
3238
3325
  },
3239
3326
  command: "transcript.transcribe",
@@ -3422,7 +3509,8 @@ const TOOLS = [
3422
3509
  },
3423
3510
  {
3424
3511
  name: "caption_set_template",
3425
- description: "Pick a caption template (default glowStack).",
3512
+ description:
3513
+ "Pick a caption template (default glowStack); clears style overrides. Emphasis templates (hormoziEmphasis, tiltedBox, serifItalic, condensedCaps, scriptKeyword) style the IMPORTANT word of each phrase, not the spoken one; emphasis is detected on apply when missing (result.emphasis).",
3426
3514
  inputSchema: {
3427
3515
  type: "object",
3428
3516
  properties: {
@@ -3447,6 +3535,11 @@ const TOOLS = [
3447
3535
  "matrixDecode",
3448
3536
  "glitchRgb",
3449
3537
  "blendDifference",
3538
+ "hormoziEmphasis",
3539
+ "tiltedBox",
3540
+ "serifItalic",
3541
+ "condensedCaps",
3542
+ "scriptKeyword",
3450
3543
  ],
3451
3544
  },
3452
3545
  expectedRevision: { type: "number" },
@@ -3458,7 +3551,8 @@ const TOOLS = [
3458
3551
 
3459
3552
  {
3460
3553
  name: "caption_set_style",
3461
- description: "Override caption style fields (template kept); pass only what changes.",
3554
+ description:
3555
+ "Override caption style fields (template kept); pass only what changes. highlightMode emphasis/both detects emphasis words when missing (result.emphasis).",
3462
3556
  inputSchema: {
3463
3557
  type: "object",
3464
3558
  properties: {
@@ -3475,11 +3569,45 @@ const TOOLS = [
3475
3569
  strokeWidth: { type: "number" },
3476
3570
  fontSize: { type: "string", description: "e.g. '2.6rem' (1-5rem)." },
3477
3571
  uppercase: { type: "boolean" },
3572
+ highlightMode: {
3573
+ type: "string",
3574
+ enum: ["spoken", "emphasis", "both"],
3575
+ description: "spoken = word being said; emphasis = key words all group long.",
3576
+ },
3577
+ emphasisColor: { type: "string" },
3578
+ emphasisBackgroundColor: { type: "string", description: "'transparent' = no box." },
3579
+ emphasisScale: { type: "number", description: "0.5-2." },
3580
+ emphasisItalic: { type: "boolean" },
3581
+ emphasisFontFamily: {
3582
+ type: "string",
3583
+ description: "Engine font, e.g. Great Vibes, Playfair Display, Bebas Neue, Anton.",
3584
+ },
3585
+ boxRotation: { type: "number", description: "Box tilt degrees 0-15." },
3478
3586
  expectedRevision: { type: "number" },
3479
3587
  },
3480
3588
  },
3481
3589
  command: "caption.set-style",
3482
3590
  },
3591
+ {
3592
+ name: "caption_mark_emphasis",
3593
+ description:
3594
+ "Set the key words emphasis caption styles mark. No wordIds: detect from the speech map (strong moments + numbers; hand marks kept unless reset). wordIds: mark (emphasis true) or unmark (false). Returns { emphasizedWords, words }.",
3595
+ inputSchema: {
3596
+ type: "object",
3597
+ properties: {
3598
+ id: { type: "string" },
3599
+ path: { type: "string" },
3600
+ wordIds: { type: "array", items: { type: "string" }, description: "From transcript_get." },
3601
+ emphasis: { type: "boolean", description: "With wordIds; default true." },
3602
+ detect: { type: "boolean" },
3603
+ reset: { type: "boolean", description: "Detection replaces hand marks too." },
3604
+ minGapMs: { type: "number", description: "Default 1200." },
3605
+ everyMs: { type: "number", description: "Default 2500; lower = more words." },
3606
+ expectedRevision: { type: "number" },
3607
+ },
3608
+ },
3609
+ command: "caption.mark-emphasis",
3610
+ },
3483
3611
  {
3484
3612
  name: "caption_move",
3485
3613
  description:
@@ -3513,14 +3641,20 @@ const TOOLS = [
3513
3641
  {
3514
3642
  name: "media_generate_image",
3515
3643
  description:
3516
- "Generate one image (Replicate gpt-image-2, needs Replicate connected). Returns { imagePath }. 3:2 for 16:9, 2:3 for 9:16.",
3644
+ "Generate one image on the user's own image connector (auto: Replicate, else Higgsfield; their credits). Returns { imagePath, provider, model, transparent }. transparent=true: trimmed PNG sticker. No connector: code NO_IMAGE_CONNECTOR, so ask the user for their own images. Each call is a paid generation.",
3517
3645
  inputSchema: {
3518
3646
  type: "object",
3519
3647
  properties: {
3520
3648
  prompt: { type: "string", description: "Keep text out of the image." },
3521
- aspectRatio: { type: "string", enum: ["1:1", "3:2", "2:3"] },
3649
+ aspectRatio: {
3650
+ type: "string",
3651
+ enum: ["1:1", "3:2", "2:3", "4:3", "3:4", "16:9", "9:16"],
3652
+ description: "Default 3:2. 16:9 / 9:16 for full-frame video stills.",
3653
+ },
3522
3654
  quality: { type: "string", enum: ["low", "medium", "high"] },
3523
3655
  referenceImagePath: { type: "string", description: "Style reference (path or URL)." },
3656
+ transparent: { type: "boolean", description: "Background removed (sticker PNG)." },
3657
+ provider: { type: "string", enum: ["auto", "replicate", "higgsfield"] },
3524
3658
  outputName: { type: "string" },
3525
3659
  },
3526
3660
  required: ["prompt"],
@@ -3671,6 +3805,350 @@ const TOOLS = [
3671
3805
  },
3672
3806
  command: "media.generate-presenter",
3673
3807
  },
3808
+ {
3809
+ name: "project_inspect_footage",
3810
+ description:
3811
+ "Import checks before styling camera footage: flat/log (needs a grade), a flat green/blue backdrop (key it), rotation (vertical phone/camera clips). Returns per clip { flat, backdrop, rotation, contrast, blackLevel, saturation, suggestions:[{ why, verb, args }] }; run the suggested verbs.",
3812
+ inputSchema: {
3813
+ type: "object",
3814
+ properties: { id: { type: "string" }, path: { type: "string" }, clipId: { type: "string" } },
3815
+ },
3816
+ command: "project.inspect-footage",
3817
+ },
3818
+ {
3819
+ name: "project_speech_map",
3820
+ description:
3821
+ "The words the speaker STRESSES and where they land on the edited timeline (any language, incl. code-switched speech): measured from the audio (louder, sharp attack, pause before, drawn out) plus numbers and English key terms. Returns { durationMs, moments:[{ atMs, text, phrase, strength 1-3, emphasis, reasons, suggest }] }. Use as cues: zooms on strength 2-3, keyword graphics on the phrase, hits on strength 3. Needs a transcript.",
3822
+ inputSchema: {
3823
+ type: "object",
3824
+ properties: {
3825
+ id: { type: "string" },
3826
+ path: { type: "string" },
3827
+ minGapMs: { type: "number", description: "Default 2500." },
3828
+ everyMs: {
3829
+ type: "number",
3830
+ description:
3831
+ "~one moment per this long. Default 5000; Shorts 3000-4000; long-form 8000-15000.",
3832
+ },
3833
+ words: { type: "boolean", description: "Also return every word's emphasis." },
3834
+ },
3835
+ },
3836
+ command: "project.speech-map",
3837
+ },
3838
+ {
3839
+ name: "project_compose_soundtrack",
3840
+ description:
3841
+ "Score this edit: music + sound design composed FROM THE PROJECT'S OWN TIMELINE (zooms, graphics, pops, cuts, speech, stressed words) and placed under the voice with ducking; takes over zoom/overlay default sounds; replaces the previous score on re-run. Call LAST, after cuts/zooms/graphics. dryRun returns the score for media_compose_soundtrack. Returns { audioPath, scorePath, events, heroMoments, acts, soundsTakenOver, replaced }.",
3842
+ inputSchema: {
3843
+ type: "object",
3844
+ properties: {
3845
+ id: { type: "string" },
3846
+ path: { type: "string" },
3847
+ style: { type: "string", enum: ["talking-head", "short", "promo", "calm"] },
3848
+ mood: { type: "string", enum: ["minor", "major"] },
3849
+ bpm: { type: "number" },
3850
+ sfx: { type: "boolean", description: "Default true." },
3851
+ useSpeech: {
3852
+ type: "boolean",
3853
+ description: "Hero hits on the most stressed words. Default true.",
3854
+ },
3855
+ cues: {
3856
+ type: "array",
3857
+ items: {
3858
+ type: "object",
3859
+ properties: {
3860
+ atMs: { type: "number" },
3861
+ strength: { type: "number" },
3862
+ label: { type: "string" },
3863
+ },
3864
+ required: ["atMs"],
3865
+ },
3866
+ description: "Extra moments, e.g. a graphic's own cue times.",
3867
+ },
3868
+ musicVolume: { type: "number", description: "0-2. Default by style." },
3869
+ takeOverSounds: { type: "boolean", description: "Default true." },
3870
+ dryRun: { type: "boolean" },
3871
+ seed: { type: "number" },
3872
+ },
3873
+ },
3874
+ command: "project.compose-soundtrack",
3875
+ },
3876
+ // ── native motion elements ──────────────────────────────────────
3877
+ {
3878
+ name: "project_add_motion_element",
3879
+ description:
3880
+ "Native motion element: an editable, word-timed graphic the engine draws (preview = export, no HTML render): keyword headline, chip, stamp, count (number/range rolls up), steps, slam (+ camera kick), behind (word behind the speaker), lowerThird, highlight, endCard, progress, iconPop, frame (viewfinder / selection box / corners around the speaker). Anchor with wordId (follows the word through cuts) or atMs. Brand kit by default. Returns { elementId, element }.",
3881
+ inputSchema: {
3882
+ type: "object",
3883
+ properties: {
3884
+ id: { type: "string" },
3885
+ path: { type: "string" },
3886
+ ...MOTION_ELEMENT_PROPS,
3887
+ expectedRevision: { type: "number" },
3888
+ },
3889
+ required: ["type", "content"],
3890
+ },
3891
+ command: "project.add-motion-element",
3892
+ },
3893
+ {
3894
+ name: "project_add_motion_elements",
3895
+ description:
3896
+ "Add many native motion elements in one edit (one revision). All or nothing: an invalid one rejects the batch with 'element <i>: <reason>'. Returns { elementIds, elements }.",
3897
+ inputSchema: {
3898
+ type: "object",
3899
+ properties: {
3900
+ id: { type: "string" },
3901
+ path: { type: "string" },
3902
+ elements: {
3903
+ type: "array",
3904
+ items: {
3905
+ type: "object",
3906
+ properties: MOTION_ELEMENT_PROPS,
3907
+ required: ["type", "content"],
3908
+ },
3909
+ },
3910
+ expectedRevision: { type: "number" },
3911
+ },
3912
+ required: ["elements"],
3913
+ },
3914
+ command: "project.add-motion-elements",
3915
+ },
3916
+ {
3917
+ name: "project_update_motion_element",
3918
+ description:
3919
+ "Change a native motion element; pass only what changes. style merges (field null = default); zone/layer/sound/zIndex null = default; atMs/wordId re-anchors and keeps the length. Editing a style-edit element makes it yours (re-runs keep it).",
3920
+ inputSchema: {
3921
+ type: "object",
3922
+ properties: {
3923
+ id: { type: "string" },
3924
+ path: { type: "string" },
3925
+ elementId: { type: "string" },
3926
+ ...MOTION_ELEMENT_PROPS,
3927
+ expectedRevision: { type: "number" },
3928
+ },
3929
+ required: ["elementId"],
3930
+ },
3931
+ command: "project.update-motion-element",
3932
+ },
3933
+ {
3934
+ name: "project_remove_motion_element",
3935
+ description: "Remove a native motion element by id.",
3936
+ inputSchema: {
3937
+ type: "object",
3938
+ properties: {
3939
+ id: { type: "string" },
3940
+ path: { type: "string" },
3941
+ elementId: { type: "string" },
3942
+ expectedRevision: { type: "number" },
3943
+ },
3944
+ required: ["elementId"],
3945
+ },
3946
+ command: "project.remove-motion-element",
3947
+ },
3948
+ {
3949
+ name: "project_list_motion_elements",
3950
+ description:
3951
+ "List placed native motion elements (id, type, span, content, style, zone, layer, sound, wordId, origin) with one-line summaries. catalog=true adds every type's content shape and defaults.",
3952
+ inputSchema: {
3953
+ type: "object",
3954
+ properties: {
3955
+ id: { type: "string" },
3956
+ path: { type: "string" },
3957
+ type: { type: "string", enum: MOTION_ELEMENT_TYPES },
3958
+ catalog: { type: "boolean" },
3959
+ },
3960
+ },
3961
+ command: "project.list-motion-elements",
3962
+ },
3963
+ {
3964
+ name: "project_style_edit",
3965
+ description:
3966
+ "Style a talking-head / Short in one call: inspect-footage (applyFixes applies grade/key), speech-map, native motion elements on the stressed moments (numbers → count, strength-3 English statement → slam, other heroes → keyword/stamp, code-switch key terms → chip, first moment → hook, last 4 s → end card, long-form topic turns → lower third) + silent zooms (1.5x/1.25x, 1.8x hero), then compose-soundtrack. paper-cut = the Vox paper-cut look (serif ink on torn paper strips, newsprint backdrop, speaker cut out with a white keyline in the lower half, paper foley, no zooms; look=false keeps your background/framing). Latin text only: for non-Latin speech pass texts { wordId: 'English keyword' } (else zoom only). Re-runs replace only what style-edit placed. Run after cuts. dryRun returns the plan.",
3967
+ inputSchema: {
3968
+ type: "object",
3969
+ properties: {
3970
+ id: { type: "string" },
3971
+ path: { type: "string" },
3972
+ style: {
3973
+ type: "string",
3974
+ enum: ["bold-short", "editorial-long", "clean-tutorial", "paper-cut"],
3975
+ },
3976
+ format: {
3977
+ type: "string",
3978
+ enum: ["short", "long"],
3979
+ description: "Density; default from aspect (9:16 = short).",
3980
+ },
3981
+ applyFixes: { type: "boolean", description: "Apply inspect-footage fixes. Default false." },
3982
+ backdrop: {
3983
+ type: "string",
3984
+ description:
3985
+ "Background behind a keyed-out speaker (applyFixes) and the paper-cut backdrop: wallpaper id, colour or CSS gradient. Default: the style's studio gradient when no background is set; paper-cut uses /wallpapers/paper-cream.jpg.",
3986
+ },
3987
+ look: {
3988
+ type: "boolean",
3989
+ description:
3990
+ "paper-cut only: also set the look (paper backdrop, speaker cutout + white keyline, lower-half framing). Default true.",
3991
+ },
3992
+ texts: {
3993
+ type: "object",
3994
+ additionalProperties: { type: "string" },
3995
+ description: "{ [wordId]: 'English keyword' } overrides.",
3996
+ },
3997
+ endCard: {
3998
+ type: "array",
3999
+ items: { type: "string" },
4000
+ description: "[line1, line2]. Default: hook + hero line.",
4001
+ },
4002
+ accent: { type: "string", description: "Accent colour for every element; default brand." },
4003
+ highlight: { type: "string", description: "Emphasis colour; default brand." },
4004
+ zooms: { type: "boolean", description: "Default true." },
4005
+ score: { type: "boolean", description: "Run compose-soundtrack. Default true." },
4006
+ mood: { type: "string", enum: ["minor", "major"] },
4007
+ dryRun: { type: "boolean" },
4008
+ expectedRevision: { type: "number" },
4009
+ },
4010
+ },
4011
+ command: "project.style-edit",
4012
+ },
4013
+ {
4014
+ name: "media_compose_soundtrack",
4015
+ description:
4016
+ "Compose + render an original soundtrack (music, beats, SFX) from a score on the SAME timeline as the edit, so hits land on their moments. Synthesized locally: no key, no licensing, ~1 s per 15 s. Plan cues from the edit first, set bpm so cuts land on beats, then place sounds on cues. Returns { audioPath, scorePath, durationMs, lufs, truePeakDb, events, cues, warnings }; place with project_add_audio. Format + recipes: skill reference/soundtrack.md.",
4017
+ inputSchema: {
4018
+ type: "object",
4019
+ properties: {
4020
+ score: {
4021
+ type: "object",
4022
+ description: "The score. Pass score or scorePath.",
4023
+ properties: {
4024
+ duration: { type: "number", description: "Seconds (max 600)." },
4025
+ seed: { type: "number" },
4026
+ cues: {
4027
+ type: "object",
4028
+ description: 'Named moments: { "slam": 3.0, "logo": "slam+9.95" }.',
4029
+ additionalProperties: { type: ["number", "string"] },
4030
+ },
4031
+ events: {
4032
+ type: "array",
4033
+ items: {
4034
+ type: "object",
4035
+ properties: {
4036
+ at: { type: ["number", "string"], description: 'Seconds, a cue, or "cue+0.2".' },
4037
+ sound: {
4038
+ type: "string",
4039
+ enum: [
4040
+ "kick",
4041
+ "snare",
4042
+ "clap",
4043
+ "hat",
4044
+ "openhat",
4045
+ "tick",
4046
+ "click",
4047
+ "blip",
4048
+ "pop",
4049
+ "typing",
4050
+ "whoosh",
4051
+ "riser",
4052
+ "downlifter",
4053
+ "impact",
4054
+ "boom",
4055
+ "subdrop",
4056
+ "shimmer",
4057
+ "bass",
4058
+ "pluck",
4059
+ "pad",
4060
+ "lead",
4061
+ "bell",
4062
+ "keys",
4063
+ ],
4064
+ },
4065
+ gain: { type: "number" },
4066
+ pan: { type: "number", description: "-1 left .. 1 right" },
4067
+ dur: { type: "number" },
4068
+ note: { type: ["string", "number"], description: '"A3" or MIDI' },
4069
+ notes: { type: "array", items: { type: ["string", "number"] } },
4070
+ chord: { type: "string", description: '"Am", "Fmaj7"' },
4071
+ from: { type: "number", description: "Sweep start Hz" },
4072
+ to: { type: "number", description: "Sweep end Hz" },
4073
+ bright: { type: "number", description: "0 dark .. 1 bright" },
4074
+ reverb: { type: "number", description: "0..1 send" },
4075
+ repeat: {
4076
+ type: "object",
4077
+ properties: {
4078
+ count: { type: "number" },
4079
+ every: { type: "number" },
4080
+ gainStep: { type: "number" },
4081
+ alternatePan: { type: "boolean" },
4082
+ jitter: { type: "number" },
4083
+ },
4084
+ },
4085
+ },
4086
+ required: ["at", "sound"],
4087
+ },
4088
+ },
4089
+ sections: {
4090
+ type: "array",
4091
+ items: {
4092
+ type: "object",
4093
+ properties: {
4094
+ from: { type: ["number", "string"] },
4095
+ to: { type: ["number", "string"] },
4096
+ bpm: { type: "number" },
4097
+ chords: { type: "array", items: { type: "string" } },
4098
+ chordBeats: { type: "number", description: "Beats per chord, default 4." },
4099
+ drums: {
4100
+ type: "string",
4101
+ enum: ["four-on-floor", "backbeat", "half-time", "breakbeat", "none"],
4102
+ },
4103
+ hats: { type: "string", enum: ["16ths", "8ths", "offbeat", "none"] },
4104
+ bass: { type: "string", enum: ["pulse", "octave", "root", "sustain", "none"] },
4105
+ arp: { type: "string", enum: ["up", "down", "updown", "none"] },
4106
+ arpRate: { type: "string", enum: ["4ths", "8ths", "16ths"] },
4107
+ pad: { type: ["boolean", "number"] },
4108
+ whooshOnChange: { type: "boolean" },
4109
+ melody: {
4110
+ type: "object",
4111
+ properties: {
4112
+ notes: { type: "array", items: { type: ["string", "number", "null"] } },
4113
+ rate: { type: "string", enum: ["4ths", "8ths", "16ths"] },
4114
+ sound: { type: "string", enum: ["pluck", "lead", "bell", "keys"] },
4115
+ gain: { type: "number" },
4116
+ length: { type: "number" },
4117
+ reverb: { type: "number" },
4118
+ },
4119
+ },
4120
+ gain: { type: "number" },
4121
+ mix: {
4122
+ type: "object",
4123
+ description: "Per-part levels 0-2: drums, hats, bass, arp, pad, melody.",
4124
+ },
4125
+ },
4126
+ required: ["from", "to", "bpm"],
4127
+ },
4128
+ },
4129
+ master: {
4130
+ type: "object",
4131
+ properties: {
4132
+ lufs: { type: "number", description: "Default -14." },
4133
+ ceiling: { type: "number", description: "True-peak dBFS, default -1." },
4134
+ fadeIn: { type: "number" },
4135
+ fadeOut: { type: "number" },
4136
+ drive: { type: "number", description: "1 clean .. 4 heavy, default 1.3." },
4137
+ room: { type: "number", description: "Reverb size 0..1." },
4138
+ },
4139
+ },
4140
+ },
4141
+ required: ["duration"],
4142
+ },
4143
+ scorePath: {
4144
+ type: "string",
4145
+ description: "Absolute path to a score JSON (e.g. a previous scorePath).",
4146
+ },
4147
+ name: { type: "string", description: "File name stem." },
4148
+ },
4149
+ },
4150
+ command: "media.compose-soundtrack",
4151
+ },
3674
4152
  {
3675
4153
  name: "media_generate_music",
3676
4154
  description:
@@ -3823,7 +4301,7 @@ const TOOLS = [
3823
4301
  recipe: {
3824
4302
  type: "object",
3825
4303
  description:
3826
- "{ id?, title, description, category, footage?, aspectRatio?, fields: [{ key, label, type: text|longtext|select|color, default?, options?, required?, help? }], prompt, style?: { aspectRatio?, colorCorrection?, lut?: { preset, intensity? }, background?: { mode, image? }, cameraLayout?, captions?: { enabled, templateId? }, wallpaper? }, checklist: string[] }",
4304
+ "{ id?, title, description, category, footage?, aspectRatio?, fields: [{ key, label, type: text|longtext|select|color|images, default?, options?, required?, help?, minCount?, maxCount?, optional? }], prompt, style?: { aspectRatio?, colorCorrection?, lut?: { preset, intensity? }, background?: { mode, image? }, cameraLayout?, captions?: { enabled, templateId? }, wallpaper? }, checklist: string[] }",
3827
4305
  },
3828
4306
  },
3829
4307
  required: ["recipe"],
@@ -4029,7 +4507,11 @@ const TOOLS = [
4029
4507
  aspectRatio: { type: "string", enum: ["16:9", "9:16", "1:1"] },
4030
4508
  width: { type: "number" },
4031
4509
  height: { type: "number" },
4032
- durationMs: { type: "number", description: "Default 2500." },
4510
+ durationMs: {
4511
+ type: "number",
4512
+ description:
4513
+ "Omit when the root declares data-duration (that is the length rendered; a mismatch warns). Else default 2500. Max 600000 (10 min).",
4514
+ },
4033
4515
  frameRate: { type: "number" },
4034
4516
  outputName: { type: "string" },
4035
4517
  audioPath: { type: "string", description: "Muxed in (not when transparent)." },
@@ -4054,7 +4536,10 @@ const TOOLS = [
4054
4536
  aspectRatio: { type: "string", enum: ["16:9", "9:16", "1:1"] },
4055
4537
  width: { type: "number" },
4056
4538
  height: { type: "number" },
4057
- atMs: { type: "number", description: "Max 30000." },
4539
+ atMs: {
4540
+ type: "number",
4541
+ description: "Clamped to the composition's data-duration; result atMs = time captured.",
4542
+ },
4058
4543
  transparent: { type: "boolean" },
4059
4544
  assets: ASSETS_PROP,
4060
4545
  outputName: { type: "string" },
@@ -4129,7 +4614,7 @@ const TOOLS = [
4129
4614
  {
4130
4615
  name: "asset_list_transitions",
4131
4616
  description:
4132
- "List bundled transitions (id, title, category, durationSeconds) for project_add_transition.",
4617
+ "List bundled transitions (id, title, kind overlay|native, effect, category, durationSeconds, defaultSoundId) for project_add_transition.",
4133
4618
  inputSchema: { type: "object", properties: {} },
4134
4619
  command: "asset.list-transitions",
4135
4620
  },
@@ -4290,7 +4775,7 @@ const TOOLS = [
4290
4775
  {
4291
4776
  name: "export_generate_thumbnail",
4292
4777
  description:
4293
- "Generate a YouTube thumbnail for an export (Replicate, spends credits). Pass subject + hook, or omit to derive from the transcript. Returns { imagePath }.",
4778
+ "Generate a YouTube thumbnail for an export (user's image connector: Replicate, else Higgsfield; spends their credits). Pass subject + hook, or omit to derive from the transcript. Returns { imagePath }.",
4294
4779
  inputSchema: {
4295
4780
  type: "object",
4296
4781
  properties: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@writepanda/mcp",
3
- "version": "1.183.0",
3
+ "version": "1.191.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",