@writepanda/mcp 1.9.4 → 1.11.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 +458 -23
- package/package.json +1 -1
package/bin/server.mjs
CHANGED
|
@@ -34,10 +34,7 @@ import path from "node:path";
|
|
|
34
34
|
import process from "node:process";
|
|
35
35
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
36
36
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
37
|
-
import {
|
|
38
|
-
CallToolRequestSchema,
|
|
39
|
-
ListToolsRequestSchema,
|
|
40
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
37
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
41
38
|
|
|
42
39
|
// ── Credentials + transport ───────────────────────────────────────────
|
|
43
40
|
|
|
@@ -176,7 +173,8 @@ const TOOLS = [
|
|
|
176
173
|
},
|
|
177
174
|
{
|
|
178
175
|
name: "project_show",
|
|
179
|
-
description:
|
|
176
|
+
description:
|
|
177
|
+
"Resolve a project's path + summary metadata by id or path. Cheaper than project_read.",
|
|
180
178
|
inputSchema: {
|
|
181
179
|
type: "object",
|
|
182
180
|
properties: {
|
|
@@ -189,7 +187,7 @@ const TOOLS = [
|
|
|
189
187
|
{
|
|
190
188
|
name: "project_read",
|
|
191
189
|
description:
|
|
192
|
-
"Read a project's full JSON. Returns { path, project }. Pass `project.revision` back as `expectedRevision` on subsequent writes for conflict-safe edits.",
|
|
190
|
+
"Read a project's full JSON. Returns { path, project, clipStates }. `clipStates` is a per-clip summary: { clipId, mediaPath, durationMs, transcribed, wordCount, audioCleaned, cleanedAudioPath? } — use it to decide whether to call transcript_transcribe or audio_clean before editing. Pass `project.revision` back as `expectedRevision` on subsequent writes for conflict-safe edits.",
|
|
193
191
|
inputSchema: {
|
|
194
192
|
type: "object",
|
|
195
193
|
properties: {
|
|
@@ -219,7 +217,8 @@ const TOOLS = [
|
|
|
219
217
|
},
|
|
220
218
|
{
|
|
221
219
|
name: "project_open",
|
|
222
|
-
description:
|
|
220
|
+
description:
|
|
221
|
+
"Open the desktop editor focused on a specific project (so the user can take over).",
|
|
223
222
|
inputSchema: {
|
|
224
223
|
type: "object",
|
|
225
224
|
properties: {
|
|
@@ -233,13 +232,19 @@ const TOOLS = [
|
|
|
233
232
|
// ── compose: clips, motion graphics, FX, lower-thirds ──────────
|
|
234
233
|
{
|
|
235
234
|
name: "project_add_clip",
|
|
236
|
-
description:
|
|
235
|
+
description:
|
|
236
|
+
"Append (or insert) a video clip into the project's main track. Probes duration via FFmpeg.",
|
|
237
237
|
inputSchema: {
|
|
238
238
|
type: "object",
|
|
239
239
|
properties: {
|
|
240
240
|
id: { type: "string" },
|
|
241
241
|
path: { type: "string" },
|
|
242
242
|
media: { type: "string", description: "Absolute path to video file" },
|
|
243
|
+
atIndex: {
|
|
244
|
+
type: "number",
|
|
245
|
+
description:
|
|
246
|
+
"Insert before this zero-based clip index. Omit to append at the end.",
|
|
247
|
+
},
|
|
243
248
|
expectedRevision: { type: "number" },
|
|
244
249
|
},
|
|
245
250
|
required: ["media"],
|
|
@@ -272,7 +277,10 @@ const TOOLS = [
|
|
|
272
277
|
properties: {
|
|
273
278
|
id: { type: "string" },
|
|
274
279
|
path: { type: "string" },
|
|
275
|
-
fxId: {
|
|
280
|
+
fxId: {
|
|
281
|
+
type: "string",
|
|
282
|
+
description: "Bundled FX id (e.g. film-burn). Use asset_list_fx to discover.",
|
|
283
|
+
},
|
|
276
284
|
src: { type: "string", description: "Direct path/URL (alternative to fxId)" },
|
|
277
285
|
atMs: { type: "number" },
|
|
278
286
|
durationMs: { type: "number", description: "Default 2500ms for bundled FX" },
|
|
@@ -296,9 +304,21 @@ const TOOLS = [
|
|
|
296
304
|
durationMs: { type: "number", description: "Default 5000ms" },
|
|
297
305
|
designType: {
|
|
298
306
|
type: "string",
|
|
299
|
-
description:
|
|
307
|
+
description:
|
|
308
|
+
"name-bar | slash-reveal | center-stack | minimal-underline | box-reveal | corner-brackets | border-frame | split-horizontal",
|
|
309
|
+
},
|
|
310
|
+
accentColor: { type: "string", description: "Hex accent/background color" },
|
|
311
|
+
textColor: { type: "string", description: "Hex text color. Default #ffffff" },
|
|
312
|
+
backgroundColor: {
|
|
313
|
+
type: "string",
|
|
314
|
+
description: "Hex background fill. Default matches accentColor",
|
|
300
315
|
},
|
|
301
|
-
|
|
316
|
+
backgroundRadius: {
|
|
317
|
+
type: "number",
|
|
318
|
+
description: "Corner radius in pixels. Default 8",
|
|
319
|
+
},
|
|
320
|
+
fontSize: { type: "number", description: "Primary text font size in px. Default 28" },
|
|
321
|
+
fontFamily: { type: "string", description: "CSS font family name. Default Inter" },
|
|
302
322
|
expectedRevision: { type: "number" },
|
|
303
323
|
},
|
|
304
324
|
required: ["content", "atMs"],
|
|
@@ -309,7 +329,8 @@ const TOOLS = [
|
|
|
309
329
|
// ── compose: visual regions ─────────────────────────────────────
|
|
310
330
|
{
|
|
311
331
|
name: "project_add_zoom",
|
|
312
|
-
description:
|
|
332
|
+
description:
|
|
333
|
+
"Add a zoom region — highlight a UI moment. Typical pattern: find 'click X' in transcript, drop zoom there.",
|
|
313
334
|
inputSchema: {
|
|
314
335
|
type: "object",
|
|
315
336
|
properties: {
|
|
@@ -360,6 +381,309 @@ const TOOLS = [
|
|
|
360
381
|
command: "project.add-speed",
|
|
361
382
|
},
|
|
362
383
|
|
|
384
|
+
{
|
|
385
|
+
name: "project_add_annotation",
|
|
386
|
+
description:
|
|
387
|
+
"Drop a text or figure annotation overlay on the canvas for a given time range. Position and size are in percent (0-100) of the canvas dimensions.",
|
|
388
|
+
inputSchema: {
|
|
389
|
+
type: "object",
|
|
390
|
+
properties: {
|
|
391
|
+
id: { type: "string" },
|
|
392
|
+
path: { type: "string" },
|
|
393
|
+
startMs: { type: "number" },
|
|
394
|
+
endMs: { type: "number" },
|
|
395
|
+
type: {
|
|
396
|
+
type: "string",
|
|
397
|
+
description: "text | figure",
|
|
398
|
+
},
|
|
399
|
+
text: { type: "string", description: "Required when type is 'text'" },
|
|
400
|
+
x: { type: "number", description: "Horizontal center, percent 0-100. Default 50." },
|
|
401
|
+
y: { type: "number", description: "Vertical center, percent 0-100. Default 50." },
|
|
402
|
+
width: { type: "number", description: "Width, percent 0-100. Default 30." },
|
|
403
|
+
height: { type: "number", description: "Height, percent 0-100. Default 20." },
|
|
404
|
+
expectedRevision: { type: "number" },
|
|
405
|
+
},
|
|
406
|
+
required: ["startMs", "endMs", "type"],
|
|
407
|
+
},
|
|
408
|
+
command: "project.add-annotation",
|
|
409
|
+
},
|
|
410
|
+
|
|
411
|
+
// ── project lifecycle (extended) ───────────────────────────────
|
|
412
|
+
{
|
|
413
|
+
name: "project_save",
|
|
414
|
+
description:
|
|
415
|
+
"Overwrite a project with the full JSON you supply. Use this when you've built or mutated a project object client-side and want to persist it. Always pass `expectedRevision` (the revision you read) — the server will reject the write if a concurrent edit happened between your read and save, returning { ok: false, details: { code: 'revision_conflict' } }.",
|
|
416
|
+
inputSchema: {
|
|
417
|
+
type: "object",
|
|
418
|
+
properties: {
|
|
419
|
+
id: { type: "string" },
|
|
420
|
+
path: { type: "string" },
|
|
421
|
+
project: { type: "object", description: "Full project JSON to persist" },
|
|
422
|
+
expectedRevision: {
|
|
423
|
+
type: "number",
|
|
424
|
+
description: "Revision from your last project_read. Strongly recommended.",
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
required: ["project"],
|
|
428
|
+
},
|
|
429
|
+
command: "project.save",
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
name: "project_delete",
|
|
433
|
+
description:
|
|
434
|
+
"Permanently delete a project file from disk. There is no trash — this is irreversible. Use project_list to confirm the id before deleting.",
|
|
435
|
+
inputSchema: {
|
|
436
|
+
type: "object",
|
|
437
|
+
properties: {
|
|
438
|
+
id: { type: "string" },
|
|
439
|
+
path: { type: "string" },
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
command: "project.delete",
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
name: "project_remove_clip",
|
|
446
|
+
description:
|
|
447
|
+
"Remove a clip from the main track by its clip id. Use project_read → clipStates to find clip ids. All regions whose time falls within the removed clip are also removed.",
|
|
448
|
+
inputSchema: {
|
|
449
|
+
type: "object",
|
|
450
|
+
properties: {
|
|
451
|
+
id: { type: "string" },
|
|
452
|
+
path: { type: "string" },
|
|
453
|
+
clipId: {
|
|
454
|
+
type: "string",
|
|
455
|
+
description: "Clip id to remove (e.g. clip-1). From clipStates in project_read.",
|
|
456
|
+
},
|
|
457
|
+
expectedRevision: { type: "number" },
|
|
458
|
+
},
|
|
459
|
+
required: ["clipId"],
|
|
460
|
+
},
|
|
461
|
+
command: "project.remove-clip",
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
name: "project_split_clip",
|
|
465
|
+
description:
|
|
466
|
+
"Split a clip in two at a position in source time (milliseconds from the start of that clip). Produces two clips: the portion before the split point and the portion after. Useful for inserting a motion graphic between two halves of a recording.",
|
|
467
|
+
inputSchema: {
|
|
468
|
+
type: "object",
|
|
469
|
+
properties: {
|
|
470
|
+
id: { type: "string" },
|
|
471
|
+
path: { type: "string" },
|
|
472
|
+
clipId: { type: "string", description: "Clip to split" },
|
|
473
|
+
atSourceMs: {
|
|
474
|
+
type: "number",
|
|
475
|
+
description: "Split position in milliseconds from the clip's own start (source time)",
|
|
476
|
+
},
|
|
477
|
+
expectedRevision: { type: "number" },
|
|
478
|
+
},
|
|
479
|
+
required: ["clipId", "atSourceMs"],
|
|
480
|
+
},
|
|
481
|
+
command: "project.split-clip",
|
|
482
|
+
},
|
|
483
|
+
|
|
484
|
+
// ── compose: edit existing regions ─────────────────────────────
|
|
485
|
+
{
|
|
486
|
+
name: "project_remove_region",
|
|
487
|
+
description:
|
|
488
|
+
"Delete an existing region (zoom, trim, speed, annotation, fx, lower-third, overlay) by its UUID. Use project_read to find region IDs. Returns the updated project.",
|
|
489
|
+
inputSchema: {
|
|
490
|
+
type: "object",
|
|
491
|
+
properties: {
|
|
492
|
+
id: { type: "string" },
|
|
493
|
+
path: { type: "string" },
|
|
494
|
+
regionType: {
|
|
495
|
+
type: "string",
|
|
496
|
+
description: "zoom | trim | speed | annotation | fx | lower-third | overlay",
|
|
497
|
+
},
|
|
498
|
+
regionId: { type: "string", description: "UUID of the region to delete" },
|
|
499
|
+
expectedRevision: { type: "number" },
|
|
500
|
+
},
|
|
501
|
+
required: ["regionType", "regionId"],
|
|
502
|
+
},
|
|
503
|
+
command: "project.remove-region",
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
name: "project_update_region",
|
|
507
|
+
description:
|
|
508
|
+
"Patch fields on an existing region without replacing it. Accepts any subset of the region's own properties — only the supplied fields are changed, everything else is preserved. Works on zoom (depth, focusX, focusY), trim (startMs, endMs), speed (startMs, endMs, speed), lower-third (content, subtitle, atMs, durationMs, designType, accentColor, textColor, backgroundColor, backgroundRadius, fontSize, fontFamily), fx (atMs, durationMs), annotation, and overlay regions.",
|
|
509
|
+
inputSchema: {
|
|
510
|
+
type: "object",
|
|
511
|
+
properties: {
|
|
512
|
+
id: { type: "string" },
|
|
513
|
+
path: { type: "string" },
|
|
514
|
+
regionType: {
|
|
515
|
+
type: "string",
|
|
516
|
+
description: "zoom | trim | speed | annotation | fx | lower-third | overlay",
|
|
517
|
+
},
|
|
518
|
+
regionId: { type: "string", description: "UUID of the region to update" },
|
|
519
|
+
patch: {
|
|
520
|
+
type: "object",
|
|
521
|
+
description:
|
|
522
|
+
"Fields to update (partial). E.g. { depth: 4 } to change zoom depth, or { content: 'New Name' } on a lower-third.",
|
|
523
|
+
},
|
|
524
|
+
expectedRevision: { type: "number" },
|
|
525
|
+
},
|
|
526
|
+
required: ["regionType", "regionId", "patch"],
|
|
527
|
+
},
|
|
528
|
+
command: "project.update-region",
|
|
529
|
+
},
|
|
530
|
+
|
|
531
|
+
// ── compose: webcam + crop + export settings ────────────────────
|
|
532
|
+
{
|
|
533
|
+
name: "project_set_webcam_layout",
|
|
534
|
+
description:
|
|
535
|
+
"Set the webcam layout preset and optional position/scale for the current project. Use 'none' to hide the webcam overlay entirely.",
|
|
536
|
+
inputSchema: {
|
|
537
|
+
type: "object",
|
|
538
|
+
properties: {
|
|
539
|
+
id: { type: "string" },
|
|
540
|
+
path: { type: "string" },
|
|
541
|
+
preset: {
|
|
542
|
+
type: "string",
|
|
543
|
+
description:
|
|
544
|
+
"picture-in-picture | vertical-stack | side-by-side | none. 'none' hides the webcam.",
|
|
545
|
+
},
|
|
546
|
+
cx: {
|
|
547
|
+
type: "number",
|
|
548
|
+
description:
|
|
549
|
+
"Horizontal center of the webcam overlay, normalised 0-1. Only for picture-in-picture.",
|
|
550
|
+
},
|
|
551
|
+
cy: {
|
|
552
|
+
type: "number",
|
|
553
|
+
description:
|
|
554
|
+
"Vertical center of the webcam overlay, normalised 0-1. Only for picture-in-picture.",
|
|
555
|
+
},
|
|
556
|
+
scale: {
|
|
557
|
+
type: "number",
|
|
558
|
+
description: "Size multiplier for the webcam overlay. Default 1.0.",
|
|
559
|
+
},
|
|
560
|
+
cropX: { type: "number", description: "Webcam crop left edge, normalised 0-1." },
|
|
561
|
+
cropY: { type: "number", description: "Webcam crop top edge, normalised 0-1." },
|
|
562
|
+
cropWidth: { type: "number", description: "Webcam crop width, normalised 0-1." },
|
|
563
|
+
cropHeight: { type: "number", description: "Webcam crop height, normalised 0-1." },
|
|
564
|
+
expectedRevision: { type: "number" },
|
|
565
|
+
},
|
|
566
|
+
required: ["preset"],
|
|
567
|
+
},
|
|
568
|
+
command: "project.set-webcam-layout",
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
name: "project_set_crop",
|
|
572
|
+
description:
|
|
573
|
+
"Set the main video crop region for the project. All values are normalised 0-1 relative to the source frame dimensions. Default is { x:0, y:0, width:1, height:1 } (no crop).",
|
|
574
|
+
inputSchema: {
|
|
575
|
+
type: "object",
|
|
576
|
+
properties: {
|
|
577
|
+
id: { type: "string" },
|
|
578
|
+
path: { type: "string" },
|
|
579
|
+
x: { type: "number", description: "Left edge, normalised 0-1" },
|
|
580
|
+
y: { type: "number", description: "Top edge, normalised 0-1" },
|
|
581
|
+
width: { type: "number", description: "Crop width, normalised 0-1" },
|
|
582
|
+
height: { type: "number", description: "Crop height, normalised 0-1" },
|
|
583
|
+
expectedRevision: { type: "number" },
|
|
584
|
+
},
|
|
585
|
+
required: ["x", "y", "width", "height"],
|
|
586
|
+
},
|
|
587
|
+
command: "project.set-crop",
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
name: "project_set_export_settings",
|
|
591
|
+
description:
|
|
592
|
+
"Pre-configure the project's export defaults (quality, format, resolution). These are the settings the Export dialog opens with — they don't affect the CLI export_start quality flag.",
|
|
593
|
+
inputSchema: {
|
|
594
|
+
type: "object",
|
|
595
|
+
properties: {
|
|
596
|
+
id: { type: "string" },
|
|
597
|
+
path: { type: "string" },
|
|
598
|
+
quality: {
|
|
599
|
+
type: "string",
|
|
600
|
+
description: "medium | good | source. Controls CRF/bitrate preset.",
|
|
601
|
+
},
|
|
602
|
+
format: {
|
|
603
|
+
type: "string",
|
|
604
|
+
description: "mp4 | webm | gif. Default mp4.",
|
|
605
|
+
},
|
|
606
|
+
resolution: {
|
|
607
|
+
type: "string",
|
|
608
|
+
description:
|
|
609
|
+
"Output resolution: 4k | 1080p | 720p | 480p | source. Default source.",
|
|
610
|
+
},
|
|
611
|
+
gifFrameRate: {
|
|
612
|
+
type: "number",
|
|
613
|
+
description: "GIF frame rate (fps). Only used when format is gif. Default 15.",
|
|
614
|
+
},
|
|
615
|
+
gifSize: {
|
|
616
|
+
type: "string",
|
|
617
|
+
description:
|
|
618
|
+
"GIF size preset: small | medium | large. Only used when format is gif.",
|
|
619
|
+
},
|
|
620
|
+
expectedRevision: { type: "number" },
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
command: "project.set-export-settings",
|
|
624
|
+
},
|
|
625
|
+
|
|
626
|
+
{
|
|
627
|
+
name: "project_set_aspect_ratio",
|
|
628
|
+
description:
|
|
629
|
+
"Change the project's output aspect ratio. Affects the canvas size and how the video is cropped/letterboxed on export. Pick based on the target platform: 16:9 for YouTube/desktop, 9:16 for Shorts/Reels/TikTok, 1:1 for Instagram feed.",
|
|
630
|
+
inputSchema: {
|
|
631
|
+
type: "object",
|
|
632
|
+
properties: {
|
|
633
|
+
id: { type: "string" },
|
|
634
|
+
path: { type: "string" },
|
|
635
|
+
ratio: {
|
|
636
|
+
type: "string",
|
|
637
|
+
description: "16:9 | 9:16 | 1:1 | 4:3 | 3:4",
|
|
638
|
+
},
|
|
639
|
+
expectedRevision: { type: "number" },
|
|
640
|
+
},
|
|
641
|
+
required: ["ratio"],
|
|
642
|
+
},
|
|
643
|
+
command: "project.set-aspect-ratio",
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
name: "project_set_wallpaper",
|
|
647
|
+
description:
|
|
648
|
+
"Set the project's background wallpaper. Pass a bundled wallpaper id (e.g. 'gradient-blue', 'dark-mesh') or 'none' to remove the background. The available wallpaper ids match the filenames in the app's /public/wallpapers/ directory.",
|
|
649
|
+
inputSchema: {
|
|
650
|
+
type: "object",
|
|
651
|
+
properties: {
|
|
652
|
+
id: { type: "string" },
|
|
653
|
+
path: { type: "string" },
|
|
654
|
+
wallpaper: {
|
|
655
|
+
type: "string",
|
|
656
|
+
description: "Bundled wallpaper id or 'none' to remove background",
|
|
657
|
+
},
|
|
658
|
+
expectedRevision: { type: "number" },
|
|
659
|
+
},
|
|
660
|
+
required: ["wallpaper"],
|
|
661
|
+
},
|
|
662
|
+
command: "project.set-wallpaper",
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
name: "project_set_style",
|
|
666
|
+
description:
|
|
667
|
+
"Adjust the project's cinematic framing style: padding around the recording (0-200), drop shadow intensity (0-100), border radius (0-100), motion blur amount (0-100), and whether to show blur behind the recording. Pass only the fields you want to change — others are left as-is.",
|
|
668
|
+
inputSchema: {
|
|
669
|
+
type: "object",
|
|
670
|
+
properties: {
|
|
671
|
+
id: { type: "string" },
|
|
672
|
+
path: { type: "string" },
|
|
673
|
+
padding: { type: "number", description: "Padding around the recording in px. 0-200." },
|
|
674
|
+
shadowIntensity: { type: "number", description: "Drop shadow strength. 0-100." },
|
|
675
|
+
borderRadius: { type: "number", description: "Corner rounding. 0-100." },
|
|
676
|
+
motionBlurAmount: { type: "number", description: "Motion blur strength. 0-100." },
|
|
677
|
+
showBlur: {
|
|
678
|
+
type: "boolean",
|
|
679
|
+
description: "Whether to show a blurred background behind the recording.",
|
|
680
|
+
},
|
|
681
|
+
expectedRevision: { type: "number" },
|
|
682
|
+
},
|
|
683
|
+
},
|
|
684
|
+
command: "project.set-style",
|
|
685
|
+
},
|
|
686
|
+
|
|
363
687
|
// ── transcript-based editing ────────────────────────────────────
|
|
364
688
|
{
|
|
365
689
|
name: "transcript_transcribe",
|
|
@@ -410,7 +734,8 @@ const TOOLS = [
|
|
|
410
734
|
},
|
|
411
735
|
{
|
|
412
736
|
name: "transcript_remove_fillers",
|
|
413
|
-
description:
|
|
737
|
+
description:
|
|
738
|
+
"Auto-detect filler words ('um', 'uh', 'like', 'you know', ...) plus immediately-repeated words and bulk-trim them.",
|
|
414
739
|
inputSchema: {
|
|
415
740
|
type: "object",
|
|
416
741
|
properties: {
|
|
@@ -424,7 +749,8 @@ const TOOLS = [
|
|
|
424
749
|
},
|
|
425
750
|
{
|
|
426
751
|
name: "transcript_search",
|
|
427
|
-
description:
|
|
752
|
+
description:
|
|
753
|
+
"Find every occurrence of a phrase in the merged transcript. Returns matches with their word IDs.",
|
|
428
754
|
inputSchema: {
|
|
429
755
|
type: "object",
|
|
430
756
|
properties: {
|
|
@@ -436,6 +762,41 @@ const TOOLS = [
|
|
|
436
762
|
},
|
|
437
763
|
command: "transcript.search",
|
|
438
764
|
},
|
|
765
|
+
{
|
|
766
|
+
name: "transcript_find_replace",
|
|
767
|
+
description:
|
|
768
|
+
"Find the first occurrence of a phrase in the transcript and replace it with new text. Useful for correcting mis-transcribed names, technical terms, or brand names without re-transcribing. Returns { matched, replacements } — matched is false if the phrase wasn't found.",
|
|
769
|
+
inputSchema: {
|
|
770
|
+
type: "object",
|
|
771
|
+
properties: {
|
|
772
|
+
id: { type: "string" },
|
|
773
|
+
path: { type: "string" },
|
|
774
|
+
find: { type: "string", description: "Phrase to search for (case-insensitive)" },
|
|
775
|
+
replace: { type: "string", description: "Replacement text (replaces the whole phrase)" },
|
|
776
|
+
expectedRevision: { type: "number" },
|
|
777
|
+
},
|
|
778
|
+
required: ["find", "replace"],
|
|
779
|
+
},
|
|
780
|
+
command: "transcript.find-replace",
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
name: "transcript_remove_silences",
|
|
784
|
+
description:
|
|
785
|
+
"Detect and trim silent gaps in the recording — leading silence before the first word, trailing silence after the last word, and inter-word gaps longer than `thresholdMs`. Bulk-creates trim regions. Returns { removed, totalTrimmedMs }.",
|
|
786
|
+
inputSchema: {
|
|
787
|
+
type: "object",
|
|
788
|
+
properties: {
|
|
789
|
+
id: { type: "string" },
|
|
790
|
+
path: { type: "string" },
|
|
791
|
+
thresholdMs: {
|
|
792
|
+
type: "number",
|
|
793
|
+
description: "Gaps longer than this are trimmed. Default 700ms.",
|
|
794
|
+
},
|
|
795
|
+
expectedRevision: { type: "number" },
|
|
796
|
+
},
|
|
797
|
+
},
|
|
798
|
+
command: "transcript.remove-silences",
|
|
799
|
+
},
|
|
439
800
|
|
|
440
801
|
// ── audio ───────────────────────────────────────────────────────
|
|
441
802
|
{
|
|
@@ -471,7 +832,8 @@ const TOOLS = [
|
|
|
471
832
|
},
|
|
472
833
|
{
|
|
473
834
|
name: "caption_set_template",
|
|
474
|
-
description:
|
|
835
|
+
description:
|
|
836
|
+
"Pick a caption template: classic, modern, minimal, bold, spotlight, boxed, neon, or colored.",
|
|
475
837
|
inputSchema: {
|
|
476
838
|
type: "object",
|
|
477
839
|
properties: {
|
|
@@ -485,16 +847,51 @@ const TOOLS = [
|
|
|
485
847
|
command: "caption.set-template",
|
|
486
848
|
},
|
|
487
849
|
|
|
850
|
+
{
|
|
851
|
+
name: "caption_set_style",
|
|
852
|
+
description:
|
|
853
|
+
"Override specific style properties of the caption overlay without changing the template. All fields are optional — pass only what you want to change. positionY controls vertical placement (0=top, 100=bottom, default 85). fontSize accepts CSS values like '28px' or '1.5em'.",
|
|
854
|
+
inputSchema: {
|
|
855
|
+
type: "object",
|
|
856
|
+
properties: {
|
|
857
|
+
id: { type: "string" },
|
|
858
|
+
path: { type: "string" },
|
|
859
|
+
wordsPerLine: {
|
|
860
|
+
type: "number",
|
|
861
|
+
description: "Max words shown at once. Default varies by template.",
|
|
862
|
+
},
|
|
863
|
+
positionY: { type: "number", description: "Vertical position 0-100. Default 85." },
|
|
864
|
+
color: { type: "string", description: "Caption text color (hex/CSS)" },
|
|
865
|
+
backgroundColor: { type: "string", description: "Caption background color" },
|
|
866
|
+
highlightColor: {
|
|
867
|
+
type: "string",
|
|
868
|
+
description: "Color of the currently-spoken word highlight",
|
|
869
|
+
},
|
|
870
|
+
highlightBackgroundColor: {
|
|
871
|
+
type: "string",
|
|
872
|
+
description: "Background of the currently-spoken word",
|
|
873
|
+
},
|
|
874
|
+
strokeColor: { type: "string", description: "Text stroke/outline color" },
|
|
875
|
+
strokeWidth: { type: "number", description: "Text stroke width in px" },
|
|
876
|
+
fontSize: { type: "string", description: "CSS font-size value, e.g. '28px'" },
|
|
877
|
+
expectedRevision: { type: "number" },
|
|
878
|
+
},
|
|
879
|
+
},
|
|
880
|
+
command: "caption.set-style",
|
|
881
|
+
},
|
|
882
|
+
|
|
488
883
|
// ── motion graphics ─────────────────────────────────────────────
|
|
489
884
|
{
|
|
490
885
|
name: "motion_list",
|
|
491
|
-
description:
|
|
886
|
+
description:
|
|
887
|
+
"List every motion-graphic template (id, name, slots, defaults, aspectRatios). 19 templates ship today.",
|
|
492
888
|
inputSchema: { type: "object", properties: {} },
|
|
493
889
|
command: "motion.list",
|
|
494
890
|
},
|
|
495
891
|
{
|
|
496
892
|
name: "motion_themes",
|
|
497
|
-
description:
|
|
893
|
+
description:
|
|
894
|
+
"List every style-pack theme (color overrides). default, mr-beast, mkbhd, kurzgesagt, veritasium.",
|
|
498
895
|
inputSchema: { type: "object", properties: {} },
|
|
499
896
|
command: "motion.themes",
|
|
500
897
|
},
|
|
@@ -514,6 +911,34 @@ const TOOLS = [
|
|
|
514
911
|
},
|
|
515
912
|
command: "motion.generate",
|
|
516
913
|
},
|
|
914
|
+
{
|
|
915
|
+
name: "motion_render_html",
|
|
916
|
+
description:
|
|
917
|
+
"Render arbitrary HTML/CSS/JS to an MP4. Use this when the bundled motion-graphic templates don't fit the brief — Claude can author full animations and we'll render them. Pass either inline `html` or a `htmlPath`. The HTML loads in a Chromium offscreen window at the requested dimensions; we capture frames for durationMs starting after the first paint. Animations should auto-start on DOMContentLoaded (CSS animations, GSAP, Lottie via CDN, anything Chromium can render). Async — returns { jobId, outputPath }; poll job_wait for the final MP4.",
|
|
918
|
+
inputSchema: {
|
|
919
|
+
type: "object",
|
|
920
|
+
properties: {
|
|
921
|
+
html: {
|
|
922
|
+
type: "string",
|
|
923
|
+
description: "Inline HTML string (mutually exclusive with htmlPath).",
|
|
924
|
+
},
|
|
925
|
+
htmlPath: {
|
|
926
|
+
type: "string",
|
|
927
|
+
description: "Absolute path to .html file (mutually exclusive with html).",
|
|
928
|
+
},
|
|
929
|
+
aspectRatio: {
|
|
930
|
+
type: "string",
|
|
931
|
+
description: "16:9 (default) | 9:16 | 1:1. Ignored if width+height supplied.",
|
|
932
|
+
},
|
|
933
|
+
width: { type: "number" },
|
|
934
|
+
height: { type: "number" },
|
|
935
|
+
durationMs: { type: "number", description: "How long to capture. Default 2500." },
|
|
936
|
+
frameRate: { type: "number", description: "Default 30." },
|
|
937
|
+
outputName: { type: "string", description: "Optional filename stem." },
|
|
938
|
+
},
|
|
939
|
+
},
|
|
940
|
+
command: "motion.render-html",
|
|
941
|
+
},
|
|
517
942
|
|
|
518
943
|
// ── assets ──────────────────────────────────────────────────────
|
|
519
944
|
{
|
|
@@ -524,7 +949,8 @@ const TOOLS = [
|
|
|
524
949
|
},
|
|
525
950
|
{
|
|
526
951
|
name: "asset_list_fx",
|
|
527
|
-
description:
|
|
952
|
+
description:
|
|
953
|
+
"List every bundled FX video overlay (id, title, blendMode, defaults, absolute path).",
|
|
528
954
|
inputSchema: { type: "object", properties: {} },
|
|
529
955
|
command: "asset.list-fx",
|
|
530
956
|
},
|
|
@@ -532,7 +958,8 @@ const TOOLS = [
|
|
|
532
958
|
// ── project-aware LLM ──────────────────────────────────────────
|
|
533
959
|
{
|
|
534
960
|
name: "llm_generate_title",
|
|
535
|
-
description:
|
|
961
|
+
description:
|
|
962
|
+
"Generate a YouTube-ready title from the project's merged transcript using the bundled local LLM.",
|
|
536
963
|
inputSchema: {
|
|
537
964
|
type: "object",
|
|
538
965
|
properties: {
|
|
@@ -580,7 +1007,10 @@ const TOOLS = [
|
|
|
580
1007
|
properties: {
|
|
581
1008
|
id: { type: "string" },
|
|
582
1009
|
path: { type: "string" },
|
|
583
|
-
outputPath: {
|
|
1010
|
+
outputPath: {
|
|
1011
|
+
type: "string",
|
|
1012
|
+
description: "Where to write the MP4. Default: recordings dir",
|
|
1013
|
+
},
|
|
584
1014
|
quality: { type: "string", description: "draft | standard | high | ultra. Default high" },
|
|
585
1015
|
},
|
|
586
1016
|
},
|
|
@@ -635,7 +1065,10 @@ const TOOLS = [
|
|
|
635
1065
|
type: "object",
|
|
636
1066
|
properties: {
|
|
637
1067
|
id: { type: "string", description: "Job id from the async tool's response" },
|
|
638
|
-
timeoutMs: {
|
|
1068
|
+
timeoutMs: {
|
|
1069
|
+
type: "number",
|
|
1070
|
+
description: "Max wait. Default 60_000, hard-capped at 300_000",
|
|
1071
|
+
},
|
|
639
1072
|
},
|
|
640
1073
|
required: ["id"],
|
|
641
1074
|
},
|
|
@@ -674,7 +1107,7 @@ const TOOLS = [
|
|
|
674
1107
|
const server = new Server(
|
|
675
1108
|
{
|
|
676
1109
|
name: "pandastudio",
|
|
677
|
-
version: "1.
|
|
1110
|
+
version: "1.11.0",
|
|
678
1111
|
},
|
|
679
1112
|
{
|
|
680
1113
|
capabilities: {
|
|
@@ -712,7 +1145,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
712
1145
|
dispatchArgs = args.args ?? {};
|
|
713
1146
|
if (typeof command !== "string" || !command.includes(".")) {
|
|
714
1147
|
return {
|
|
715
|
-
content: [
|
|
1148
|
+
content: [
|
|
1149
|
+
{ type: "text", text: "pandastudio_call requires `command` of the form 'verb.noun'" },
|
|
1150
|
+
],
|
|
716
1151
|
isError: true,
|
|
717
1152
|
};
|
|
718
1153
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@writepanda/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.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",
|