@writepanda/mcp 1.34.0 → 1.42.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 +249 -8
- package/package.json +1 -1
package/bin/server.mjs
CHANGED
|
@@ -162,6 +162,37 @@ const TOOLS = [
|
|
|
162
162
|
inputSchema: { type: "object", properties: {} },
|
|
163
163
|
command: "system.list",
|
|
164
164
|
},
|
|
165
|
+
{
|
|
166
|
+
name: "system_get_transcription_language",
|
|
167
|
+
description:
|
|
168
|
+
"Read the active transcription engine selection for the current workspace. Returns { language } where language is one of: 'auto' (Parakeet TDT v3, default — auto-detects English + 25 European languages including Russian/Ukrainian), 'chinese' / 'japanese' / 'korean' / 'hindi' / 'arabic' / 'thai' (each routes to Whisper Large-v3-turbo). Use this when surfacing the active engine to the user or before recommending a switch.",
|
|
169
|
+
inputSchema: { type: "object", properties: {} },
|
|
170
|
+
command: "system.getTranscriptionLanguage",
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: "system_set_transcription_language",
|
|
174
|
+
description:
|
|
175
|
+
"Switch the transcription engine for the current workspace. Default 'auto' uses Parakeet (English + 25 European languages, auto-detected, word-level timestamps native). Non-'auto' values route to Whisper Large-v3-turbo with that language locked in. **Important**: non-'auto' choices require a one-time ~1.1 GB Whisper model download — call `system_is_whisper_model_downloaded` first, and surface the download requirement to the user before switching. Use this when the user explicitly asks for a non-European transcription language (e.g., 'transcribe this in Chinese'), or when project content clearly indicates a different language than the current setting.",
|
|
176
|
+
inputSchema: {
|
|
177
|
+
type: "object",
|
|
178
|
+
properties: {
|
|
179
|
+
language: {
|
|
180
|
+
type: "string",
|
|
181
|
+
enum: ["auto", "chinese", "japanese", "korean", "hindi", "arabic", "thai"],
|
|
182
|
+
description: "Target language. 'auto' = Parakeet (default); the others route to Whisper.",
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
required: ["language"],
|
|
186
|
+
},
|
|
187
|
+
command: "system.setTranscriptionLanguage",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "system_is_whisper_model_downloaded",
|
|
191
|
+
description:
|
|
192
|
+
"Check whether the Whisper Large-v3-turbo model has been downloaded yet. Returns { downloaded: boolean }. ALWAYS call this before `system_set_transcription_language` to a non-'auto' value — Whisper transcription fails with a structured error if the model isn't on disk, and surprising the user with a 1.1 GB download mid-flow is bad UX.",
|
|
193
|
+
inputSchema: { type: "object", properties: {} },
|
|
194
|
+
command: "system.isWhisperModelDownloaded",
|
|
195
|
+
},
|
|
165
196
|
|
|
166
197
|
// ── workspaces (v1.19) ──────────────────────────────────────────
|
|
167
198
|
// Multi-workspace isolation: each workspace has its own projects,
|
|
@@ -400,6 +431,21 @@ const TOOLS = [
|
|
|
400
431
|
},
|
|
401
432
|
command: "project.setFolder",
|
|
402
433
|
},
|
|
434
|
+
{
|
|
435
|
+
name: "project_rename",
|
|
436
|
+
description:
|
|
437
|
+
"Rename a project (the display name shown in the editor title bar and the Home grid). Identify the project by `id` (preferred) or `path`. Pass the new `name` (trimmed; must be non-empty). The on-disk filename is NOT changed (files are keyed by project id), so this is safe to call while the project is open. Returns { id, path, name }. Use when the user asks to rename, retitle, or relabel a project.",
|
|
438
|
+
inputSchema: {
|
|
439
|
+
type: "object",
|
|
440
|
+
properties: {
|
|
441
|
+
id: { type: "string", description: "Project UUID (preferred)." },
|
|
442
|
+
path: { type: "string", description: "Absolute project file path." },
|
|
443
|
+
name: { type: "string", description: "New display name. Trimmed; must be non-empty." },
|
|
444
|
+
},
|
|
445
|
+
required: ["name"],
|
|
446
|
+
},
|
|
447
|
+
command: "project.rename",
|
|
448
|
+
},
|
|
403
449
|
{
|
|
404
450
|
name: "project_locate",
|
|
405
451
|
description:
|
|
@@ -436,7 +482,7 @@ const TOOLS = [
|
|
|
436
482
|
{
|
|
437
483
|
name: "project_read",
|
|
438
484
|
description:
|
|
439
|
-
"Read a project's full JSON. Returns { path, project, clipStates, workspaceId, workspaceName, isInActiveWorkspace }. `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. **Workspace fields:** every read includes the project's owning workspace; if `isInActiveWorkspace` is false, the YouTube account and Replicate key in scope are NOT the ones that own this project — surface the mismatch to the user before any export or publish. Pass `project.revision` back as `expectedRevision` on subsequent writes for conflict-safe edits. **Performance tip:** pass `includeTranscript: false` after your first read to drop the per-clip transcript words from the response — they're typically 600+ KB on a 5-minute recording and most agent flows don't need them after pacing is done. clipStates always tells you the transcribed/wordCount status either way.",
|
|
485
|
+
"Read a project's full JSON. Returns { path, project, clipStates, workspaceId, workspaceName, isInActiveWorkspace }. `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. **Workspace fields:** every read includes the project's owning workspace; if `isInActiveWorkspace` is false, the YouTube account and Replicate key in scope are NOT the ones that own this project — surface the mismatch to the user before any export or publish. Pass `project.revision` back as `expectedRevision` on subsequent writes for conflict-safe edits. **Performance tip:** pass `includeTranscript: false` after your first read to drop the per-clip transcript words from the response — they're typically 600+ KB on a 5-minute recording and most agent flows don't need them after pacing is done. clipStates always tells you the transcribed/wordCount status either way. Clips at project.mainTrack.clips[], overlays at project.editor.mediaOverlayRegions[]. Response includes editedDurationMs, sourceDurationMs, totalTrimmedMs, trimCount.",
|
|
440
486
|
inputSchema: {
|
|
441
487
|
type: "object",
|
|
442
488
|
properties: {
|
|
@@ -507,16 +553,21 @@ const TOOLS = [
|
|
|
507
553
|
{
|
|
508
554
|
name: "project_add_motion_graphic",
|
|
509
555
|
description:
|
|
510
|
-
"Drop a motion-graphic MP4 onto the timeline.
|
|
556
|
+
"Drop a motion-graphic MP4/WebM onto the timeline. PREFERRED: pass `fromJob` (the jobId from motion_render_html / motion_generate / motion_concat) — the server resolves the outputPath internally, so you never handle the path string at all. Fallback: pass `file` (an absolute path) only for external/uploaded files. Optional SFX plays when it appears.",
|
|
511
557
|
inputSchema: {
|
|
512
558
|
type: "object",
|
|
513
559
|
properties: {
|
|
514
560
|
id: { type: "string" },
|
|
515
561
|
path: { type: "string" },
|
|
562
|
+
fromJob: {
|
|
563
|
+
type: "string",
|
|
564
|
+
description:
|
|
565
|
+
"PREFERRED for render outputs. The jobId returned by motion_render_html / motion_generate / motion_concat. The server reads the outputPath the job produced — you never construct or pass a path, eliminating the path-truncation failure mode entirely. The job must have succeeded (call job_wait first). Mutually exclusive with `file`.",
|
|
566
|
+
},
|
|
516
567
|
file: {
|
|
517
568
|
type: "string",
|
|
518
569
|
description:
|
|
519
|
-
"Absolute path to
|
|
570
|
+
"Absolute path to an external MP4/WebM (e.g. a user upload). For RENDER outputs use `fromJobId` instead. If you must use `file`, pass the exact `outputPath` from a render job — never construct paths manually (they get truncated at the space in `Application Support` and the overlay silently fails). One of `fromJobId` or `file` is required.",
|
|
520
571
|
},
|
|
521
572
|
durationMs: { type: "number" },
|
|
522
573
|
atMs: { type: "number", description: "Default: end of timeline" },
|
|
@@ -551,10 +602,71 @@ const TOOLS = [
|
|
|
551
602
|
},
|
|
552
603
|
expectedRevision: { type: "number" },
|
|
553
604
|
},
|
|
554
|
-
required: ["
|
|
605
|
+
required: ["durationMs"],
|
|
555
606
|
},
|
|
556
607
|
command: "project.add-motion-graphic",
|
|
557
608
|
},
|
|
609
|
+
{
|
|
610
|
+
name: "project_add_designed_segment",
|
|
611
|
+
description:
|
|
612
|
+
"Create a 'designed segment' — the YouTube split where the host stays LIVE on one half and a motion-graphic panel fills the other (host right / panel left, like a Vox or MKBHD explainer beat). ONE atomic call places both pieces over the same time range with the same transition and source anchor, so they can't drift or mismatch: the camera is repositioned + cover-cropped into its half (a full-bleed split clip-transform) and a full-frame transparent panel graphic is composited on top. Stays in the live compositor — camera is full-quality and scrubbable, nothing baked. IMPORTANT: author the panel graphic as a FULL-FRAME (1920×1080) TRANSPARENT render via motion_render_html --transparent — paint the opaque panel + content on the panel side, leave the camera's half fully transparent (it reveals the repositioned host). See SKILL.md 'Designed segments'.",
|
|
613
|
+
inputSchema: {
|
|
614
|
+
type: "object",
|
|
615
|
+
properties: {
|
|
616
|
+
id: { type: "string" },
|
|
617
|
+
path: { type: "string" },
|
|
618
|
+
fromJob: {
|
|
619
|
+
type: "string",
|
|
620
|
+
description:
|
|
621
|
+
"PREFERRED. The jobId from the transparent motion_render_html job (the full-frame panel graphic with the camera half transparent). Server resolves the outputPath internally — no shell, no path truncation. Mutually exclusive with `file`.",
|
|
622
|
+
},
|
|
623
|
+
file: {
|
|
624
|
+
type: "string",
|
|
625
|
+
description:
|
|
626
|
+
"Absolute path to the transparent panel graphic (WebM/MOV with alpha). Prefer `fromJob`; if used, pass a render job's exact outputPath — never hand-construct (truncates at the space in 'Application Support').",
|
|
627
|
+
},
|
|
628
|
+
durationMs: { type: "number" },
|
|
629
|
+
atMs: { type: "number", description: "Default: end of timeline" },
|
|
630
|
+
cameraSide: {
|
|
631
|
+
type: "string",
|
|
632
|
+
enum: ["left", "right"],
|
|
633
|
+
description:
|
|
634
|
+
"Which half the CAMERA fills; the panel graphic fills the opposite half. 'right' is the canonical reference (host right, panel left).",
|
|
635
|
+
},
|
|
636
|
+
cameraRatio: {
|
|
637
|
+
type: "number",
|
|
638
|
+
enum: [50, 55],
|
|
639
|
+
description:
|
|
640
|
+
"Camera's share of the frame width: 55 (default, the reference split) or 50 (balanced). Panel gets the remaining 45 or 50. Author the panel's opaque width to match (e.g. left 45% for cameraSide=right, cameraRatio=55).",
|
|
641
|
+
},
|
|
642
|
+
transitionMs: {
|
|
643
|
+
type: "number",
|
|
644
|
+
description: "Slide-in/out window at each edge in ms. Default 320.",
|
|
645
|
+
},
|
|
646
|
+
soundUrl: {
|
|
647
|
+
type: "string",
|
|
648
|
+
description:
|
|
649
|
+
"Optional SFX when the segment appears. Bundled id (bundled:sound/<id>), absolute path, or file:// URL. Use asset_list_sounds to discover.",
|
|
650
|
+
},
|
|
651
|
+
soundVolume: {
|
|
652
|
+
type: "number",
|
|
653
|
+
description: "Sound volume 0–1. Default 1 when soundUrl is set.",
|
|
654
|
+
},
|
|
655
|
+
anchorSourceMs: {
|
|
656
|
+
type: "number",
|
|
657
|
+
description:
|
|
658
|
+
"Anchor BOTH regions to a SOURCE-time moment (transcript word startMs). Pass when atMs was derived from a transcript word so the whole segment stays glued across later transcript edits. Omit for free-floating placement.",
|
|
659
|
+
},
|
|
660
|
+
anchorSourceEndMs: {
|
|
661
|
+
type: "number",
|
|
662
|
+
description: "Optional anchor end (source ms) for ranged anchoring.",
|
|
663
|
+
},
|
|
664
|
+
expectedRevision: { type: "number" },
|
|
665
|
+
},
|
|
666
|
+
required: ["durationMs", "cameraSide"],
|
|
667
|
+
},
|
|
668
|
+
command: "project.add-designed-segment",
|
|
669
|
+
},
|
|
558
670
|
{
|
|
559
671
|
name: "project_set_overlay_backdrop_blur",
|
|
560
672
|
description:
|
|
@@ -985,6 +1097,25 @@ const TOOLS = [
|
|
|
985
1097
|
},
|
|
986
1098
|
command: "project.remove-region",
|
|
987
1099
|
},
|
|
1100
|
+
{
|
|
1101
|
+
name: "project_clear_edits",
|
|
1102
|
+
description:
|
|
1103
|
+
"Reset the project to a clean editing slate in ONE atomic call. Removes every time-based region (trim, speed, zoom, motion graphic, lower third, FX, annotation, clip-transform), removes all audio overlays, and turns captions off (keeping the user's chosen caption template + style overrides so re-enabling later restores their look). Clips, transcripts, cleaned audio, and the project aspect ratio are NOT touched. Use this when the user says 'start over', 'clear all edits', or 'reset the timeline' — instead of looping project_remove_region (which N-tuples revisions and failure points). Pass full=true to also clear per-clip LUTs + crops + webcam layout + wallpaper for a true blank canvas. Returns a `cleared` summary listing how many of each kind were removed.",
|
|
1104
|
+
inputSchema: {
|
|
1105
|
+
type: "object",
|
|
1106
|
+
properties: {
|
|
1107
|
+
id: { type: "string" },
|
|
1108
|
+
path: { type: "string" },
|
|
1109
|
+
full: {
|
|
1110
|
+
type: "boolean",
|
|
1111
|
+
description:
|
|
1112
|
+
"Also reset project-level visual config (per-clip LUT presets, LUT intensity, screen crop, webcam crop, webcam layout, wallpaper). Default false — only time-based edits + captions.enabled are cleared. Aspect ratio is NEVER reset.",
|
|
1113
|
+
},
|
|
1114
|
+
expectedRevision: { type: "number" },
|
|
1115
|
+
},
|
|
1116
|
+
},
|
|
1117
|
+
command: "project.clear-edits",
|
|
1118
|
+
},
|
|
988
1119
|
{
|
|
989
1120
|
name: "project_update_region",
|
|
990
1121
|
description:
|
|
@@ -1342,6 +1473,25 @@ const TOOLS = [
|
|
|
1342
1473
|
},
|
|
1343
1474
|
command: "transcript.search",
|
|
1344
1475
|
},
|
|
1476
|
+
{
|
|
1477
|
+
name: "transcript_find_issues",
|
|
1478
|
+
description:
|
|
1479
|
+
"Scan the transcript for editorial problems the speaker created and recovered from: **duplicate-take** (a line re-recorded later — keep the cleaner second take, cut the first), **false-start** (an abandoned fragment before a restart), and **adjacent-repeat** (a stutter like 'the the'). Returns candidate `issues`, each with `type`, `severity`, the `wordIds` of the DISCARDED attempt (pass straight to transcript.delete-words), the edited-time span, the offending `text`, and a `note` explaining what's kept. CONSERVATIVE and read-only — it never edits; review each candidate against context before deleting, since some repeats are intentional. Run this after transcribe + remove-fillers as the content-cleanup pass. Filter with `types` (comma-separated) and cap with `limit`.",
|
|
1480
|
+
inputSchema: {
|
|
1481
|
+
type: "object",
|
|
1482
|
+
properties: {
|
|
1483
|
+
id: { type: "string" },
|
|
1484
|
+
path: { type: "string" },
|
|
1485
|
+
types: {
|
|
1486
|
+
type: "string",
|
|
1487
|
+
description:
|
|
1488
|
+
"Optional comma-separated filter: any of `duplicate-take`, `false-start`, `adjacent-repeat`. Omit for all.",
|
|
1489
|
+
},
|
|
1490
|
+
limit: { type: "number", description: "Optional max number of issues to return." },
|
|
1491
|
+
},
|
|
1492
|
+
},
|
|
1493
|
+
command: "transcript.find-issues",
|
|
1494
|
+
},
|
|
1345
1495
|
{
|
|
1346
1496
|
name: "transcript_find_replace",
|
|
1347
1497
|
description:
|
|
@@ -1413,13 +1563,27 @@ const TOOLS = [
|
|
|
1413
1563
|
{
|
|
1414
1564
|
name: "caption_set_template",
|
|
1415
1565
|
description:
|
|
1416
|
-
"Pick a caption template: classic, modern, minimal, bold, spotlight, boxed, neon, or
|
|
1566
|
+
"Pick a caption template: classic, modern, minimal, bold, spotlight, boxed, neon, colored, texture, or editorial. `texture` fills large uppercase words with a flowing texture mask (lava/marble/metal/wood/concrete/rock) — pick the texture via caption_set_style `texture` (defaults to lava). `editorial` is a magazine-emphasis style: the word being spoken right now renders large (and takes an accent color) while the rest of the line shrinks, so one big word sweeps across the line in time with the speech.",
|
|
1417
1567
|
inputSchema: {
|
|
1418
1568
|
type: "object",
|
|
1419
1569
|
properties: {
|
|
1420
1570
|
id: { type: "string" },
|
|
1421
1571
|
path: { type: "string" },
|
|
1422
|
-
templateId: {
|
|
1572
|
+
templateId: {
|
|
1573
|
+
type: "string",
|
|
1574
|
+
enum: [
|
|
1575
|
+
"classic",
|
|
1576
|
+
"modern",
|
|
1577
|
+
"minimal",
|
|
1578
|
+
"bold",
|
|
1579
|
+
"spotlight",
|
|
1580
|
+
"boxed",
|
|
1581
|
+
"neon",
|
|
1582
|
+
"colored",
|
|
1583
|
+
"texture",
|
|
1584
|
+
"editorial",
|
|
1585
|
+
],
|
|
1586
|
+
},
|
|
1423
1587
|
expectedRevision: { type: "number" },
|
|
1424
1588
|
},
|
|
1425
1589
|
required: ["templateId"],
|
|
@@ -1454,6 +1618,11 @@ const TOOLS = [
|
|
|
1454
1618
|
strokeColor: { type: "string", description: "Text stroke/outline color" },
|
|
1455
1619
|
strokeWidth: { type: "number", description: "Text stroke width in px" },
|
|
1456
1620
|
fontSize: { type: "string", description: "CSS font-size value, e.g. '28px'" },
|
|
1621
|
+
texture: {
|
|
1622
|
+
type: "string",
|
|
1623
|
+
enum: ["lava", "marble", "metal", "wood", "concrete", "rock"],
|
|
1624
|
+
description: "Texture for the 'texture' caption template. Ignored by other templates.",
|
|
1625
|
+
},
|
|
1457
1626
|
expectedRevision: { type: "number" },
|
|
1458
1627
|
},
|
|
1459
1628
|
},
|
|
@@ -1508,7 +1677,49 @@ const TOOLS = [
|
|
|
1508
1677
|
command: "media.generate-image",
|
|
1509
1678
|
},
|
|
1510
1679
|
// `motion_generate` removed in v1.31.0 — see comment above the
|
|
1511
|
-
// motion-graphics section.
|
|
1680
|
+
// motion-graphics section.
|
|
1681
|
+
{
|
|
1682
|
+
name: "motion_list",
|
|
1683
|
+
description:
|
|
1684
|
+
"List the bundled motion-graphic templates an agent can drop onto the timeline. Returns each template's id, name, description, style, tags, aspectRatios, durationMs, `overlay` (true → renders with alpha; sits over the video), and `slots` — the editable fields (key, type: string|color|list, label, default). Call this to discover what's available and exactly which text/colors you can set, then render one with motion_generate. The SKILL.md 'Motion graphics' catalog documents when to use each.",
|
|
1685
|
+
inputSchema: { type: "object", properties: {} },
|
|
1686
|
+
command: "motion.list",
|
|
1687
|
+
},
|
|
1688
|
+
{
|
|
1689
|
+
name: "motion_generate",
|
|
1690
|
+
description:
|
|
1691
|
+
"Render a bundled template by id with your own slot values (text, colors, list items) and a background mode, then add the result to the timeline with project_add_motion_graphic (or project_add_designed_segment for split-panel). Async — returns { jobId, outputPath }; call job_wait, then pass that jobId as `fromJob` to the add tool. Discover templates + slots with motion_list. Prefer this over motion_render_html whenever a bundled template fits the brief — it's faster and already designed.",
|
|
1692
|
+
inputSchema: {
|
|
1693
|
+
type: "object",
|
|
1694
|
+
properties: {
|
|
1695
|
+
templateId: {
|
|
1696
|
+
type: "string",
|
|
1697
|
+
description: "Template id from motion_list (e.g. creator-card, stat-reveal, split-panel).",
|
|
1698
|
+
},
|
|
1699
|
+
slots: {
|
|
1700
|
+
type: "object",
|
|
1701
|
+
description:
|
|
1702
|
+
"Editable values keyed by slot key (see motion_list / the SKILL catalog): text strings, color hex strings, or list arrays. Omitted slots fall back to the template default.",
|
|
1703
|
+
},
|
|
1704
|
+
aspectRatio: {
|
|
1705
|
+
type: "string",
|
|
1706
|
+
enum: ["16:9", "9:16", "1:1"],
|
|
1707
|
+
description: "Defaults to the template's first listed aspect.",
|
|
1708
|
+
},
|
|
1709
|
+
background: {
|
|
1710
|
+
type: "string",
|
|
1711
|
+
enum: ["solid", "transparent", "glass"],
|
|
1712
|
+
description:
|
|
1713
|
+
"'solid' = hard full-frame card. 'transparent' = alpha; camera shows through un-painted pixels. 'glass' = alpha + frosted blur of the camera behind the content (then set backdropBlurStrength on project_add_motion_graphic). Omit to use the template's natural mode — overlay templates (split-panel, lower thirds, grain/3D/shatter/parallax titles) default to transparent, opaque cards to solid. Don't force 'solid' on an overlay template — its see-through half turns black.",
|
|
1714
|
+
},
|
|
1715
|
+
outputName: { type: "string", description: "Optional filename stem (no extension)." },
|
|
1716
|
+
},
|
|
1717
|
+
required: ["templateId", "slots"],
|
|
1718
|
+
},
|
|
1719
|
+
command: "motion.generate",
|
|
1720
|
+
},
|
|
1721
|
+
// Custom HTML authoring path is `motion_render_html` (for briefs no
|
|
1722
|
+
// bundled template fits).
|
|
1512
1723
|
{
|
|
1513
1724
|
name: "motion_render_html",
|
|
1514
1725
|
description:
|
|
@@ -1868,6 +2079,19 @@ const TOOLS = [
|
|
|
1868
2079
|
},
|
|
1869
2080
|
|
|
1870
2081
|
// ── async jobs ─────────────────────────────────────────────────
|
|
2082
|
+
{
|
|
2083
|
+
name: "job_cancel",
|
|
2084
|
+
description:
|
|
2085
|
+
"Cancel a running or queued job. Idempotent — already-terminal jobs are returned as-is.",
|
|
2086
|
+
inputSchema: {
|
|
2087
|
+
type: "object",
|
|
2088
|
+
properties: {
|
|
2089
|
+
id: { type: "string", description: "Job id" },
|
|
2090
|
+
},
|
|
2091
|
+
required: ["id"],
|
|
2092
|
+
},
|
|
2093
|
+
command: "job.cancel",
|
|
2094
|
+
},
|
|
1871
2095
|
{
|
|
1872
2096
|
name: "job_wait",
|
|
1873
2097
|
description:
|
|
@@ -1896,6 +2120,23 @@ const TOOLS = [
|
|
|
1896
2120
|
command: "job.get",
|
|
1897
2121
|
},
|
|
1898
2122
|
|
|
2123
|
+
// ── timeline helpers ────────────────────────────────────────────
|
|
2124
|
+
{
|
|
2125
|
+
name: "timeline_source_to_edited",
|
|
2126
|
+
description:
|
|
2127
|
+
"Convert source-time ms to edited-timeline ms, accounting for trims and speed regions. Returns null if inside a trimmed region.",
|
|
2128
|
+
inputSchema: {
|
|
2129
|
+
type: "object",
|
|
2130
|
+
properties: {
|
|
2131
|
+
id: { type: "string" },
|
|
2132
|
+
path: { type: "string" },
|
|
2133
|
+
sourceMs: { type: "number", description: "Source-time position in ms" },
|
|
2134
|
+
},
|
|
2135
|
+
required: ["sourceMs"],
|
|
2136
|
+
},
|
|
2137
|
+
command: "timeline.source-to-edited",
|
|
2138
|
+
},
|
|
2139
|
+
|
|
1899
2140
|
// ── escape hatch ────────────────────────────────────────────────
|
|
1900
2141
|
{
|
|
1901
2142
|
name: "pandastudio_call",
|
|
@@ -1918,7 +2159,7 @@ const TOOLS = [
|
|
|
1918
2159
|
const server = new Server(
|
|
1919
2160
|
{
|
|
1920
2161
|
name: "pandastudio",
|
|
1921
|
-
version: "1.
|
|
2162
|
+
version: "1.19.0",
|
|
1922
2163
|
},
|
|
1923
2164
|
{
|
|
1924
2165
|
capabilities: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@writepanda/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.42.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",
|