@writepanda/mcp 1.285.0 → 1.287.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.
- package/bin/server.mjs +132 -19
- package/package.json +1 -1
package/bin/server.mjs
CHANGED
|
@@ -608,7 +608,7 @@ const MOTION_ELEMENT_PROPS = {
|
|
|
608
608
|
content: {
|
|
609
609
|
type: ["object", "string", "array"],
|
|
610
610
|
description:
|
|
611
|
-
'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 or "@speaker" (the speaker cut out of her shot), label?, twin? (mirrored pair, one each side) }; 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) }. Creator effects (picture slots take a path or "@speaker" = the speaker\'s own live shot, the default): reelWall { images?, count? 4-24 } (tiles of her shot behind her); cardFan { images?, count? 3-9 } (card reveal: cards fan in an arc behind her head); orbit { images?, count? 2-6 } (cards circle her, layer around); polaroids { images?, count? 2-8, captions? } (scattered around her, layer around); wordWall { text, rows? 3-14, background? } (word repeated in rows behind her); neon { text, flicker? }; bloom { text } (colour bloom); scrapbook { text } (ransom-note letters); keycaps { text <=24 } (keys falling, pile at the bottom); productCard { title, subtitle?, price?, image?, button? ("Add to cart"), done? ("Added") }; scrollList { title?, items: (string | { title, subtitle?, image? })[] <=30, look?: playlist|feed } (phone list scrolling); splitScreen { panels? 2|3, images?, zooms? 1-4 } (three scenes). Any script, symbols and emoji draw.',
|
|
611
|
+
'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; omit = equal time per item, the last too; a cue in the last ~1.5 s is pulled in so its item still holds) }; 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 or "@speaker" (the speaker cut out of her shot), label?, twin? (mirrored pair, one each side) }; 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) }. Creator effects (picture slots take a path or "@speaker" = the speaker\'s own live shot, the default): reelWall { images?, count? 4-24 } (tiles of her shot behind her); cardFan { images?, count? 3-9 } (card reveal: cards fan in an arc behind her head); orbit { images?, count? 2-6 } (cards circle her, layer around); polaroids { images?, count? 2-8, captions? } (scattered around her, layer around); wordWall { text, rows? 3-14, background? } (word repeated in rows behind her); neon { text, flicker? }; bloom { text } (colour bloom); scrapbook { text } (ransom-note letters); keycaps { text <=24 } (keys falling, pile at the bottom); productCard { title, subtitle?, price?, image?, button? ("Add to cart"), done? ("Added") }; scrollList { title?, items: (string | { title, subtitle?, image? })[] <=30, look?: playlist|feed } (phone list scrolling); splitScreen { panels? 2|3, images?, zooms? 1-4 } (three scenes). Any script, symbols and emoji draw.',
|
|
612
612
|
},
|
|
613
613
|
atMs: { type: "number", description: "Start, edited ms. Or wordId." },
|
|
614
614
|
wordId: {
|
|
@@ -621,7 +621,8 @@ const MOTION_ELEMENT_PROPS = {
|
|
|
621
621
|
cueWordIds: {
|
|
622
622
|
type: "array",
|
|
623
623
|
items: { type: "string" },
|
|
624
|
-
description:
|
|
624
|
+
description:
|
|
625
|
+
"steps: one word id per item. End the element >= 1.5 s after the last one (a later cue is pulled in).",
|
|
625
626
|
},
|
|
626
627
|
style: {
|
|
627
628
|
type: "object",
|
|
@@ -998,6 +999,7 @@ const TOOLS = [
|
|
|
998
999
|
"marathi",
|
|
999
1000
|
"gujarati",
|
|
1000
1001
|
"punjabi",
|
|
1002
|
+
"turkish",
|
|
1001
1003
|
],
|
|
1002
1004
|
},
|
|
1003
1005
|
},
|
|
@@ -1416,7 +1418,7 @@ const TOOLS = [
|
|
|
1416
1418
|
{
|
|
1417
1419
|
name: "youtube_list_accounts",
|
|
1418
1420
|
description:
|
|
1419
|
-
"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).",
|
|
1421
|
+
"Connected YouTube accounts and channels (pick before export_publish_youtube). Each channel's userLastPrivacy = what the user last picked in the app's Publish window (use only when they ask for their usual setting). 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).",
|
|
1420
1422
|
inputSchema: { type: "object", properties: {} },
|
|
1421
1423
|
command: "youtube.list-accounts",
|
|
1422
1424
|
},
|
|
@@ -1434,7 +1436,7 @@ const TOOLS = [
|
|
|
1434
1436
|
{
|
|
1435
1437
|
name: "export_publish_youtube",
|
|
1436
1438
|
description:
|
|
1437
|
-
"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. Error code daily_limit or youtube_quota_exceeded (with resetsAt): tell the user when it resets; never retry before resetsAt. Returns { videoUrl, channelId }.",
|
|
1439
|
+
"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. Error code daily_limit or youtube_quota_exceeded (with resetsAt): tell the user when it resets; never retry before resetsAt. privacyStatus defaults to unlisted: public only when the user asked. A vertical or square export of 3 min or less becomes a YouTube Short (result short: true); pass addShortsTag to add #Shorts. Returns { videoUrl, channelId, short }.",
|
|
1438
1440
|
inputSchema: {
|
|
1439
1441
|
type: "object",
|
|
1440
1442
|
properties: {
|
|
@@ -1447,6 +1449,10 @@ const TOOLS = [
|
|
|
1447
1449
|
categoryId: { type: "string" },
|
|
1448
1450
|
privacyStatus: { type: "string", enum: ["public", "unlisted", "private"] },
|
|
1449
1451
|
setThumbnail: { type: "boolean" },
|
|
1452
|
+
addShortsTag: {
|
|
1453
|
+
type: "boolean",
|
|
1454
|
+
description: "Add #Shorts to the description when the export is a Short. Default false.",
|
|
1455
|
+
},
|
|
1450
1456
|
subtitles: {
|
|
1451
1457
|
type: "boolean",
|
|
1452
1458
|
description:
|
|
@@ -1463,7 +1469,7 @@ const TOOLS = [
|
|
|
1463
1469
|
{
|
|
1464
1470
|
name: "social_channels",
|
|
1465
1471
|
description:
|
|
1466
|
-
"Social accounts connected in the active workspace (Instagram, TikTok, Facebook, LinkedIn, X) with ids, limits and per-post settings, plus each network's count.",
|
|
1472
|
+
"Social accounts connected in the active workspace (Instagram, TikTok, Facebook, LinkedIn, X) with ids, limits and per-post settings, plus each network's count. X accounts carry xPremium (25,000-char limit when true, else 280). YouTube and YouTube Shorts aren't here: use youtube_list_accounts + export_publish_youtube.",
|
|
1467
1473
|
inputSchema: { type: "object", properties: {} },
|
|
1468
1474
|
command: "social.channels",
|
|
1469
1475
|
},
|
|
@@ -1490,6 +1496,20 @@ const TOOLS = [
|
|
|
1490
1496
|
},
|
|
1491
1497
|
command: "social.connect-status",
|
|
1492
1498
|
},
|
|
1499
|
+
{
|
|
1500
|
+
name: "social_set_x_premium",
|
|
1501
|
+
description:
|
|
1502
|
+
"Mark a connected X account as X Premium (posts up to 25,000 chars) or standard (280). Only when the user says the account has Premium: X rejects long posts from other accounts. Returns the updated channel.",
|
|
1503
|
+
inputSchema: {
|
|
1504
|
+
type: "object",
|
|
1505
|
+
properties: {
|
|
1506
|
+
channelId: { type: "string", description: "The X account's id from social_channels." },
|
|
1507
|
+
premium: { type: "boolean" },
|
|
1508
|
+
},
|
|
1509
|
+
required: ["channelId", "premium"],
|
|
1510
|
+
},
|
|
1511
|
+
command: "social.set-x-premium",
|
|
1512
|
+
},
|
|
1493
1513
|
{
|
|
1494
1514
|
name: "social_disconnect",
|
|
1495
1515
|
description:
|
|
@@ -1803,7 +1823,7 @@ const TOOLS = [
|
|
|
1803
1823
|
{
|
|
1804
1824
|
name: "project_edit_summary",
|
|
1805
1825
|
description:
|
|
1806
|
-
"What the edit contains now: lengths (editedDurationMs, sourceDurationMs, removedMs cut or sped up), counts (cuts, zooms, motionElements, graphics, transitions, brollBeats, mediaOverlays, captions { on, template }, audio { narration, music, soundEffects }, voiceEnhancedClips vs loudnessNormalizedClips, ...) and highlights[] (plain lines). Each recording counts once, so splits change neither source length nor cuts. Cheap; use it to report an editing pass instead of project_read.",
|
|
1826
|
+
"What the edit contains now: lengths (editedDurationMs, sourceDurationMs, removedMs cut or sped up), counts (cuts, zooms, motionElements, graphics, transitions, brollBeats, mediaOverlays, captions { on, template }, audio { narration, music, soundEffects }, audioTracks[{id, kind voice|music|sfx, basis}] (the same kind the export mix and audio_preview_mix use), voiceEnhancedClips vs loudnessNormalizedClips, ...) and highlights[] (plain lines). Each recording counts once, so splits change neither source length nor cuts. Cheap; use it to report an editing pass instead of project_read.",
|
|
1807
1827
|
inputSchema: {
|
|
1808
1828
|
type: "object",
|
|
1809
1829
|
properties: {
|
|
@@ -2092,14 +2112,17 @@ const TOOLS = [
|
|
|
2092
2112
|
],
|
|
2093
2113
|
[
|
|
2094
2114
|
"stop-recording",
|
|
2095
|
-
"Stop recording
|
|
2115
|
+
"Stop recording. The take goes into the episode project by itself (desktop track once finalized, each guest's as their upload lands, even after the studio is left); returns savedTo and takeId. podcast_status shows finalization and guest uploads.",
|
|
2096
2116
|
],
|
|
2097
2117
|
[
|
|
2098
2118
|
"start-screen-share",
|
|
2099
2119
|
"Open the desktop picker to share the host screen with everyone in the call (any time, backstage included; it's recorded while recording). One sharer at a time. Waits for the host to choose; fails if the picker is cancelled or capture fails.",
|
|
2100
2120
|
],
|
|
2101
2121
|
["stop-screen-share", "Stop and finalize the host's podcast screen share."],
|
|
2102
|
-
[
|
|
2122
|
+
[
|
|
2123
|
+
"open-in-editor",
|
|
2124
|
+
"Open the finished podcast's episode in the editor when guest media has arrived (the take is already in the episode project).",
|
|
2125
|
+
],
|
|
2103
2126
|
].map(([noun, description]) => ({
|
|
2104
2127
|
name: `podcast_${noun.replaceAll("-", "_")}`,
|
|
2105
2128
|
description,
|
|
@@ -2623,6 +2646,34 @@ const TOOLS = [
|
|
|
2623
2646
|
},
|
|
2624
2647
|
command: "podcast.list-upcoming",
|
|
2625
2648
|
},
|
|
2649
|
+
{
|
|
2650
|
+
name: "podcast_found_recordings",
|
|
2651
|
+
description:
|
|
2652
|
+
"Recordings that belong to a podcast episode but never reached it (older takes left by Back before Open in editor): guest tracks matched by session code plus the nearest desktop camera recording, each with a key. No path / id / sessionCode = every episode.",
|
|
2653
|
+
inputSchema: {
|
|
2654
|
+
type: "object",
|
|
2655
|
+
properties: {
|
|
2656
|
+
path: { type: "string" },
|
|
2657
|
+
id: { type: "string" },
|
|
2658
|
+
sessionCode: { type: "string" },
|
|
2659
|
+
},
|
|
2660
|
+
},
|
|
2661
|
+
command: "podcast.found-recordings",
|
|
2662
|
+
},
|
|
2663
|
+
{
|
|
2664
|
+
name: "podcast_add_found_recordings",
|
|
2665
|
+
description:
|
|
2666
|
+
"Add a podcast_found_recordings take (path + key) to its episode: it becomes the episode's podcast clip, files move into the project folder. Matched by time, so ask the user first; check the sync afterwards (project_auto_sync_podcast).",
|
|
2667
|
+
inputSchema: {
|
|
2668
|
+
type: "object",
|
|
2669
|
+
properties: {
|
|
2670
|
+
path: { type: "string", description: "The episode's project file." },
|
|
2671
|
+
key: { type: "string", description: "The found take's key." },
|
|
2672
|
+
},
|
|
2673
|
+
required: ["path", "key"],
|
|
2674
|
+
},
|
|
2675
|
+
command: "podcast.add-found-recordings",
|
|
2676
|
+
},
|
|
2626
2677
|
{
|
|
2627
2678
|
name: "podcast_get_episode",
|
|
2628
2679
|
description:
|
|
@@ -3698,6 +3749,39 @@ const TOOLS = [
|
|
|
3698
3749
|
},
|
|
3699
3750
|
command: "project.remove-mute-region",
|
|
3700
3751
|
},
|
|
3752
|
+
{
|
|
3753
|
+
name: "project_add_original_voice",
|
|
3754
|
+
description:
|
|
3755
|
+
'Turn Enhance voice off for an edited-time span ("Original voice"): the main track plays its original audio there, enhanced elsewhere. Only enhanced clips change. Returns regionId (editor.enhanceOffRegions).',
|
|
3756
|
+
inputSchema: {
|
|
3757
|
+
type: "object",
|
|
3758
|
+
properties: {
|
|
3759
|
+
id: { type: "string" },
|
|
3760
|
+
path: { type: "string" },
|
|
3761
|
+
atMs: { type: "number" },
|
|
3762
|
+
durationMs: { type: "number" },
|
|
3763
|
+
expectedRevision: { type: "number" },
|
|
3764
|
+
},
|
|
3765
|
+
required: ["atMs", "durationMs"],
|
|
3766
|
+
},
|
|
3767
|
+
command: "project.add-original-voice",
|
|
3768
|
+
},
|
|
3769
|
+
{
|
|
3770
|
+
name: "project_remove_original_voice",
|
|
3771
|
+
description:
|
|
3772
|
+
"Remove an Original voice region so Enhance voice applies there again (ids: editor.enhanceOffRegions[].id).",
|
|
3773
|
+
inputSchema: {
|
|
3774
|
+
type: "object",
|
|
3775
|
+
properties: {
|
|
3776
|
+
id: { type: "string" },
|
|
3777
|
+
path: { type: "string" },
|
|
3778
|
+
regionId: { type: "string" },
|
|
3779
|
+
expectedRevision: { type: "number" },
|
|
3780
|
+
},
|
|
3781
|
+
required: ["regionId"],
|
|
3782
|
+
},
|
|
3783
|
+
command: "project.remove-original-voice",
|
|
3784
|
+
},
|
|
3701
3785
|
{
|
|
3702
3786
|
name: "project_add_bleep",
|
|
3703
3787
|
description:
|
|
@@ -4143,7 +4227,7 @@ const TOOLS = [
|
|
|
4143
4227
|
{
|
|
4144
4228
|
name: "project_review_moment",
|
|
4145
4229
|
description:
|
|
4146
|
-
"LOOK AND LISTEN to a moment of the edit as the viewer gets it: a contact sheet of the frames around atMs (edited ms) or wordId, the EXPORTED mix of that stretch transcribed and compared with what the edit should play (extra = leftover of a cut word or line, missing = clipped word), whether each cut lands in quiet or speech, nearby edits and stutters, plus a plain verdict. Use after every cut / pause / insert fix BEFORE telling the user it's fixed, and when the user names a moment ('at 16 seconds'). ASYNC: { jobId }; job_wait.",
|
|
4230
|
+
"LOOK AND LISTEN to a moment of the edit as the viewer gets it: a contact sheet of the frames around atMs (edited ms) or wordId, the EXPORTED mix of that stretch transcribed and compared with what the edit should play (the clips' words AND narration / voiceover track words; extra = leftover of a cut word or line, missing = clipped word), whether each cut that interrupts the main track's sound lands in quiet or speech (judged on the main track alone, so narration or music playing across it doesn't count; a join of silent clips such as images is never a cut in the speech), nearby edits and stutters, plus a plain verdict. Use after every cut / pause / insert fix BEFORE telling the user it's fixed, and when the user names a moment ('at 16 seconds'). ASYNC: { jobId }; job_wait.",
|
|
4147
4231
|
inputSchema: {
|
|
4148
4232
|
type: "object",
|
|
4149
4233
|
properties: {
|
|
@@ -4198,7 +4282,7 @@ const TOOLS = [
|
|
|
4198
4282
|
{
|
|
4199
4283
|
name: "audio_preview_mix",
|
|
4200
4284
|
description:
|
|
4201
|
-
"Render final export audio only to temporary .m4a plus description and voice/music/SFX stem RMS/masking flags. Edited-time range fromMs..toMs inside 0..editedDurationMs (project_read), default whole project, max 10 minutes. Async: job.wait.",
|
|
4285
|
+
"Render final export audio only to temporary .m4a plus description and voice/music/SFX stem RMS/masking flags; tracks[{id, kind, basis}] = which stem each audio track went to (same as project_edit_summary audioTracks). Edited-time range fromMs..toMs inside 0..editedDurationMs (project_read), default whole project, max 10 minutes. Async: job.wait.",
|
|
4202
4286
|
inputSchema: {
|
|
4203
4287
|
type: "object",
|
|
4204
4288
|
properties: {
|
|
@@ -5126,7 +5210,7 @@ const TOOLS = [
|
|
|
5126
5210
|
{
|
|
5127
5211
|
name: "project_auto_reframe",
|
|
5128
5212
|
description:
|
|
5129
|
-
"Active-speaker auto-reframe: frames whoever talks, shot by shot (face tracking, no speaker IDs needed). Landscape footage on a vertical / square canvas gets a crop that follows the speaker; footage that already has the canvas shape (a 16:9 wide of two hosts in a 16:9 video) gets a punch-in on the speaker inside the frame, which is also how to get face-tracked zooms on the main video. zoom = fixed punch-in. split=true stacks two people (podcast / interview wide) one above the other on a 9:16 canvas. Slow (minutes). clear reverts. Concurrent saves are merged and retried (three attempts). Exhausted conflicts report code revision_conflict / status 409 (job.errorDetails for async jobs).",
|
|
5213
|
+
"Active-speaker auto-reframe: frames whoever talks, shot by shot (face tracking, no speaker IDs needed). Landscape footage on a vertical / square canvas gets a crop that follows the speaker; footage that already has the canvas shape (a 16:9 wide of two hosts in a 16:9 video) gets a punch-in on the speaker inside the frame, which is also how to get face-tracked zooms on the main video. zoom = fixed punch-in. split=true stacks two people (podcast / interview wide) one above the other on a 9:16 canvas (not on a clip with its own camera video). In the vertical-stack camera layout the crop is computed for the main video's top half. Slow (minutes). clear reverts. Concurrent saves are merged and retried (three attempts). Exhausted conflicts report code revision_conflict / status 409 (job.errorDetails for async jobs).",
|
|
5130
5214
|
inputSchema: {
|
|
5131
5215
|
type: "object",
|
|
5132
5216
|
properties: {
|
|
@@ -5396,6 +5480,12 @@ const TOOLS = [
|
|
|
5396
5480
|
type: "boolean",
|
|
5397
5481
|
description: "Repeat the file to endMs (a ~30 s Lyria clip under a longer video).",
|
|
5398
5482
|
},
|
|
5483
|
+
audioKind: {
|
|
5484
|
+
type: "string",
|
|
5485
|
+
enum: ["voice", "music", "sfx"],
|
|
5486
|
+
description:
|
|
5487
|
+
"What the track is (its mix bus, preview-mix stem and edit-summary count). Omit for bundled library files and generated narration / music / SFX (known); pass it for other files, e.g. music for an uploaded song. Change later: project_update_region regionType=audio-overlay patch {audioKind}.",
|
|
5488
|
+
},
|
|
5399
5489
|
transcribe: {
|
|
5400
5490
|
type: "boolean",
|
|
5401
5491
|
description:
|
|
@@ -5807,7 +5897,7 @@ const TOOLS = [
|
|
|
5807
5897
|
{
|
|
5808
5898
|
name: "project_set_clip_webcam",
|
|
5809
5899
|
description:
|
|
5810
|
-
"Attach, replace or remove a clip's camera video
|
|
5900
|
+
"Attach, replace or remove a clip's camera video, in sync from its first frame: a presenter camera over a screen recording, or a REACTION video (main clip = the video reacted to, camera = the reactor; audio=mix plays both). The main video keeps its kind. In 9:16 use set-webcam-layout vertical-stack (main on top, camera below), then auto-reframe frames the main video inside its half. Not for podcast composites.",
|
|
5811
5901
|
inputSchema: {
|
|
5812
5902
|
type: "object",
|
|
5813
5903
|
properties: {
|
|
@@ -5818,8 +5908,9 @@ const TOOLS = [
|
|
|
5818
5908
|
webcam: { type: "string", description: "Absolute path, or '' to remove." },
|
|
5819
5909
|
audio: {
|
|
5820
5910
|
type: "string",
|
|
5821
|
-
enum: ["auto", "camera", "keep"],
|
|
5822
|
-
description:
|
|
5911
|
+
enum: ["auto", "mix", "camera", "keep"],
|
|
5912
|
+
description:
|
|
5913
|
+
"auto: camera audio if the main video is silent; mix: both sounds (reaction video); camera: camera's sound only; keep: unchanged. Re-transcribe after a change.",
|
|
5823
5914
|
},
|
|
5824
5915
|
expectedRevision: { type: "number" },
|
|
5825
5916
|
},
|
|
@@ -5895,13 +5986,14 @@ const TOOLS = [
|
|
|
5895
5986
|
{
|
|
5896
5987
|
name: "system_set_transcription_preferences",
|
|
5897
5988
|
description:
|
|
5898
|
-
"Save workspace transcription language, provider and/or on-device engine atomically (pass any). engine: auto (Parakeet for English/auto-detect, Whisper LOCKED to any other named language), parakeet (keeps fillers, detects the language, can't be locked), whisper (locks to the language). Confirm consent to upload audio for cloud. Cloud and apple cannot use auto.",
|
|
5989
|
+
"Save workspace transcription language, provider and/or on-device engine atomically (pass any). engine: auto (Parakeet for English/auto-detect, Whisper LOCKED to any other named language), parakeet (keeps fillers, detects the language, can't be locked), whisper (locks to the language). Confirm consent to upload audio for cloud. Cloud and apple cannot use auto. fallback: when the cloud provider / Apple speech still fails after retries, local (default: this computer transcribes only the failed parts, reported) or none (fail, never transcribe on this computer: use when the user wants the cloud provider only).",
|
|
5899
5990
|
inputSchema: {
|
|
5900
5991
|
type: "object",
|
|
5901
5992
|
properties: {
|
|
5902
5993
|
language: { type: "string" },
|
|
5903
5994
|
provider: { type: "string", enum: ["local", "apple", "deepgram", "elevenlabs"] },
|
|
5904
5995
|
engine: { type: "string", enum: ["auto", "parakeet", "whisper"] },
|
|
5996
|
+
fallback: { type: "string", enum: ["local", "none"] },
|
|
5905
5997
|
},
|
|
5906
5998
|
},
|
|
5907
5999
|
command: "system.set-transcription-preferences",
|
|
@@ -5910,13 +6002,19 @@ const TOOLS = [
|
|
|
5910
6002
|
{
|
|
5911
6003
|
name: "transcript_transcribe",
|
|
5912
6004
|
description:
|
|
5913
|
-
"Transcribe clips with word timestamps (default: only clips without words). Asking again while the same transcription is still running returns that job (alreadyRunning: true), never a second run: keep waiting on it. Engine: language auto/english = Parakeet; any other named language = local Whisper LOCKED to it (engine override). Returns { jobId, plan } (plan = engine + reason, ranges, totalAudioMs: the audio that will be processed, estimate/estimatedMs: how long on this computer, advice: faster options for a long Whisper run; tell the user for long jobs). Long Whisper runs are saved chunk by chunk: stalls restart from the last chunk automatically; after job_cancel, a crash or an app restart, transcribing again resumes there. Progress is real (audio decoded); time left in progress.message and progress.data.etaMs. To REDO a transcript (wrong language, bad run) confirm language, pass per-run language and provider overrides, 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 tail trim, e.g. one half of a split; only the kept parts when cuts hide most of a long recording; result transcribedRanges[]). Clips with no audio track are skipped (result noAudioClips), not failed. Result warnings[] flags words that can't cover the audio (sound with no words, too few words: music, noise, another language) and a re-run with the same words as before: surface it, and get a second reading with engine whisper or a cloud provider (Parakeet isn't locked by language english, so retrying the same way repeats it). ASYNC: { jobId }, job_wait. Concurrent saves are merged and retried (three attempts). Exhausted conflicts report code revision_conflict / status 409 (job.errorDetails for async jobs).",
|
|
6005
|
+
"Transcribe clips with word timestamps (default: only clips without words). Asking again while the same transcription is still running returns that job (alreadyRunning: true), never a second run: keep waiting on it. Engine: language auto/english = Parakeet; any other named language = local Whisper LOCKED to it (engine override). Returns { jobId, plan } (plan = engine + reason, ranges, totalAudioMs: the audio that will be processed, estimate/estimatedMs: how long on this computer, advice: faster options for a long Whisper run; tell the user for long jobs). Long Whisper runs are saved chunk by chunk: stalls restart from the last chunk automatically; after job_cancel, a crash or an app restart, transcribing again resumes there. Progress is real (audio decoded); time left in progress.message and progress.data.etaMs. To REDO a transcript (wrong language, bad run) confirm language, pass per-run language and provider overrides, 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 tail trim, e.g. one half of a split; only the kept parts when cuts hide most of a long recording; result transcribedRanges[]). Clips with no audio track are skipped (result noAudioClips), not failed. Result warnings[] flags words that can't cover the audio (sound with no words, too few words: music, noise, another language) and a re-run with the same words as before: surface it, and get a second reading with engine whisper or a cloud provider (Parakeet isn't locked by language english, so retrying the same way repeats it). ASYNC: { jobId }, job_wait. Concurrent saves are merged and retried (three attempts). Exhausted conflicts report code revision_conflict / status 409 (job.errorDetails for async jobs). CLOUD (deepgram / elevenlabs): long audio goes up in ~5 min parts, each retried on network drops, timeouts, 5xx and 429. fallback: none = parts that still fail FAIL the job (job.errorDetails.code cloud_failed, failedRanges; nothing runs on this computer; parts that worked are kept for a re-run), local = this computer transcribes only the failed parts. Default none when you pass a cloud/apple provider, else the workspace setting. The job says which engine really ran: progress.data.engine / fallbackFrom while running, result clips[].engine / engines / fallback. job_cancel stops every upload, retry and on-device helper of the job.",
|
|
5914
6006
|
inputSchema: {
|
|
5915
6007
|
type: "object",
|
|
5916
6008
|
properties: {
|
|
5917
6009
|
id: { type: "string" },
|
|
5918
6010
|
path: { type: "string" },
|
|
5919
6011
|
clipId: { type: "string", description: "Only this clip, even if already transcribed." },
|
|
6012
|
+
fallback: {
|
|
6013
|
+
type: "string",
|
|
6014
|
+
enum: ["local", "none"],
|
|
6015
|
+
description:
|
|
6016
|
+
"When the cloud provider / Apple speech still fails after retries: none = fail the job (never transcribe on this computer), local = this computer transcribes only the failed parts. Default: none when provider is passed, else the workspace setting.",
|
|
6017
|
+
},
|
|
5920
6018
|
language: {
|
|
5921
6019
|
type: "string",
|
|
5922
6020
|
description:
|
|
@@ -8676,13 +8774,18 @@ const TOOLS = [
|
|
|
8676
8774
|
{
|
|
8677
8775
|
name: "llm_generate_title",
|
|
8678
8776
|
description:
|
|
8679
|
-
"Generate a YouTube title from what the EDITED video says (cut words left out) with the local LLM. Returns { title }: one complete phrase within maxChars, never cut mid-word.",
|
|
8777
|
+
"Generate a YouTube title from what the EDITED video says (cut words left out) with the local LLM. Names the people speaking when the project knows them (podcast participants); instruction steers it. Returns { title }: one complete phrase within maxChars, never cut mid-word.",
|
|
8680
8778
|
inputSchema: {
|
|
8681
8779
|
type: "object",
|
|
8682
8780
|
properties: {
|
|
8683
8781
|
id: { type: "string" },
|
|
8684
8782
|
path: { type: "string" },
|
|
8685
8783
|
maxChars: { type: "number" },
|
|
8784
|
+
instruction: {
|
|
8785
|
+
type: "string",
|
|
8786
|
+
description:
|
|
8787
|
+
"Optional steer from the user (what to focus on), max 500 chars. Speaker names the project knows are used automatically.",
|
|
8788
|
+
},
|
|
8686
8789
|
},
|
|
8687
8790
|
},
|
|
8688
8791
|
command: "llm.generate-title",
|
|
@@ -8690,13 +8793,18 @@ const TOOLS = [
|
|
|
8690
8793
|
{
|
|
8691
8794
|
name: "llm_generate_description",
|
|
8692
8795
|
description:
|
|
8693
|
-
"Generate a 3-5 sentence YouTube description from what the EDITED video says, with the local LLM. Returns { description, warnings? }: a warning means it repeated the transcript, so write the description yourself.",
|
|
8796
|
+
"Generate a 3-5 sentence YouTube description from what the EDITED video says, with the local LLM. Names the people speaking when the project knows them (podcast participants); instruction steers what it focuses on. Returns { description, warnings? }: a warning means it repeated the transcript, so write the description yourself.",
|
|
8694
8797
|
inputSchema: {
|
|
8695
8798
|
type: "object",
|
|
8696
8799
|
properties: {
|
|
8697
8800
|
id: { type: "string" },
|
|
8698
8801
|
path: { type: "string" },
|
|
8699
8802
|
maxChars: { type: "number" },
|
|
8803
|
+
instruction: {
|
|
8804
|
+
type: "string",
|
|
8805
|
+
description:
|
|
8806
|
+
"Optional steer from the user (what to focus on), max 500 chars. Speaker names the project knows are used automatically.",
|
|
8807
|
+
},
|
|
8700
8808
|
},
|
|
8701
8809
|
},
|
|
8702
8810
|
command: "llm.generate-description",
|
|
@@ -8725,6 +8833,11 @@ const TOOLS = [
|
|
|
8725
8833
|
id: { type: "string" },
|
|
8726
8834
|
path: { type: "string" },
|
|
8727
8835
|
maxChapters: { type: "number" },
|
|
8836
|
+
instruction: {
|
|
8837
|
+
type: "string",
|
|
8838
|
+
description:
|
|
8839
|
+
"Optional steer from the user (what to focus on), max 500 chars. Speaker names the project knows are used automatically.",
|
|
8840
|
+
},
|
|
8728
8841
|
},
|
|
8729
8842
|
},
|
|
8730
8843
|
command: "llm.generate-timestamps",
|
|
@@ -8939,7 +9052,7 @@ const TOOLS = [
|
|
|
8939
9052
|
{
|
|
8940
9053
|
name: "clips_export",
|
|
8941
9054
|
description:
|
|
8942
|
-
"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[] }.",
|
|
9055
|
+
"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[{ clipId, outputPath, exportId }] }. To publish a clip, export it here, then pass exportId to export_publish_youtube (9:16 = Short) or export_publish_social.",
|
|
8943
9056
|
inputSchema: {
|
|
8944
9057
|
type: "object",
|
|
8945
9058
|
properties: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@writepanda/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.287.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",
|