goatedit-mcp 1.1.12 → 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.
- package/bundle/goatedit-mcp.js +90 -9
- package/package.json +1 -1
package/bundle/goatedit-mcp.js
CHANGED
|
@@ -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.
|
|
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",
|
|
@@ -21797,8 +21806,18 @@ var SEQUENCE_EDITS = /* @__PURE__ */ new Set([
|
|
|
21797
21806
|
"setAudioMaster",
|
|
21798
21807
|
"loopClip"
|
|
21799
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
|
+
]);
|
|
21800
21819
|
var EXPECTED_SEQUENCE = str(
|
|
21801
|
-
"Optional
|
|
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."
|
|
21802
21821
|
);
|
|
21803
21822
|
var PLACING_EDITS = /* @__PURE__ */ new Set([
|
|
21804
21823
|
"addClip",
|
|
@@ -21825,7 +21844,7 @@ var NEW_TRACK_ABOVE = str(
|
|
|
21825
21844
|
);
|
|
21826
21845
|
var NEW_TRACK_BELOW = str('With trackId "__new_track__": put the new track directly below this video track.');
|
|
21827
21846
|
var TOOLS = BASE_TOOLS.map((tool) => {
|
|
21828
|
-
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;
|
|
21829
21848
|
return {
|
|
21830
21849
|
...tool,
|
|
21831
21850
|
inputSchema: {
|
|
@@ -21984,6 +22003,11 @@ function withRunProject(stepArgs, projectId) {
|
|
|
21984
22003
|
if (typeof projectId !== "string" || !projectId || typeof stepArgs.projectId === "string") return stepArgs;
|
|
21985
22004
|
return { ...stepArgs, projectId };
|
|
21986
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
|
+
}
|
|
21987
22011
|
|
|
21988
22012
|
// src/tab-registry.ts
|
|
21989
22013
|
function indexTabs(reports, isOpen) {
|
|
@@ -21994,6 +22018,23 @@ function indexTabs(reports, isOpen) {
|
|
|
21994
22018
|
return index;
|
|
21995
22019
|
}
|
|
21996
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
|
+
|
|
21997
22038
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/client.js
|
|
21998
22039
|
var ExperimentalClientTasks = class {
|
|
21999
22040
|
constructor(_client) {
|
|
@@ -24493,7 +24534,7 @@ var VERSION = (() => {
|
|
|
24493
24534
|
}
|
|
24494
24535
|
return "0.0.0";
|
|
24495
24536
|
})();
|
|
24496
|
-
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.`;
|
|
24497
24538
|
var MEDIA_TYPES = {
|
|
24498
24539
|
".mp4": { mime: "video/mp4", kind: "video" },
|
|
24499
24540
|
".m4v": { mime: "video/mp4", kind: "video" },
|
|
@@ -24750,6 +24791,8 @@ wss.on("error", () => {
|
|
|
24750
24791
|
});
|
|
24751
24792
|
var tabs = /* @__PURE__ */ new Map();
|
|
24752
24793
|
var tabReports = /* @__PURE__ */ new Map();
|
|
24794
|
+
var tabOrigins = /* @__PURE__ */ new WeakMap();
|
|
24795
|
+
var lastEditorOrigin = process.env.GOATEDIT_EDITOR_URL || null;
|
|
24753
24796
|
function reindexTabs() {
|
|
24754
24797
|
tabs.clear();
|
|
24755
24798
|
for (const [pid, r] of indexTabs(tabReports.values(), (s) => s.readyState === import_websocket.default.OPEN)) {
|
|
@@ -24858,6 +24901,10 @@ wss.on("connection", (ws, req) => {
|
|
|
24858
24901
|
return;
|
|
24859
24902
|
}
|
|
24860
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
|
+
}
|
|
24861
24908
|
ws.on("message", (data) => {
|
|
24862
24909
|
let message;
|
|
24863
24910
|
try {
|
|
@@ -25017,8 +25064,31 @@ var LOCAL_HANDLERS = {
|
|
|
25017
25064
|
list_connected_projects: async () => connectedProjects(),
|
|
25018
25065
|
list_local_assets: async (args) => handleListLocalAssets(args),
|
|
25019
25066
|
generate_voiceover: async (args, projectId) => handleGenerateVoiceover(args.text, projectId),
|
|
25020
|
-
find_local_file: async (args) => handleFindLocalFile(args)
|
|
25067
|
+
find_local_file: async (args) => handleFindLocalFile(args),
|
|
25068
|
+
open_project: async (args) => handleOpenProject(args)
|
|
25021
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
|
+
}
|
|
25022
25092
|
async function callLocal(method, args, projectId) {
|
|
25023
25093
|
await ensureTransport();
|
|
25024
25094
|
if (ownsPort) return LOCAL_HANDLERS[method](args, projectId ?? void 0);
|
|
@@ -25034,6 +25104,15 @@ var LOCAL_TOOLS = [
|
|
|
25034
25104
|
description: "List the GoatEdit projects open in connected browser tabs, with which one tools act on by default.",
|
|
25035
25105
|
inputSchema: { type: "object", properties: {} }
|
|
25036
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
|
+
},
|
|
25037
25116
|
{
|
|
25038
25117
|
name: "use_project",
|
|
25039
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.",
|
|
@@ -25081,7 +25160,8 @@ var LOCAL_TOOLS = [
|
|
|
25081
25160
|
},
|
|
25082
25161
|
continueOnError: { type: "boolean", description: "Run the remaining steps after one fails." },
|
|
25083
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." },
|
|
25084
|
-
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." }
|
|
25085
25165
|
},
|
|
25086
25166
|
required: ["calls"]
|
|
25087
25167
|
}
|
|
@@ -25109,6 +25189,7 @@ async function callTool(name, args) {
|
|
|
25109
25189
|
case "list_connected_projects":
|
|
25110
25190
|
case "list_local_assets":
|
|
25111
25191
|
case "generate_voiceover":
|
|
25192
|
+
case "open_project":
|
|
25112
25193
|
return callLocal(name, forwarded, projectId);
|
|
25113
25194
|
case "use_project": {
|
|
25114
25195
|
const wanted = typeof forwarded.projectId === "string" ? forwarded.projectId : projectId;
|
|
@@ -25176,7 +25257,7 @@ async function runTools(args) {
|
|
|
25176
25257
|
const tool = typeof step?.tool === "string" ? step.tool : "";
|
|
25177
25258
|
try {
|
|
25178
25259
|
const stepArgs = resolveRefs(step.args && typeof step.args === "object" ? step.args : {}, refs);
|
|
25179
|
-
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)));
|
|
25180
25261
|
if (typeof step.as === "string" && step.as) {
|
|
25181
25262
|
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
25182
25263
|
const { _image, _images, ...kept } = raw;
|
package/package.json
CHANGED