goatedit-mcp 1.1.1 → 1.1.2

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.
@@ -19155,6 +19155,14 @@ var num = (description) => ({ type: "number", description });
19155
19155
  var str = (description) => ({ type: "string", description });
19156
19156
  var bool = (description) => ({ type: "boolean", description });
19157
19157
  var CLIP_ID = str("ID of the clip, as returned by get_timeline.");
19158
+ var SKIP_ADJUSTMENTS = bool(
19159
+ "Render without any adjustment layer (a global camera move, a grade). For frames you will bake into a clip that sits under that layer, which would otherwise apply it twice. The timeline is not changed."
19160
+ );
19161
+ var HIDE_CLIP_IDS = {
19162
+ type: "array",
19163
+ items: { type: "string" },
19164
+ description: "Render as if these clips were not there. The timeline is not changed."
19165
+ };
19158
19166
  var TEXT_STYLE_PROPS = {
19159
19167
  fontSize: num("Font size in pixels, authored against a 1080p-tall frame."),
19160
19168
  fontFamily: str("Font family name."),
@@ -19171,10 +19179,24 @@ var TEXT_STYLE_PROPS = {
19171
19179
  textShadowColor: str("Drop shadow colour."),
19172
19180
  lineHeight: num("Line advance as a multiple of the line height. Defaults to 1.2."),
19173
19181
  letterSpacing: num("Extra tracking in pixels @1080p."),
19182
+ textUnderline: bool("Underline the text."),
19183
+ textShadowBlur: num("Drop shadow softness in pixels @1080p. 0 is a hard shadow."),
19184
+ textShadowOffsetX: num("Drop shadow offset to the right, pixels @1080p."),
19185
+ textShadowOffsetY: num("Drop shadow offset downward, pixels @1080p."),
19186
+ textGradientStart: str("Fill the words with a gradient from this colour (top) \u2026"),
19187
+ textGradientEnd: str("\u2026 to this one (bottom). Set both."),
19188
+ textAccentColor: str("Colour for accent words. Mark an accent word in the text as *word*."),
19189
+ textAccentFontFamily: str(`Font for accent words, e.g. "'Great Vibes', cursive" for a script word inside heavy caps.`),
19190
+ textAccentScale: num("Size of accent words relative to the rest, e.g. 1.8."),
19191
+ textAccentGradientStart: str("Gradient start colour for accent words."),
19192
+ textAccentGradientEnd: str("Gradient end colour for accent words."),
19193
+ textSwooshUnderline: bool("Hand-drawn swoosh under the last line."),
19194
+ textSticker: { type: "string", enum: ["ghost", "arrow", "pen", "star", "flame"], description: "Small sticker drawn beside the accent word." },
19195
+ textLayoutMode: { type: "string", enum: ["side-grid", "luxury-stack", "editorial-hero", "slanted-red", "double-hero"], description: "Editorial layout engine for multi-word titles." },
19174
19196
  posX: num("Horizontal position as a fraction of the frame, 0..1. 0.5 is centre."),
19175
19197
  posY: num("Vertical position as a fraction of the frame, 0..1. 0.5 is centre.")
19176
19198
  };
19177
- var TOOLS = [
19199
+ var BASE_TOOLS = [
19178
19200
  {
19179
19201
  name: "get_session_status",
19180
19202
  description: "Check whether a GoatEdit editor tab is currently attached to this session, and which project it has open. Call this first if any other tool reports the editor is not connected.",
@@ -19237,7 +19259,9 @@ var TOOLS = [
19237
19259
  description: "Write the frame to disk through the local bridge and return its path instead of the image itself. A frame is around 200KB, and inline it is paid for twice \u2014 as tokens and as a cached object \u2014 so a review pass over twenty cuts is expensive. With this set, read the returned path with your own file tools. Falls back to an inline image, with a warning, when the bridge is not running."
19238
19260
  },
19239
19261
  width: num("Width of the returned image in pixels, up to 1280. Defaults to 1024."),
19240
- outlines: bool(`Draw every layer's box, handles and anchor point over the picture, with a timecode \u2014 the look of the editor's selection, on every layer at once. The same option the Export dialog offers as "Show layer outlines".`)
19262
+ outlines: bool(`Draw every layer's box, handles and anchor point over the picture, with a timecode \u2014 the look of the editor's selection, on every layer at once. The same option the Export dialog offers as "Show layer outlines".`),
19263
+ skipAdjustments: SKIP_ADJUSTMENTS,
19264
+ hideClipIds: HIDE_CLIP_IDS
19241
19265
  }
19242
19266
  },
19243
19267
  method: "renderFrame"
@@ -19263,7 +19287,9 @@ var TOOLS = [
19263
19287
  startTime: num("Start of the range in seconds."),
19264
19288
  endTime: num("End of the range in seconds. Ranges over 10 minutes are refused."),
19265
19289
  frames: num("How many frames to sample, up to 6. Defaults to 4."),
19266
- width: num("Width of each frame in pixels. Defaults to 640.")
19290
+ width: num("Width of each frame in pixels. Defaults to 640."),
19291
+ skipAdjustments: SKIP_ADJUSTMENTS,
19292
+ hideClipIds: HIDE_CLIP_IDS
19267
19293
  },
19268
19294
  required: ["startTime", "endTime"]
19269
19295
  },
@@ -19356,7 +19382,7 @@ var TOOLS = [
19356
19382
  },
19357
19383
  {
19358
19384
  name: "get_timeline",
19359
- description: 'Get just the timeline \u2014 all tracks with their clips, ids, start times and durations. Cheaper than get_project_state; use it before your first edit so you have current clip ids. After that, each edit\u2019s reply lists the clips it added, changed and removed under timelineChanges, so a re-read is only needed when that says it was truncated. Animation data (keyframes, motion tracking, masks, per-word caption timings) is reported as counts under *Summary keys rather than in full, so a clip showing keyframeSummary is animated even though the keyframes themselves are not listed. By default the view is lean: a field at its usual value is left out (no volume means 1, no isMuted means false, no opacity means 1), and effects are listed by id and type only. Pass detail: "full" when you need effect parameters.',
19385
+ description: 'Get just the timeline \u2014 all tracks with their clips, ids, start times and durations. Cheaper than get_project_state; use it before your first edit so you have current clip ids. After that, each edit\u2019s reply lists the clips it added, changed and removed under timelineChanges, so a re-read is only needed when that says it was truncated. Animation data (keyframes, motion tracking, masks, per-word caption timings) is reported as counts under *Summary keys rather than in full, so a clip showing keyframeSummary is animated even though the keyframes themselves are not listed; get_clip returns them. By default the view is lean: a field at its usual value is left out (no volume means 1, no isMuted means false, no opacity means 1), and effects are listed by id and type only. Pass detail: "full" when you need effect parameters.',
19360
19386
  inputSchema: {
19361
19387
  type: "object",
19362
19388
  properties: {
@@ -19368,6 +19394,40 @@ var TOOLS = [
19368
19394
  },
19369
19395
  method: "getTimeline"
19370
19396
  },
19397
+ {
19398
+ name: "get_clip",
19399
+ description: "Get clips in full, animation included: every keyframe on every property (time in seconds from the clip\u2019s start, value, ease \u2014 the shape set_keyframes takes), effect parameters and their keyframes, mask shapes, crop, key and transform. Use it to copy or adjust a clip\u2019s animation; get_timeline only counts keyframes. Per-frame data (motion tracking, roto point sets per frame, caption word timings) stays a count, as does any property with more than 300 keyframes.",
19400
+ inputSchema: {
19401
+ type: "object",
19402
+ properties: {
19403
+ clipIds: { type: "array", items: { type: "string" }, description: "Clip ids from get_timeline, up to 50." }
19404
+ },
19405
+ required: ["clipIds"]
19406
+ },
19407
+ method: "getClips"
19408
+ },
19409
+ {
19410
+ name: "create_checkpoint",
19411
+ description: "Save a copy of the whole project (every sequence and the media bin) to return to with restore_checkpoint. Take one before a long scripted change: undo goes back one step at a time and a big rebuild can outrun it. Kept in the editor tab until it reloads, the newest 20 per project.",
19412
+ inputSchema: { type: "object", properties: { name: str("Optional label.") } },
19413
+ method: "createCheckpoint"
19414
+ },
19415
+ {
19416
+ name: "list_checkpoints",
19417
+ description: "List the checkpoints saved in this tab for the open project.",
19418
+ inputSchema: { type: "object", properties: {} },
19419
+ method: "listCheckpoints"
19420
+ },
19421
+ {
19422
+ name: "restore_checkpoint",
19423
+ description: "Put the whole project back as it was at a checkpoint, as one undoable step. Everything done since is undone at once, in every sequence.",
19424
+ inputSchema: {
19425
+ type: "object",
19426
+ properties: { checkpointId: str("Id from create_checkpoint or list_checkpoints.") },
19427
+ required: ["checkpointId"]
19428
+ },
19429
+ method: "restoreCheckpoint"
19430
+ },
19371
19431
  {
19372
19432
  name: "get_media_bin",
19373
19433
  description: "List every media file imported into the project, with id, name, type and duration.",
@@ -19418,7 +19478,7 @@ var TOOLS = [
19418
19478
  inputSchema: {
19419
19479
  type: "object",
19420
19480
  properties: {
19421
- trackId: str('Target track id from get_timeline, or "__new_video__" to let the editor pick a free video track.'),
19481
+ trackId: str('Target track id from get_timeline, "__new_video__" to let the editor pick a free video track, or "__new_track__" for a new video track on top.'),
19422
19482
  mediaFileId: str("Media bin file id from get_media_bin."),
19423
19483
  startTime: num("Where the clip starts on the timeline, in seconds."),
19424
19484
  duration: num("Length of the clip on the timeline, in seconds."),
@@ -19525,6 +19585,152 @@ var TOOLS = [
19525
19585
  },
19526
19586
  method: "addShapeClip"
19527
19587
  },
19588
+ {
19589
+ name: "update_shape_clip",
19590
+ description: "Change a shape after it was made \u2014 fill, size, corners, outline, and a dashed or rounded outline \u2014 without deleting it and losing its id, keyframes and effects.",
19591
+ inputSchema: {
19592
+ type: "object",
19593
+ properties: {
19594
+ clipId: CLIP_ID,
19595
+ color: str("Fill colour."),
19596
+ width: num("Width in design units against a 1080p-tall frame."),
19597
+ height: num("Height in design units against a 1080p-tall frame."),
19598
+ cornerRadius: num("Corner radius in design units."),
19599
+ strokeColor: str("Outline colour."),
19600
+ strokeWidth: num("Outline width in design units."),
19601
+ strokeCap: { type: "string", enum: ["butt", "round", "square"], description: "Ends of an open or dashed outline." },
19602
+ strokeJoin: { type: "string", enum: ["miter", "round", "bevel"], description: "Outline corners." },
19603
+ dash: num("Dash length for a dashed outline, design units. 0 for solid."),
19604
+ gap: num("Gap between dashes, design units.")
19605
+ },
19606
+ required: ["clipId"]
19607
+ },
19608
+ method: "updateShapeClip"
19609
+ },
19610
+ {
19611
+ name: "add_null",
19612
+ description: "Add a Null Object: an invisible handle that other clips are parented to (set_parent). Move, scale or rotate the null and everything under it follows, orbiting its position \u2014 the After Effects way to group a polaroid with its caption, or make a whole layout drift as one.",
19613
+ inputSchema: {
19614
+ type: "object",
19615
+ properties: {
19616
+ startTime: num("Seconds. Defaults to 0."),
19617
+ duration: num("Seconds."),
19618
+ x: num("Position as a fraction of the frame width. Defaults to 0.5."),
19619
+ y: num("Position as a fraction of the frame height. Defaults to 0.5."),
19620
+ name: str("Display name."),
19621
+ trackId: str('Video track, or "__new_video__" (default) for a free one.')
19622
+ },
19623
+ required: ["duration"]
19624
+ },
19625
+ method: "addNull"
19626
+ },
19627
+ {
19628
+ name: "set_parent",
19629
+ description: "Parent a clip to another clip or null, After Effects style. The clip stays exactly where it is when parented; from then on it follows the parent's move, scale and rotation, orbiting the parent's position and spreading as it scales. parentId null unparents. position / scale / rotation pick-whip a single channel to a different clip (null cuts that channel loose): a channel parent passes on only its own change. Parent first, then animate the parent.",
19630
+ inputSchema: {
19631
+ type: "object",
19632
+ properties: {
19633
+ clipId: CLIP_ID,
19634
+ parentId: { type: ["string", "null"], description: "Parent clip id, or null to unparent." },
19635
+ position: { type: ["string", "null"], description: "Take position only from this clip." },
19636
+ scale: { type: ["string", "null"], description: "Take scale only from this clip." },
19637
+ rotation: { type: ["string", "null"], description: "Take rotation only from this clip." }
19638
+ },
19639
+ required: ["clipId"]
19640
+ },
19641
+ method: "setParent"
19642
+ },
19643
+ {
19644
+ name: "group_clips",
19645
+ description: "Group clips under a new null spanning all of them, so they move, scale and rotate as one; each keeps its place. Returns the group's clipId \u2014 animate that. With layout set, the null becomes an Auto Layout frame and the clips flow inside it (tune it with set_auto_layout).",
19646
+ inputSchema: {
19647
+ type: "object",
19648
+ properties: {
19649
+ clipIds: { type: "array", items: { type: "string" }, description: "Clips to group." },
19650
+ name: str("Group name."),
19651
+ layout: { type: "string", enum: ["none", "horizontal", "vertical", "wrap", "orbit"], description: "Defaults to none: a plain group." },
19652
+ gap: num("Gap between laid-out clips, design units. Defaults to 16."),
19653
+ x: num("Group pivot, fraction of the frame width. Defaults to the members' average."),
19654
+ y: num("Group pivot, fraction of the frame height."),
19655
+ trackId: str('Video track for the null, or "__new_video__" (default).')
19656
+ },
19657
+ required: ["clipIds"]
19658
+ },
19659
+ method: "groupClips"
19660
+ },
19661
+ {
19662
+ name: "set_proximity",
19663
+ description: "Drive one property of a clip by how close it is to another clip on screen \u2014 a card that brightens as a cursor nears it, a label that grows when a photo slides over it. Distance is measured between where both are drawn (parents and layout included), as a fraction of the frame width. At `near` or closer the property is nearValue, at `far` or beyond it is farValue, linear between.",
19664
+ inputSchema: {
19665
+ type: "object",
19666
+ properties: {
19667
+ clipId: CLIP_ID,
19668
+ targetClipId: str("The clip whose distance drives it."),
19669
+ property: { type: "string", enum: ["opacity", "scaleX", "scaleY", "posX", "posY", "rotation"], description: "Defaults to opacity." },
19670
+ near: num("Distance for nearValue. Defaults to 0.05."),
19671
+ far: num("Distance for farValue. Defaults to 0.3."),
19672
+ nearValue: num("Defaults to 1."),
19673
+ farValue: num("Defaults to 0."),
19674
+ remove: bool("Remove the driver.")
19675
+ },
19676
+ required: ["clipId"]
19677
+ },
19678
+ method: "setProximity"
19679
+ },
19680
+ {
19681
+ name: "set_clip_speed",
19682
+ description: "Retime a video or audio clip like the Speed panel: speed (0.1\u201310) keeps the source range and changes the clip length (stopping at the next clip); ramp applies a speed curve \u2014 a preset name (montage, hero, bullet, jumpCut, flashIn, flashOut) or [{x 0..1, y rate}] points; null flattens it. reversed plays it backwards; holdFrame freezes from that many seconds into the clip (null releases). Linked audio is retimed with it.",
19683
+ inputSchema: {
19684
+ type: "object",
19685
+ properties: {
19686
+ clipId: CLIP_ID,
19687
+ speed: num("Playback rate, 1 = normal."),
19688
+ ramp: { type: ["string", "array", "null"], description: "Preset id, or points [{x, y}], or null." },
19689
+ reversed: bool("Play backwards."),
19690
+ holdFrame: { type: ["number", "null"], description: "Freeze from this clip-relative time; null removes the hold." }
19691
+ },
19692
+ required: ["clipId"]
19693
+ },
19694
+ method: "setClipSpeed"
19695
+ },
19696
+ {
19697
+ name: "set_auto_layout",
19698
+ description: "Figma-style Auto Layout. On a frame (group_clips with a layout, or any clip with children): mode, gap, padding, alignment, resizing, clip content, reverse, stack order, orbit settings. Children are spaced by the room they take at their own scale, and the frame's own scale and rotation act on the whole arrangement. On a child: absolute (leave the flow and sit at its own position inside the frame), grow (share of free space when resizing is fill), min/max size.",
19699
+ inputSchema: {
19700
+ type: "object",
19701
+ properties: {
19702
+ clipId: CLIP_ID,
19703
+ enabled: bool("Switch the layout on or off without losing it."),
19704
+ mode: { type: "string", enum: ["none", "horizontal", "vertical", "wrap", "orbit"] },
19705
+ gap: num("Gap between children, design units."),
19706
+ gapV: num("Gap between rows in wrap mode."),
19707
+ padding: num("All four paddings at once."),
19708
+ paddingLeft: num("Design units."),
19709
+ paddingRight: num("Design units."),
19710
+ paddingTop: num("Design units."),
19711
+ paddingBottom: num("Design units."),
19712
+ primaryAlign: { type: "string", enum: ["start", "center", "end", "space-between", "space-around", "space-evenly"] },
19713
+ counterAlign: { type: "string", enum: ["start", "center", "end", "stretch"] },
19714
+ horizontalResizing: { type: "string", enum: ["fixed", "hug", "fill"] },
19715
+ verticalResizing: { type: "string", enum: ["fixed", "hug", "fill"] },
19716
+ clipContent: bool("Clip children to the frame."),
19717
+ reverse: bool("Reverse the flow order."),
19718
+ stackOrder: { type: "string", enum: ["firstOnTop", "lastOnTop"] },
19719
+ orbitRadius: num("Orbit mode radius, design units."),
19720
+ orbitSpeed: num("Orbit mode degrees per second."),
19721
+ orbitStartAngle: num("Orbit mode start angle, degrees."),
19722
+ orbitRotateChildren: bool("Turn children to face along the orbit."),
19723
+ absolute: bool("Child only: leave the flow."),
19724
+ grow: num("Child only: share of free space with fill resizing."),
19725
+ minWidth: num("Design units."),
19726
+ maxWidth: num("Design units."),
19727
+ minHeight: num("Design units."),
19728
+ maxHeight: num("Design units.")
19729
+ },
19730
+ required: ["clipId"]
19731
+ },
19732
+ method: "setAutoLayout"
19733
+ },
19528
19734
  {
19529
19735
  name: "set_shape_gradient",
19530
19736
  description: "Give a shape or a pen path a gradient fill or a gradient stroke, replacing its flat colour. Works on anything with a shapeType, including paths drawn with the pen tool. The ramp is laid out against the shape's own bounding box, so it stays put on the object when the shape is moved, scaled or rotated rather than sliding across it. Pass null to clear one and go back to the flat colour. gradientKeyframes animates it \u2014 a sweep that turns, a glow that shifts colour.",
@@ -19695,12 +19901,69 @@ var TOOLS = [
19695
19901
  scaleX: num("Horizontal scale, overriding scale."),
19696
19902
  scaleY: num("Vertical scale, overriding scale."),
19697
19903
  rotation: num("Rotation in degrees."),
19698
- opacity: num("Opacity from 0 to 1.")
19904
+ opacity: num("Opacity from 0 to 1."),
19905
+ blendMode: {
19906
+ type: "string",
19907
+ enum: [
19908
+ "normal",
19909
+ "multiply",
19910
+ "screen",
19911
+ "overlay",
19912
+ "darken",
19913
+ "lighten",
19914
+ "color-dodge",
19915
+ "color-burn",
19916
+ "hard-light",
19917
+ "soft-light",
19918
+ "difference",
19919
+ "exclusion",
19920
+ "hue",
19921
+ "saturation",
19922
+ "color",
19923
+ "luminosity"
19924
+ ],
19925
+ description: "How the clip mixes with the layers beneath it. screen for light leaks, flares and dust shot on black; multiply for paper textures, ink and shadows shot on white; overlay or soft-light for texture over a picture. normal sets it back."
19926
+ }
19699
19927
  },
19700
19928
  required: ["clipId"]
19701
19929
  },
19702
19930
  method: "setClipTransform"
19703
19931
  },
19932
+ {
19933
+ name: "set_ultra_key",
19934
+ description: "Green screen: knock a colour out of a video or image clip so the tracks beneath show through \u2014 a paper unfold, a smoke burst or a presenter shot on green or blue. Unset values keep what the clip already has (or the defaults: green, tolerance 0.18, softness 0.1, spill 0.4). Raise tolerance when green is left behind, softness for a gentler edge, spill when edges carry a green tint. Check the result with render_preview_frame; pass remove to take the key off.",
19935
+ inputSchema: {
19936
+ type: "object",
19937
+ properties: {
19938
+ clipId: CLIP_ID,
19939
+ keyColor: str('Colour to remove, as "#rrggbb". Defaults to "#00ff00"; pick "#0000ff" for blue screen, or the sampled backdrop colour when the green is off-pure.'),
19940
+ tolerance: num("How close to the key colour a pixel must be to vanish, 0..1. Defaults to 0.18."),
19941
+ softness: num("Width of the soft edge around the key, 0..1. Defaults to 0.1."),
19942
+ spill: num("How strongly green fringing is desaturated on edges, 0..1. Defaults to 0.4."),
19943
+ enabled: bool("Switch the key off without losing its settings. Defaults to true."),
19944
+ remove: bool("Remove the key entirely.")
19945
+ },
19946
+ required: ["clipId"]
19947
+ },
19948
+ method: "setUltraKey"
19949
+ },
19950
+ {
19951
+ name: "set_track_matte",
19952
+ description: "After Effects track matte: another clip\u2019s transparency (alpha) or brightness (luma) becomes this clip\u2019s transparency, optionally inverted. Use it to reveal an image through a moving shape \u2014 a torn-paper or ink-bleed clip, a wipe, text \u2014 without baking. The matte clip stops drawing once it is used as a matte unless keepSourceVisible is set. Name the matte with sourceClipId (any clip, on any track; give it the same transform as the clip it cuts); without one, the matte is whatever is live on the nearest video track above. Unset values keep what the clip already has. Pass remove to take the matte off.",
19953
+ inputSchema: {
19954
+ type: "object",
19955
+ properties: {
19956
+ clipId: CLIP_ID,
19957
+ type: { type: "string", enum: ["alpha", "alphaInverted", "luma", "lumaInverted"], description: "Defaults to alpha." },
19958
+ sourceClipId: str("Clip that supplies the matte."),
19959
+ keepSourceVisible: bool("Keep drawing the matte clip as well. Defaults to false."),
19960
+ enabled: bool("Switch the matte off without losing it. Defaults to true."),
19961
+ remove: bool("Remove the matte.")
19962
+ },
19963
+ required: ["clipId"]
19964
+ },
19965
+ method: "setTrackMatte"
19966
+ },
19704
19967
  {
19705
19968
  name: "set_keyframes",
19706
19969
  description: "Animate one property of a clip over time. set_clip_transform sets a value; this is what makes it move \u2014 a card sliding on, a background drifting, a photo pushing in. Times are in seconds RELATIVE TO THE CLIP's start, and each keyframe may carry its own outgoing easing as a CSS cubic-bezier ([0.22, 1, 0.36, 1] is a good decelerate). Two or more keyframes make motion; one holds a value. Writing keyframes for a property overrides whatever flat value the clip carries for it, and replaces any track already on that property \u2014 pass clear to remove one.",
@@ -19798,11 +20061,15 @@ var TOOLS = [
19798
20061
  },
19799
20062
  {
19800
20063
  name: "save_preset",
19801
- description: "Save clips as a reusable preset: their transform keyframes (stored as fractions of the clip, so the move stretches to whatever it is applied to), effects, transition, and text or shape styling. It is kept in this browser and shows under My Presets in the Presets panel, across reloads. One clip makes a preset that can be laid onto another clip; several make a group that is added as new clips.",
20064
+ description: 'Save clips as a reusable preset: their transform keyframes (stored as fractions of the clip, so the move stretches to whatever it is applied to), effects, transition, and text or shape styling. It is kept in this browser and shows under My Presets in the Presets panel, across reloads. One clip makes a preset that can be laid onto another clip; several make a group that is added as new clips. Or pass `preset` instead of clipIds to save a definition directly \u2014 the only way to make a preset with media layers: each layer is a video or image (by URL) sized to the clip the preset is applied to, drawn over it (role "over") or cutting it as its track matte (role "matte"), e.g. a torn-paper overlay plus a luma matte that reveals a photo.',
19802
20065
  inputSchema: {
19803
20066
  type: "object",
19804
20067
  properties: {
19805
20068
  clipIds: { type: "array", items: { type: "string" }, description: "The clips to capture." },
20069
+ preset: {
20070
+ type: "object",
20071
+ description: 'A preset definition instead of clipIds: { name, category?, description?, tags?, id? (custom_\u2026 to replace one), clips?: [], layers: [{ label, role: "over"|"matte", matteType?: "alpha"|"alphaInverted"|"luma"|"lumaInverted", start (s from the clip start), dur?, followLongSide? (quarter-turn to match a portrait clip), copyEffects? (effect types copied from the clip, e.g. ["dropShadow"]), blendMode?, media: { url, name, type: "video"|"image", duration, width, height } }] }.'
20072
+ },
19806
20073
  name: str("Preset name."),
19807
20074
  category: {
19808
20075
  type: "string",
@@ -19811,14 +20078,13 @@ var TOOLS = [
19811
20078
  },
19812
20079
  tags: { type: "array", items: { type: "string" }, description: "Optional search tags." },
19813
20080
  description: str("Optional one-line description.")
19814
- },
19815
- required: ["clipIds", "name"]
20081
+ }
19816
20082
  },
19817
20083
  method: "savePreset"
19818
20084
  },
19819
20085
  {
19820
20086
  name: "apply_preset",
19821
- description: "Apply a preset (saved or built-in; ids from list_presets). With clipId, a one-clip preset is laid onto that clip, keeping its media and place and replacing animation only on the properties the preset sets. Without clipId, the preset\u2019s clips are added as new clips at time (default: the playhead).",
20087
+ description: "Apply a preset (saved or built-in; ids from list_presets). With clipId, a one-clip preset is laid onto that clip, keeping its media and place and replacing animation only on the properties the preset sets. Without clipId, the preset\u2019s clips are added as new clips at time (default: the playhead). A preset with media layers (list_presets says needsClipId) needs clipId: its layers go on new tracks directly above that clip, sized to its box, starting with it.",
19822
20088
  inputSchema: {
19823
20089
  type: "object",
19824
20090
  properties: {
@@ -20043,7 +20309,7 @@ var TOOLS = [
20043
20309
  properties: {
20044
20310
  clipId: CLIP_ID,
20045
20311
  newStartTime: num("New timeline start time in seconds."),
20046
- newTrackId: str('Track to move onto. Defaults to the clip\u2019s current track. Accepts "__new_video__" to pick a video track free across the clip\u2019s span.')
20312
+ newTrackId: str('Track to move onto. Defaults to the clip\u2019s current track. Accepts "__new_video__" to pick a video track free across the clip\u2019s span, or "__new_track__" for a new video track on top.')
20047
20313
  },
20048
20314
  required: ["clipId", "newStartTime"]
20049
20315
  },
@@ -20167,6 +20433,90 @@ var TOOLS = [
20167
20433
  method: "redo"
20168
20434
  }
20169
20435
  ];
20436
+ var SEQUENCE_EDITS = /* @__PURE__ */ new Set([
20437
+ "addClip",
20438
+ "addClips",
20439
+ "addTextClip",
20440
+ "updateTextClip",
20441
+ "addShapeClip",
20442
+ "addAdjustmentLayer",
20443
+ "addTitleCard",
20444
+ "insertRipple",
20445
+ "setClipAudio",
20446
+ "setClipTransform",
20447
+ "setUltraKey",
20448
+ "setTrackMatte",
20449
+ "setShapeGradient",
20450
+ "setMask",
20451
+ "setKeyframes",
20452
+ "setClipEffect",
20453
+ "setTextAnimation",
20454
+ "addHtmlClip",
20455
+ "setColorGrade",
20456
+ "setClipDenoise",
20457
+ "addTransition",
20458
+ "applyAiEdit",
20459
+ "batchEdit",
20460
+ "clearTrack",
20461
+ "nestClips",
20462
+ "addSequenceClip",
20463
+ "removeClip",
20464
+ "moveClip",
20465
+ "trimClip",
20466
+ "splitClip",
20467
+ "rippleDeleteClip",
20468
+ "generateCaptions",
20469
+ "applyPreset",
20470
+ "updateShapeClip",
20471
+ "addNull",
20472
+ "setParent",
20473
+ "groupClips",
20474
+ "setProximity",
20475
+ "setClipSpeed",
20476
+ "setAutoLayout"
20477
+ ]);
20478
+ var EXPECTED_SEQUENCE = str(
20479
+ "Optional guard: the sequence id this edit is meant for (from list_sequences). If the tab has a different sequence open, the call is refused instead of editing the wrong one."
20480
+ );
20481
+ var PLACING_EDITS = /* @__PURE__ */ new Set([
20482
+ "addClip",
20483
+ "addClips",
20484
+ "addTextClip",
20485
+ "addShapeClip",
20486
+ "addAdjustmentLayer",
20487
+ "addHtmlClip",
20488
+ "addSequenceClip",
20489
+ "moveClip",
20490
+ "trimClip",
20491
+ "batchEdit",
20492
+ "generateCaptions",
20493
+ "addNull",
20494
+ "groupClips",
20495
+ "setClipSpeed"
20496
+ ]);
20497
+ var OVERWRITE = {
20498
+ type: "boolean",
20499
+ description: 'Placing, moving or lengthening a clip onto a span of a named track that other clips already hold is refused, naming them, because it would cut or delete them. Pass true to replace them anyway. Use trackId "__new_video__" for a free video track or "__new_track__" for a new one on top instead.'
20500
+ };
20501
+ var NEW_TRACK_ABOVE = str(
20502
+ 'With trackId "__new_track__": put the new track directly above this video track instead of on top of everything \u2014 for a layer that belongs with a clip, below a full-length adjustment layer.'
20503
+ );
20504
+ var NEW_TRACK_BELOW = str('With trackId "__new_track__": put the new track directly below this video track.');
20505
+ var TOOLS = BASE_TOOLS.map((tool) => {
20506
+ if (!tool.method || !SEQUENCE_EDITS.has(tool.method)) return tool;
20507
+ return {
20508
+ ...tool,
20509
+ inputSchema: {
20510
+ ...tool.inputSchema,
20511
+ properties: {
20512
+ ...tool.inputSchema.properties,
20513
+ expectedSequenceId: EXPECTED_SEQUENCE,
20514
+ ...PLACING_EDITS.has(tool.method) ? { overwrite: OVERWRITE } : {},
20515
+ ...PLACING_EDITS.has(tool.method) && tool.method !== "trimClip" ? { aboveTrackId: NEW_TRACK_ABOVE, belowTrackId: NEW_TRACK_BELOW } : {}
20516
+ }
20517
+ }
20518
+ };
20519
+ });
20170
20520
  var TOOLS_BY_NAME = new Map(TOOLS.map((t) => [t.name, t]));
20171
20521
  function editDistance(a, b) {
20172
20522
  const rows = [Array.from({ length: b.length + 1 }, (_, j) => j)];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goatedit-mcp",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "Local MCP server for the GoatEdit video editor: drive your open editor tab from Claude Code or any MCP client",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",