goatedit-mcp 1.1.2 → 1.1.3

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.
@@ -19254,6 +19254,12 @@ var BASE_TOOLS = [
19254
19254
  properties: {
19255
19255
  time: num("Timeline position to render, in seconds."),
19256
19256
  clipId: str("Render this clip\u2019s midpoint instead of a time."),
19257
+ times: {
19258
+ type: "array",
19259
+ items: { type: "number" },
19260
+ description: "Render up to 24 moments as ONE contact-sheet image instead of a single frame: tiles left to right, top to bottom, each stamped with its time and frame number. Use it to review a whole cut, compare scene joins side by side, or check an animation across its length in one call. width is then the sheet width (default 1280, up to 1600)."
19261
+ },
19262
+ columns: num("With times: tiles per row. Defaults to a near-square grid."),
19257
19263
  saveToDisk: {
19258
19264
  type: "boolean",
19259
19265
  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."
@@ -19297,7 +19303,7 @@ var BASE_TOOLS = [
19297
19303
  },
19298
19304
  {
19299
19305
  name: "lint_timeline",
19300
- description: "Check the timeline for problems that leave state looking correct while the result is broken \u2014 most importantly clips that carry sound but sit on a video track, where the mixer never reads them and they are silent in both preview and export. Also reports dangling and out-of-sync clip links, clips reading past the end of their source, clips whose media has left the bin, and overlaps. Call this after any batch of edits; it is the cheapest verification available and needs no rendering.",
19306
+ description: "Check the timeline for problems that leave state looking correct while the result is broken \u2014 most importantly clips that carry sound but sit on a video track, where the mixer never reads them and they are silent in both preview and export. Also reports dangling and out-of-sync clip links, clips reading past the end of their source, clips whose media has left the bin, and overlaps. It also asks every source whether it still loads \u2014 media files, and the pictures inside HTML clips \u2014 and reports the ones that render as nothing or as a broken-image icon (a blob: link from before a reload, a local server that stopped). Call this after any batch of edits; it is the cheapest verification available and needs no rendering.",
19301
19307
  inputSchema: { type: "object", properties: {} },
19302
19308
  method: "lintTimeline"
19303
19309
  },
@@ -19693,6 +19699,101 @@ var BASE_TOOLS = [
19693
19699
  },
19694
19700
  method: "setClipSpeed"
19695
19701
  },
19702
+ {
19703
+ name: "join_scenes",
19704
+ description: "Join two scenes at a cut so the edit flows instead of cutting: push (one camera move across a shared canvas \u2014 the outgoing scene speeds out and the incoming arrives at the same speed, so the cut falls mid-motion), zoomThrough (push into one element of the outgoing scene until it fills the frame; the incoming scene opens out of it), or matchCut (a layer of the incoming scene starts exactly where a layer of the outgoing one was \u2014 position, size, turn, through parents, 3D models included \u2014 then settles into its own place). push and zoomThrough add a camera null per side and parent that side's top-level clips to it, so every layer stays editable and the camera's keyframes can be tuned afterwards. at is the cut: outgoing clips end there and incoming clips start there.",
19705
+ inputSchema: {
19706
+ type: "object",
19707
+ properties: {
19708
+ at: num("The cut time in seconds."),
19709
+ type: { type: "string", enum: ["push", "zoomThrough", "matchCut"], description: "Which join." },
19710
+ duration: num("Seconds the move takes on each side of the cut. Default 0.4."),
19711
+ direction: { type: "string", enum: ["left", "right", "up", "down"], description: "push: which way the picture travels. Default left." },
19712
+ clipId: str("zoomThrough: the element of the outgoing scene to fly into."),
19713
+ zoom: num("zoomThrough: how far in the outgoing camera goes. Default: until the element overfills the frame."),
19714
+ inZoom: num("zoomThrough: scale the incoming scene opens from. Default 1.8."),
19715
+ fromClipId: str("matchCut: the outgoing layer to match."),
19716
+ toClipId: str("matchCut: the incoming layer that takes its place.")
19717
+ },
19718
+ required: ["at", "type"]
19719
+ },
19720
+ method: "joinScenes"
19721
+ },
19722
+ {
19723
+ name: "create_3d_object",
19724
+ description: "Make a word, a logo or an SVG solid: extruded to a depth with rounded edges and lit like a 3D model, as its own clip with nothing around it (no backdrop), so it floats over whatever is beneath. Give text (drawn in font/weight/color) or mediaFileId of a transparent PNG or an SVG in the bin (a photo needs remove_background first). spins turns it whole times over the clip; rotateX/Y/Z pose it. It is a showcase clip: describe_effect showcase lists its looks (chrome, glass\u2026), lighting and side colour, set with set_clip_effect.",
19725
+ inputSchema: {
19726
+ type: "object",
19727
+ properties: {
19728
+ text: str("A word or short line to extrude."),
19729
+ font: str("With text: font family loaded in the editor. Default Inter."),
19730
+ weight: num("With text: font weight. Default 800."),
19731
+ color: str("With text: fill colour, hex. Default #f2661c."),
19732
+ mediaFileId: str("A transparent PNG or SVG in the bin, instead of text."),
19733
+ startTime: num("Start in seconds. Default 0."),
19734
+ duration: num("Length in seconds."),
19735
+ trackId: str("Track to place it on. Default a new video track on top."),
19736
+ depth: num("Extrude depth, percent of its height (0\u201360). Default 12."),
19737
+ bevel: num("Soft edges, 0\u2013100. Default 40."),
19738
+ spins: num("Whole turns over the clip. Default 0."),
19739
+ rotateX: num("Pose, degrees."),
19740
+ rotateY: num("Pose, degrees. Default 28 when not spinning, so the depth shows."),
19741
+ rotateZ: num("Pose, degrees."),
19742
+ size: num("Size in the frame, percent (20\u2013250). Default 92."),
19743
+ name: str("Clip name.")
19744
+ },
19745
+ required: ["duration"]
19746
+ },
19747
+ method: "create3dObject"
19748
+ },
19749
+ {
19750
+ name: "remove_background",
19751
+ description: "Cut the subject out of a still image \u2014 a product, an object, a person \u2014 into a transparent PNG, the way stock photos become floating cut-outs. Runs in the editor tab (the model downloads once, ~45 MB for quality). The cut-out is added to the bin as a new file and, by default, every clip showing the photo switches to it (or only clipId when given); the original stays. For video use roto instead.",
19752
+ inputSchema: {
19753
+ type: "object",
19754
+ properties: {
19755
+ clipId: str("An image clip to cut out; it switches to the cut-out."),
19756
+ mediaFileId: str("A bin image to cut out, instead of a clip."),
19757
+ engine: { type: "string", enum: ["quality", "fast"], description: "quality (default): any subject, sharper edges. fast: people only, tiny download." },
19758
+ applyToClips: bool("Switch clips to the cut-out. Default true.")
19759
+ }
19760
+ },
19761
+ method: "removeBackground"
19762
+ },
19763
+ {
19764
+ name: "set_scene",
19765
+ description: "Mark a stretch of timeline as a named scene (a ruler marker with a length), so retime_range can move or stretch it as one piece later. With start and end it makes a scene; with sceneId it changes one (start, end, label, color) or deletes it (remove: true); with no arguments it lists every scene.",
19766
+ inputSchema: {
19767
+ type: "object",
19768
+ properties: {
19769
+ start: num("Scene start in seconds."),
19770
+ end: num("Scene end in seconds."),
19771
+ label: str('Name, e.g. "P03 Grid".'),
19772
+ color: str("Marker colour, hex."),
19773
+ sceneId: str("An existing scene to change or remove."),
19774
+ remove: bool("With sceneId: delete the scene marker (clips are untouched).")
19775
+ }
19776
+ },
19777
+ method: "setScene"
19778
+ },
19779
+ {
19780
+ name: "retime_range",
19781
+ description: "Move or stretch a whole stretch of timeline in one step \u2014 every clip that lies wholly inside it, on every track. Clips scale in place, every keyframe inside them (transform, text, effects, masks, paths, tracker) scales with them, text animation timings and fades scale, and footage changes speed to cover the same source in the new length \u2014 so squeezing a scene from 1.6s to 1.2s plays all of its animation faster instead of cutting the end off. With ripple (default) everything after the range shifts by the change in its end, closing or opening the gap; scene markers move too. Name the range by scene (a set_scene id or label) or by from/to. Clips crossing an edge are left alone and reported. One undo step.",
19782
+ inputSchema: {
19783
+ type: "object",
19784
+ properties: {
19785
+ scene: str("A scene id or label from set_scene, instead of from/to."),
19786
+ from: num("Range start in seconds."),
19787
+ to: num("Range end in seconds."),
19788
+ duration: num("New length of the range in seconds. Omit to keep it (a plain move)."),
19789
+ start: num("New start of the range in seconds. Omit to keep it in place."),
19790
+ ripple: bool("Shift everything after the range by the change in its end. Default true."),
19791
+ allowOverlap: bool("Proceed even when the moved range lands on clips outside it (they would be overlapped)."),
19792
+ dryRun: bool("Report what would move without changing anything.")
19793
+ }
19794
+ },
19795
+ method: "retimeRange"
19796
+ },
19696
19797
  {
19697
19798
  name: "set_auto_layout",
19698
19799
  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.",
@@ -19890,10 +19991,17 @@ var BASE_TOOLS = [
19890
19991
  },
19891
19992
  {
19892
19993
  name: "set_clip_transform",
19893
- description: "Position, scale, rotate or fade a clip in the frame. Positions are fractions of the frame, 0..1, with 0.5 the centre \u2014 so a top-left watermark is roughly x 0.12, y 0.1.",
19994
+ description: `Position, scale, rotate or fade a clip in the frame. Positions are fractions of the frame, 0..1, with 0.5 the centre \u2014 so a top-left watermark is roughly x 0.12, y 0.1. A parented clip's own values are measured from its parent's pose, not the screen: to put it somewhere exact, pass space: "screen", or matchClipId to sit it on another clip; animated properties keep their animation and move as a whole.`,
19894
19995
  inputSchema: {
19895
19996
  type: "object",
19896
19997
  properties: {
19998
+ matchClipId: str("Put this clip where another clip appears on screen \u2014 through any parents on either side. With match choose what to copy; the default is position and size (fitted inside, keeping proportions)."),
19999
+ match: { type: "array", items: { type: "string", enum: ["position", "size", "rotation"] }, description: 'With matchClipId: what to copy. Defaults to ["position", "size"].' },
20000
+ space: { type: "string", enum: ["own", "screen"], description: "screen: x and y are where the picture's centre should appear (fractions of the frame), width/height its on-screen size in pixels, rotation as seen \u2014 solved through parents, so a parented clip lands exactly there. Default own: the clip's own values." },
20001
+ width: num("With space screen: on-screen width in pixels."),
20002
+ height: num("With space screen: on-screen height in pixels."),
20003
+ stretch: bool("With a size target: scale X and Y separately to fill it, instead of fitting inside."),
20004
+ time: num("With matchClipId or space screen: the moment to measure at, in timeline seconds. Defaults to the playhead, clamped into the clip."),
19897
20005
  clipId: CLIP_ID,
19898
20006
  x: num("Horizontal centre as a fraction of frame width, 0..1."),
19899
20007
  y: num("Vertical centre as a fraction of frame height, 0..1."),
@@ -20706,7 +20814,8 @@ var MEDIA_TYPES = {
20706
20814
  ".gif": { mime: "image/gif", kind: "image" },
20707
20815
  ".webp": { mime: "image/webp", kind: "image" },
20708
20816
  ".svg": { mime: "image/svg+xml", kind: "image" },
20709
- ".json": { mime: "application/json", kind: "lottie" }
20817
+ ".json": { mime: "application/json", kind: "lottie" },
20818
+ ".glb": { mime: "model/gltf-binary", kind: "model" }
20710
20819
  };
20711
20820
  function mediaUrl(rel) {
20712
20821
  return `http://localhost:${WEBSOCKET_PORT}/media/${rel.split(path2.sep).map(encodeURIComponent).join("/")}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goatedit-mcp",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
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",