goatedit-mcp 1.1.11 → 1.1.13

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.
@@ -19841,7 +19841,7 @@ import crypto2 from "crypto";
19841
19841
  import http from "http";
19842
19842
  import fs3 from "fs";
19843
19843
  import path3 from "path";
19844
- import { execFileSync } from "child_process";
19844
+ import { execFile, execFileSync } from "child_process";
19845
19845
  import os2 from "os";
19846
19846
  import { fileURLToPath } from "url";
19847
19847
 
@@ -20055,9 +20055,18 @@ var BASE_TOOLS = [
20055
20055
  },
20056
20056
  method: "deleteSequence"
20057
20057
  },
20058
+ {
20059
+ name: "list_projects",
20060
+ description: "The person's saved GoatEdit projects, newest first: id, name, when last changed, and whether a connected tab has it open. Use an id with open_project to work on a project that is not open yet. Read-only.",
20061
+ inputSchema: {
20062
+ type: "object",
20063
+ properties: { limit: { type: "number", description: "How many to list (default 30, at most 100)." } }
20064
+ },
20065
+ method: "listProjects"
20066
+ },
20058
20067
  {
20059
20068
  name: "list_sequences",
20060
- description: "Every sequence in the project: id, name, whether it is the one open for editing, open in a tab, a nest (made by nest_clips) or top-level, its length, how many clips it holds, and the clips that play it. Every other tool edits the open sequence only \u2014 use this to find the id to pass to open_sequence.",
20069
+ description: "Every sequence in the project: id, name, whether it is the one open for editing, open in a tab, a nest (made by nest_clips) or top-level, its length, how many clips it holds, and the clips that play it. Tools act on the open sequence unless they are given expectedSequenceId \u2014 pass one of these ids to read or edit another sequence without opening it (open_sequence moves the person's view; prefer expectedSequenceId).",
20061
20070
  inputSchema: { type: "object", properties: {} },
20062
20071
  method: "listSequences"
20063
20072
  },
@@ -21059,7 +21068,7 @@ var BASE_TOOLS = [
21059
21068
  clipId: CLIP_ID,
21060
21069
  type: {
21061
21070
  type: "string",
21062
- description: "Effect type: blur, invert, hue, saturation, brightness, contrast, exposure, grayscale, sepia, gradientMap, vignette, filmGrain, flicker, posterizeTime, lensDistortion, chromaticAberration, lensFlare, liquidGlass, opacityFx, colorCorrection, depthMap, gradientMap, glow, pixelDissolve, motionBlur, dropShadow, reflection, colorama, outline, echo, flutedGlass, lightRays, halftone, datamosh, roughenEdges, filmStock, halation, starGlow, filmDamage, lightLeak, thermal, colorBalance, duotone, levels, monochrome, channelMixer, dither, threshold, lensBlur, directionalBlur, spinBlur, zoomBlur, sharpen, cameraShake, bulge, twirl, ripple, waveWarp, polarCoords, turbulentDisplace, elasticGrid, gridSplit, cubify, glitch, vhs, ntsc, crtScreen, ledScreen, rgbSplit, jpegGlitch, ascii, macpaint, modulation, stripe, risograph, emboss, bevel, inkBleed, paperTexture, paperCutout, crossHatch, scatter, blobTracker, edgeGlow, pageTurn, lightSweep, deepGlow, twister, fill, grid, repeater, plus transform, layer3d, model3d and showcase. Call describe_effect with a type for its parameters, their ranges and what they do before setting anything but the obvious. An unknown name comes back with the full list."
21071
+ description: "Effect type: blur, invert, hue, saturation, brightness, contrast, exposure, grayscale, sepia, gradientMap, vignette, filmGrain, flicker, posterizeTime, lensDistortion, chromaticAberration, lensFlare, liquidGlass, opacityFx, colorCorrection, depthMap, gradientMap, vegas, glow, pixelDissolve, motionBlur, dropShadow, reflection, colorama, outline, echo, flutedGlass, lightRays, halftone, datamosh, roughenEdges, filmStock, halation, starGlow, filmDamage, lightLeak, thermal, colorBalance, duotone, levels, monochrome, channelMixer, dither, threshold, fourColorGradient, lensBlur, boxBlur, directionalBlur, spinBlur, zoomBlur, sharpen, cameraShake, bulge, twirl, ripple, waveWarp, polarCoords, turbulentDisplace, elasticGrid, gridSplit, cubify, glitch, vhs, ntsc, crtScreen, ledScreen, rgbSplit, jpegGlitch, ascii, macpaint, modulation, stripe, risograph, emboss, bevel, inkBleed, paperTexture, paperCutout, crossHatch, scatter, blobTracker, edgeGlow, pageTurn, lightSweep, deepGlow, twister, fill, compositeOriginal, grid, repeater, plus transform, layer3d, model3d and showcase. Call describe_effect with a type for its parameters, their ranges and what they do before setting anything but the obvious. An unknown name comes back with the full list."
21063
21072
  },
21064
21073
  params: {
21065
21074
  type: "object",
@@ -21575,7 +21584,7 @@ var BASE_TOOLS = [
21575
21584
  },
21576
21585
  {
21577
21586
  name: "studio_tasks",
21578
- description: 'The work GoatEdit Studio has for you in this project. Studio makes a video in stages (brief, research, script, breakdown (the script cut into parts), moodboard, styleGuide (the design language every part follows), storyboard, edit plan, combine (the parts joined into the final edit plan), timeline, mix, QC, export, plus any the user added, whose ids start "x-" and whose document is {"notes": string, "points"?: string[]}); a stage opens once the stages it reads are approved, so several are often open at once. Each task is kind "write" (write that stage\'s JSON document: follow `role` as your system prompt, read `inputs`, follow `lessons` when present (what this user has asked for before), and if `previousDraft` is there, revise it using `note` and `problems`) or kind "run" (ask the tab to do it with studio_run_stage). Open tasks in one run do not depend on each other: hand each to its own subagent and do them side by side. When a task names an `agent`, the user wants that subagent or skill of yours on it: hand the task to the subagent with that name, or use the skill with that name. In a batch of reels each run has `batch` (reel number, name, avatar); stages listed in batch.filled are filled by Studio itself (the locked script, the pilot\'s look), so leave them. For each write task: studio_claim it first (the person sees your agent working on that stage), then studio_submit the document. The reply to a submit names the stages it opened; call studio_tasks again for them. Stages waiting for the person are listed under waitingForPerson \u2014 stop there and tell the user to approve them in the Studio panel.',
21587
+ description: 'The work GoatEdit Studio has for you in this project. Studio makes a video in stages (brief, research, script, breakdown (the script cut into parts), moodboard, styleGuide (the design language every part follows), storyboard, edit plan, combine (the parts joined into the final edit plan), timeline, mix, QC, export, plus any the user added, whose ids start "x-" and whose document is {"notes": string, "points"?: string[]}); a stage opens once the stages it reads are approved, so several are often open at once. Each task is kind "write" (write that stage\'s JSON document: follow `role` as your system prompt, read `inputs`, follow `lessons` when present (what this user has asked for before), and if `previousDraft` is there, revise it using `note` and `problems`) or kind "run" (ask the tab to do it with studio_run_stage). Open tasks in one run do not depend on each other: hand each to its own subagent and do them side by side. When a task names an `agent`, the user wants that subagent or skill of yours on it: hand the task to the subagent with that name, or use the skill with that name. In a batch of reels each run has `batch` (reel number, name, avatar); stages listed in batch.filled are filled by Studio itself (the locked script, the pilot\'s look), so leave them. For each write task: studio_claim it first (the person sees your agent working on that stage), then studio_submit the document. The reply to a submit names the stages it opened; call studio_tasks again for them. Stages waiting for the person are listed under waitingForPerson \u2014 stop there and tell the user to approve them in the Studio panel, unless the user told you to approve them yourself (then studio_approve).',
21579
21588
  inputSchema: {
21580
21589
  type: "object",
21581
21590
  properties: {
@@ -21608,12 +21617,61 @@ var BASE_TOOLS = [
21608
21617
  runId: str("The run, from studio_tasks."),
21609
21618
  stage: str("The stage id."),
21610
21619
  document: { type: "object", description: "The stage's document, in the shape its role card gives." },
21611
- agent: str("Who wrote it, as in studio_claim.")
21620
+ agent: str("Who wrote it, as in studio_claim."),
21621
+ part: str("For a stage written part by part (the task lists `parts`): the part id. The document then holds only that part's beats (storyboard) or scenes (edit plan), and the part is checked and shown in the Studio gallery on its own.")
21612
21622
  },
21613
21623
  required: ["runId", "stage", "document"]
21614
21624
  },
21615
21625
  method: "studioSubmit"
21616
21626
  },
21627
+ {
21628
+ name: "studio_approve",
21629
+ description: "Approve a Studio stage that is waiting for the person (waitingForPerson in studio_tasks), on their behalf. Only when the user told you to approve it, or to run Studio on their own without stopping: otherwise tell them it is waiting for them in the Studio panel. personSaid keeps what they told you in the run's log, so the panel shows who approved it and why. A stage with rival drafts needs pick; a stage written part by part approves every part waiting. The reply names the stages that opened.",
21630
+ inputSchema: {
21631
+ type: "object",
21632
+ properties: {
21633
+ runId: str("The run, from studio_tasks."),
21634
+ stage: str("The stage id."),
21635
+ agent: str('Who is approving, shown in the log ("Claude Code").'),
21636
+ personSaid: str(`The user's words that let you approve it, e.g. "do everything by yourself".`),
21637
+ pick: num("With rival drafts: which one, 0-based.")
21638
+ },
21639
+ required: ["runId", "stage"]
21640
+ },
21641
+ method: "studioApprove"
21642
+ },
21643
+ {
21644
+ name: "studio_send_back",
21645
+ description: "Send a Studio stage in review back to whoever writes it, with what to change; it opens again with the note. Use it when a draft you are reviewing for the user is not good enough.",
21646
+ inputSchema: {
21647
+ type: "object",
21648
+ properties: {
21649
+ runId: str("The run, from studio_tasks."),
21650
+ stage: str("The stage id."),
21651
+ note: str("What to change.")
21652
+ },
21653
+ required: ["runId", "stage", "note"]
21654
+ },
21655
+ method: "studioSendBack"
21656
+ },
21657
+ {
21658
+ name: "studio_update_run",
21659
+ description: `Change a Studio run under way, for the stages still to come (approved, left-out or running stages keep theirs): who writes each stage and who approves it (the same settings as studio_save_workflow; brief, moodboard, styleGuide and export stay approved by the person), and editSequenceId: a sequence the person already edited, for the timeline and qc stages to restyle in place instead of building a new one. With editSequenceId set, timeline and qc can take writer "outside": you edit that sequence over MCP and studio_submit the stage's document. null for editSequenceId goes back to a new sequence.`,
21660
+ inputSchema: {
21661
+ type: "object",
21662
+ properties: {
21663
+ runId: str("The run, from studio_tasks."),
21664
+ stages: {
21665
+ type: "object",
21666
+ description: 'Stage id to { writer?, approver?: "person" | "auto", agent?, instructions?, tier?, review?, enabled? }.',
21667
+ additionalProperties: { type: "object" }
21668
+ },
21669
+ editSequenceId: { type: ["string", "null"], description: "A sequence id from list_sequences, or null." }
21670
+ },
21671
+ required: ["runId"]
21672
+ },
21673
+ method: "studioUpdateRun"
21674
+ },
21617
21675
  {
21618
21676
  name: "studio_run_stage",
21619
21677
  description: `Start a Studio stage the tab does itself: "timeline" (build the edit from the approved plan) or "qc" (check it), or a stage the workflow gives to Studio's agent in the tab. Returns at once; the work goes on in the tab and the panel shows it. Poll studio_tasks to see it finish.`,
@@ -21748,8 +21806,18 @@ var SEQUENCE_EDITS = /* @__PURE__ */ new Set([
21748
21806
  "setAudioMaster",
21749
21807
  "loopClip"
21750
21808
  ]);
21809
+ var SEQUENCE_READS = /* @__PURE__ */ new Set([
21810
+ "getTimeline",
21811
+ "getClips",
21812
+ "renderFrame",
21813
+ "lintTimeline",
21814
+ "lintMotion",
21815
+ "describeMotion",
21816
+ "listCuts",
21817
+ "exportRange"
21818
+ ]);
21751
21819
  var EXPECTED_SEQUENCE = str(
21752
- "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."
21820
+ "Optional: the sequence id (from list_sequences) this call reads or edits. It does not need to be open: the edit lands in that sequence and the tab stays on whatever sequence the person is looking at, so several agents can each work on their own sequence at once. Without it, the call acts on the open sequence."
21753
21821
  );
21754
21822
  var PLACING_EDITS = /* @__PURE__ */ new Set([
21755
21823
  "addClip",
@@ -21776,7 +21844,7 @@ var NEW_TRACK_ABOVE = str(
21776
21844
  );
21777
21845
  var NEW_TRACK_BELOW = str('With trackId "__new_track__": put the new track directly below this video track.');
21778
21846
  var TOOLS = BASE_TOOLS.map((tool) => {
21779
- if (!tool.method || !SEQUENCE_EDITS.has(tool.method)) return tool;
21847
+ if (!tool.method || !(SEQUENCE_EDITS.has(tool.method) || SEQUENCE_READS.has(tool.method))) return tool;
21780
21848
  return {
21781
21849
  ...tool,
21782
21850
  inputSchema: {
@@ -21935,6 +22003,11 @@ function withRunProject(stepArgs, projectId) {
21935
22003
  if (typeof projectId !== "string" || !projectId || typeof stepArgs.projectId === "string") return stepArgs;
21936
22004
  return { ...stepArgs, projectId };
21937
22005
  }
22006
+ function withRunSequence(stepArgs, sequenceId, tool) {
22007
+ if (typeof sequenceId !== "string" || !sequenceId || typeof stepArgs.expectedSequenceId === "string") return stepArgs;
22008
+ if (!tool || !("expectedSequenceId" in (tool.inputSchema.properties ?? {}))) return stepArgs;
22009
+ return { ...stepArgs, expectedSequenceId: sequenceId };
22010
+ }
21938
22011
 
21939
22012
  // src/tab-registry.ts
21940
22013
  function indexTabs(reports, isOpen) {
@@ -21945,6 +22018,23 @@ function indexTabs(reports, isOpen) {
21945
22018
  return index;
21946
22019
  }
21947
22020
 
22021
+ // src/open-project.ts
22022
+ var DEFAULT_EDITOR = "https://ai.goatedit.com";
22023
+ function editorUrl(origin, projectId) {
22024
+ const url2 = new URL(origin || DEFAULT_EDITOR);
22025
+ url2.pathname = "/";
22026
+ url2.search = "";
22027
+ url2.hash = "";
22028
+ url2.searchParams.set("project", projectId);
22029
+ url2.searchParams.set("via", "agent");
22030
+ return url2.toString();
22031
+ }
22032
+ function openCommand(platform, url2) {
22033
+ if (platform === "darwin") return ["open", [url2]];
22034
+ if (platform === "win32") return ["cmd", ["/c", "start", "", url2]];
22035
+ return ["xdg-open", [url2]];
22036
+ }
22037
+
21948
22038
  // node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/client.js
21949
22039
  var ExperimentalClientTasks = class {
21950
22040
  constructor(_client) {
@@ -24444,7 +24534,7 @@ var VERSION = (() => {
24444
24534
  }
24445
24535
  return "0.0.0";
24446
24536
  })();
24447
- var INSTRUCTIONS = `Edits a GoatEdit video project live in the user's browser tab at ai.goatedit.com. At the start of each new request from the user, call begin_request with the kind of edit, its style words and any reference videos, then get_style_profile for how this user edits; treat that style as defaults, the request wins. Call get_timeline before your first edit \u2014 clip ids change after every split, trim or delete, and a stale id is rejected. Each edit's reply carries timelineChanges (clips added, with their ids and placement; clips changed; ids removed), so take the next ids from there instead of reading the timeline again. get_timeline is lean by default; pass detail: "full" only when you need effect parameters, and trackId or from/to to read part of it. Several edits in a row go faster as one run_tools call. With more than one tab open, call list_connected_projects, then use_project to pick one. To work on several projects at once (one helper agent per video), do not use use_project \u2014 it moves every caller \u2014 but pass projectId on each call, or once on run_tools for all its steps. render_preview_frame and preview_media accept saveToDisk: true, which writes the frame to a file and returns its path instead of the image. If a tool reports the editor is not connected, ask the user to open ai.goatedit.com and switch on Local in the AI status menu.`;
24537
+ var INSTRUCTIONS = `Edits a GoatEdit video project live in the user's browser tab at ai.goatedit.com. At the start of each new request from the user, call begin_request with the kind of edit, its style words and any reference videos, then get_style_profile for how this user edits; treat that style as defaults, the request wins. Call get_timeline before your first edit \u2014 clip ids change after every split, trim or delete, and a stale id is rejected. Each edit's reply carries timelineChanges (clips added, with their ids and placement; clips changed; ids removed), so take the next ids from there instead of reading the timeline again. get_timeline is lean by default; pass detail: "full" only when you need effect parameters, and trackId or from/to to read part of it. Several edits in a row go faster as one run_tools call. With more than one tab open, call list_connected_projects, then use_project to pick one. To work on a project that is not open, find it with list_projects and call open_project. To work on several projects at once (one helper agent per video), do not use use_project \u2014 it moves every caller \u2014 but pass projectId on each call, or once on run_tools for all its steps. To read or edit a sequence other than the open one, do not call open_sequence \u2014 it moves the user's view \u2014 but pass expectedSequenceId on each call, or once on run_tools; the edit lands in that sequence while the user keeps working wherever they are. render_preview_frame and preview_media accept saveToDisk: true, which writes the frame to a file and returns its path instead of the image. If a tool reports the editor is not connected, ask the user to open ai.goatedit.com and switch on Local in the AI status menu.`;
24448
24538
  var MEDIA_TYPES = {
24449
24539
  ".mp4": { mime: "video/mp4", kind: "video" },
24450
24540
  ".m4v": { mime: "video/mp4", kind: "video" },
@@ -24701,6 +24791,8 @@ wss.on("error", () => {
24701
24791
  });
24702
24792
  var tabs = /* @__PURE__ */ new Map();
24703
24793
  var tabReports = /* @__PURE__ */ new Map();
24794
+ var tabOrigins = /* @__PURE__ */ new WeakMap();
24795
+ var lastEditorOrigin = process.env.GOATEDIT_EDITOR_URL || null;
24704
24796
  function reindexTabs() {
24705
24797
  tabs.clear();
24706
24798
  for (const [pid, r] of indexTabs(tabReports.values(), (s) => s.readyState === import_websocket.default.OPEN)) {
@@ -24809,6 +24901,10 @@ wss.on("connection", (ws, req) => {
24809
24901
  return;
24810
24902
  }
24811
24903
  console.error("[GoatEdit MCP] Browser editor connected via WebSocket");
24904
+ if (req.headers.origin) {
24905
+ tabOrigins.set(ws, req.headers.origin);
24906
+ lastEditorOrigin = req.headers.origin;
24907
+ }
24812
24908
  ws.on("message", (data) => {
24813
24909
  let message;
24814
24910
  try {
@@ -24968,8 +25064,31 @@ var LOCAL_HANDLERS = {
24968
25064
  list_connected_projects: async () => connectedProjects(),
24969
25065
  list_local_assets: async (args) => handleListLocalAssets(args),
24970
25066
  generate_voiceover: async (args, projectId) => handleGenerateVoiceover(args.text, projectId),
24971
- find_local_file: async (args) => handleFindLocalFile(args)
25067
+ find_local_file: async (args) => handleFindLocalFile(args),
25068
+ open_project: async (args) => handleOpenProject(args)
24972
25069
  };
25070
+ var OPEN_WAIT_MS = 45e3;
25071
+ async function handleOpenProject(args) {
25072
+ const projectId = typeof args?.projectId === "string" ? args.projectId.trim() : "";
25073
+ if (!projectId) throw new Error("Missing required argument: projectId (from list_projects).");
25074
+ const open = tabs.get(projectId);
25075
+ if (open && open.ws.readyState === import_websocket.default.OPEN) return { projectId, name: open.projectName, alreadyOpen: true };
25076
+ const origin = [...tabReports.keys()].map((ws) => tabOrigins.get(ws)).find(Boolean) ?? lastEditorOrigin;
25077
+ const url2 = editorUrl(origin, projectId);
25078
+ const [cmd, cmdArgs] = openCommand(process.platform, url2);
25079
+ await new Promise((resolve, reject) => {
25080
+ execFile(cmd, cmdArgs, (err) => err ? reject(new Error(`Could not open the browser (${cmd}): ${err.message}. Open ${url2} by hand.`)) : resolve());
25081
+ });
25082
+ const deadline = Date.now() + OPEN_WAIT_MS;
25083
+ while (Date.now() < deadline) {
25084
+ const tab = tabs.get(projectId);
25085
+ if (tab && tab.ws.readyState === import_websocket.default.OPEN) return { projectId, name: tab.projectName, opened: url2 };
25086
+ await new Promise((r) => setTimeout(r, 500));
25087
+ }
25088
+ throw new Error(
25089
+ `Opened ${url2}, but no tab reported project ${projectId} within ${OPEN_WAIT_MS / 1e3}s. The person may need to sign in, or switch on Local in the AI status menu of that tab. Check list_connected_projects again in a moment.`
25090
+ );
25091
+ }
24973
25092
  async function callLocal(method, args, projectId) {
24974
25093
  await ensureTransport();
24975
25094
  if (ownsPort) return LOCAL_HANDLERS[method](args, projectId ?? void 0);
@@ -24985,6 +25104,15 @@ var LOCAL_TOOLS = [
24985
25104
  description: "List the GoatEdit projects open in connected browser tabs, with which one tools act on by default.",
24986
25105
  inputSchema: { type: "object", properties: {} }
24987
25106
  },
25107
+ {
25108
+ name: "open_project",
25109
+ description: "Open a saved project (id from list_projects) in a new browser tab and wait until it connects, so you can work on it \u2014 several projects at once, one tab each, addressed with projectId. A project already open is left as it is. The tab asks the person to allow its footage folder with a popup: tell them to click Allow.",
25110
+ inputSchema: {
25111
+ type: "object",
25112
+ properties: { projectId: { type: "string", description: "The project to open." } },
25113
+ required: ["projectId"]
25114
+ }
25115
+ },
24988
25116
  {
24989
25117
  name: "use_project",
24990
25118
  description: "Pick which connected project every later tool call acts on, when more than one tab is open. Ids come from list_connected_projects.",
@@ -25032,7 +25160,8 @@ var LOCAL_TOOLS = [
25032
25160
  },
25033
25161
  continueOnError: { type: "boolean", description: "Run the remaining steps after one fails." },
25034
25162
  rollback: { type: "boolean", description: "Take a checkpoint first and restore it if any step fails, so a run either lands whole or not at all. Cannot be combined with continueOnError." },
25035
- projectId: { type: "string", description: "Run every step on this project (from list_connected_projects). Use it when several agents edit different tabs at once; a step's own projectId wins." }
25163
+ projectId: { type: "string", description: "Run every step on this project (from list_connected_projects). Use it when several agents edit different tabs at once; a step's own projectId wins." },
25164
+ expectedSequenceId: { type: "string", description: "Run every step that takes expectedSequenceId on this sequence (from list_sequences), open or not. Use it when several agents edit different sequences at once; a step's own expectedSequenceId wins." }
25036
25165
  },
25037
25166
  required: ["calls"]
25038
25167
  }
@@ -25060,6 +25189,7 @@ async function callTool(name, args) {
25060
25189
  case "list_connected_projects":
25061
25190
  case "list_local_assets":
25062
25191
  case "generate_voiceover":
25192
+ case "open_project":
25063
25193
  return callLocal(name, forwarded, projectId);
25064
25194
  case "use_project": {
25065
25195
  const wanted = typeof forwarded.projectId === "string" ? forwarded.projectId : projectId;
@@ -25127,7 +25257,7 @@ async function runTools(args) {
25127
25257
  const tool = typeof step?.tool === "string" ? step.tool : "";
25128
25258
  try {
25129
25259
  const stepArgs = resolveRefs(step.args && typeof step.args === "object" ? step.args : {}, refs);
25130
- const raw = await callTool(tool, withRunProject(stepArgs, args.projectId));
25260
+ const raw = await callTool(tool, withRunSequence(withRunProject(stepArgs, args.projectId), args.expectedSequenceId, BROWSER_TOOLS_BY_NAME.get(tool)));
25131
25261
  if (typeof step.as === "string" && step.as) {
25132
25262
  if (raw && typeof raw === "object" && !Array.isArray(raw)) {
25133
25263
  const { _image, _images, ...kept } = raw;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "goatedit-mcp",
3
- "version": "1.1.11",
3
+ "version": "1.1.13",
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",