creavit-studio-mcp 1.3.2 → 1.3.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.
- package/README.md +11 -3
- package/package.json +1 -1
- package/src/protocol.mjs +1 -1
- package/src/rpcServer.mjs +2 -0
- package/src/tools/browserTools.mjs +87 -4
- package/src/tools/editorTools.mjs +17 -1
- package/src/tools/projectTools.mjs +8 -0
- package/src/tools/systemTools.mjs +2 -2
package/README.md
CHANGED
|
@@ -213,13 +213,17 @@ composited in.
|
|
|
213
213
|
|
|
214
214
|
### Record a URL walkthrough
|
|
215
215
|
|
|
216
|
-
`creavit_record_url_walkthrough` is the high-level workflow.
|
|
216
|
+
`creavit_record_url_walkthrough` is the high-level, atomic workflow. Use one call
|
|
217
|
+
instead of separate browser/start/interact/stop calls so client round trips and
|
|
218
|
+
retries cannot create gaps or duplicate recordings. By default it opens
|
|
217
219
|
the URL in a frameless site-only capture window (`16:9`, `4:3`, `1:1`, `9:16`,
|
|
218
220
|
or another requested ratio) and waits for the DOM, fonts, and images before
|
|
219
221
|
recording. It then executes ordered semantic actions with the real macOS mouse,
|
|
220
222
|
so the normal Creavit custom-cursor track receives authentic move/click events.
|
|
221
223
|
It trims the timeline to `durationMs`, optionally covers the whole video with a
|
|
222
|
-
perspective preset, mutes it, and saves it.
|
|
224
|
+
perspective preset, mutes it, and saves it. With `export: true`, the tool starts
|
|
225
|
+
the render in the background and returns promptly; `openExport: true` opens the
|
|
226
|
+
video automatically when rendering finishes, without requiring export UI.
|
|
223
227
|
|
|
224
228
|
It does **not** add zoom ranges: the app records the real clicks and generates
|
|
225
229
|
its own auto-zoom segments in the editor, which land on the actual click times.
|
|
@@ -247,7 +251,11 @@ Example input:
|
|
|
247
251
|
"controlSystemMouse": true,
|
|
248
252
|
"perspective": true,
|
|
249
253
|
"perspectiveTemplate": "minimal-tilt",
|
|
250
|
-
"mute": true
|
|
254
|
+
"mute": true,
|
|
255
|
+
"export": true,
|
|
256
|
+
"exportResolution": "1080p",
|
|
257
|
+
"exportFps": 60,
|
|
258
|
+
"openExport": true
|
|
251
259
|
}
|
|
252
260
|
```
|
|
253
261
|
|
package/package.json
CHANGED
package/src/protocol.mjs
CHANGED
|
@@ -6,7 +6,7 @@ export const ENDPOINT_FILENAME = "agent-bridge.json";
|
|
|
6
6
|
|
|
7
7
|
export const MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
8
8
|
export const SERVER_NAME = "creavit-studio";
|
|
9
|
-
export const SERVER_VERSION = "1.3.
|
|
9
|
+
export const SERVER_VERSION = "1.3.2";
|
|
10
10
|
|
|
11
11
|
export const JSONRPC_ERRORS = {
|
|
12
12
|
PARSE_ERROR: -32700,
|
package/src/rpcServer.mjs
CHANGED
|
@@ -40,6 +40,8 @@ export function startRpcServer({ listTools, callTool }) {
|
|
|
40
40
|
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
41
41
|
capabilities: { tools: { listChanged: false } },
|
|
42
42
|
serverInfo: { name: SERVER_NAME, version: SERVER_VERSION },
|
|
43
|
+
instructions:
|
|
44
|
+
"Plan the complete Creavit tool sequence before the first call, then execute it without unnecessary pauses. For any website workflow that includes open + record + multiple interactions + stop, use creavit_record_url_walkthrough once instead of separate browser/recording calls; it can also start export in the background and open the result when ready. Mutating recording/export calls are idempotent, so do not stop/restart merely because a client-side timeout made the result uncertain—check creavit_recording_status or creavit_events.",
|
|
43
45
|
};
|
|
44
46
|
},
|
|
45
47
|
|
|
@@ -135,6 +135,9 @@ function trimSegmentsToDuration(segments, targetDuration) {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
export function browserTools() {
|
|
138
|
+
let activeUrlWalkthroughPromise = null;
|
|
139
|
+
let recentUrlWalkthrough = null;
|
|
140
|
+
|
|
138
141
|
return [
|
|
139
142
|
bridgeTool({
|
|
140
143
|
name: "creavit_browser_open",
|
|
@@ -165,6 +168,17 @@ export function browserTools() {
|
|
|
165
168
|
),
|
|
166
169
|
}),
|
|
167
170
|
|
|
171
|
+
bridgeTool({
|
|
172
|
+
name: "creavit_browser_activate",
|
|
173
|
+
command: "browser.activate",
|
|
174
|
+
description:
|
|
175
|
+
"Brings the capture window or external browser to the foreground without changing its size or entering fullscreen. Call immediately after recording starts if you are driving the recording manually.",
|
|
176
|
+
inputSchema: schema({
|
|
177
|
+
captureWindowId: S.number("Clean capture window ID"),
|
|
178
|
+
browser: S.string("External browser name when there is no captureWindowId"),
|
|
179
|
+
}),
|
|
180
|
+
}),
|
|
181
|
+
|
|
168
182
|
bridgeTool({
|
|
169
183
|
name: "creavit_browser_focus",
|
|
170
184
|
command: "browser.focusPoint",
|
|
@@ -220,7 +234,7 @@ export function browserTools() {
|
|
|
220
234
|
{
|
|
221
235
|
name: "creavit_record_url_walkthrough",
|
|
222
236
|
description:
|
|
223
|
-
"
|
|
237
|
+
"USE THIS SINGLE ATOMIC TOOL for requests that open a website, record several clicks/types, stop, and optionally export/open the video. Do not split that workflow into manual browser + recording calls: this tool prevents duplicate recordings and agent round-trip gaps. It opens a non-fullscreen configurable-ratio window, waits for real page readiness, keeps camera off, defaults microphone/system audio off, executes ordered semantic steps, and can export/open the final video. Zooms are NOT added: Creavit Studio records the real clicks and generates its own auto-zoom segments in the editor. Only pass autoZoom=true when the user explicitly asks for extra scripted zooms.",
|
|
224
238
|
inputSchema: schema(
|
|
225
239
|
{
|
|
226
240
|
url: S.string("http(s) page to record"),
|
|
@@ -246,10 +260,27 @@ export function browserTools() {
|
|
|
246
260
|
perspective: S.bool("Apply perspective to the complete result"),
|
|
247
261
|
perspectiveTemplate: S.string("Perspective template id (default minimal-tilt)"),
|
|
248
262
|
save: S.bool("Save the generated project after editing (default true)"),
|
|
263
|
+
export: S.bool("Start export in the background after recording/editing (default false)"),
|
|
264
|
+
exportFilePath: S.string("Optional absolute target .mp4 path; defaults beside the .crvt project"),
|
|
265
|
+
exportResolution: S.string("Export resolution (default 1080p)"),
|
|
266
|
+
exportFps: S.number("Export frame rate (default 60)"),
|
|
267
|
+
exportQuality: S.string("Export quality (default high)", {
|
|
268
|
+
enum: ["compact", "high", "max"],
|
|
269
|
+
}),
|
|
270
|
+
openExport: S.bool("Open the exported video when finished (default true when export=true)"),
|
|
249
271
|
},
|
|
250
272
|
["url"],
|
|
251
273
|
),
|
|
252
|
-
run:
|
|
274
|
+
run: (args) => {
|
|
275
|
+
const requestKey = JSON.stringify(args || {});
|
|
276
|
+
if (
|
|
277
|
+
recentUrlWalkthrough?.key === requestKey &&
|
|
278
|
+
Date.now() < recentUrlWalkthrough.expiresAt
|
|
279
|
+
) {
|
|
280
|
+
return Promise.resolve(recentUrlWalkthrough.result);
|
|
281
|
+
}
|
|
282
|
+
if (activeUrlWalkthroughPromise) return activeUrlWalkthroughPromise;
|
|
283
|
+
const operation = (async () => {
|
|
253
284
|
const browser = canonicalBrowser(args.browser || "Zen Browser");
|
|
254
285
|
const cleanWindow = args.cleanWindow !== false;
|
|
255
286
|
const loadWaitMs = Math.max(0, Math.min(60_000, Number(args.loadWaitMs) || 1500));
|
|
@@ -329,7 +360,7 @@ export function browserTools() {
|
|
|
329
360
|
}
|
|
330
361
|
const browserWindow = await waitForBrowserWindow(captureWindowId ? "Creavit Studio" : browser, titleHint, 20_000);
|
|
331
362
|
|
|
332
|
-
await callCommand("recording.start", {
|
|
363
|
+
const startResult = await callCommand("recording.start", {
|
|
333
364
|
options: {
|
|
334
365
|
startScreen: true,
|
|
335
366
|
cameraEnabled: false,
|
|
@@ -338,7 +369,19 @@ export function browserTools() {
|
|
|
338
369
|
recordingSource: { type: "window", windowId: browserWindow.id, windowInfo: browserWindow },
|
|
339
370
|
},
|
|
340
371
|
}, 180_000);
|
|
372
|
+
if (startResult?.started === false) {
|
|
373
|
+
throw new Error(
|
|
374
|
+
"A recording is already active. Stop it before starting a URL walkthrough.",
|
|
375
|
+
);
|
|
376
|
+
}
|
|
341
377
|
recordingStarted = true;
|
|
378
|
+
// recording.start briefly focuses the recorder controls. Restore the
|
|
379
|
+
// requested browser immediately so the actual walkthrough is visibly
|
|
380
|
+
// in front from the first recorded frame onward.
|
|
381
|
+
await callCommand("browser.activate", {
|
|
382
|
+
captureWindowId,
|
|
383
|
+
browser: captureWindowId ? undefined : browser,
|
|
384
|
+
});
|
|
342
385
|
const recordingStartedAt = Date.now();
|
|
343
386
|
const contentDeadline = durationMs ? recordingStartedAt + durationMs - tailMs : Number.POSITIVE_INFINITY;
|
|
344
387
|
await sleep(Math.min(leadInMs, Math.max(0, contentDeadline - Date.now())));
|
|
@@ -355,6 +398,7 @@ export function browserTools() {
|
|
|
355
398
|
selector: section.selector,
|
|
356
399
|
text: section.text,
|
|
357
400
|
value: section.value,
|
|
401
|
+
useSystemCursor: controlSystemMouse,
|
|
358
402
|
}, 30_000);
|
|
359
403
|
if (Number.isFinite(interaction?.x)) focus = { x: interaction.x, y: interaction.y };
|
|
360
404
|
} else {
|
|
@@ -387,6 +431,12 @@ export function browserTools() {
|
|
|
387
431
|
}
|
|
388
432
|
await callCommand("recording.stop", {}, 600_000);
|
|
389
433
|
recordingStarted = false;
|
|
434
|
+
// The source is finalized now. Closing it before editor processing/export
|
|
435
|
+
// avoids leaving a stale capture window over the foreground app.
|
|
436
|
+
if (captureWindowId) {
|
|
437
|
+
await callCommand("browser.close", { captureWindowId });
|
|
438
|
+
captureWindowId = null;
|
|
439
|
+
}
|
|
390
440
|
|
|
391
441
|
const editorState = await waitForEditor(previousEditor?.projectFilePath, 600_000);
|
|
392
442
|
const applied = {
|
|
@@ -396,6 +446,8 @@ export function browserTools() {
|
|
|
396
446
|
perspective: null,
|
|
397
447
|
saved: false,
|
|
398
448
|
trimmedTo: null,
|
|
449
|
+
export: null,
|
|
450
|
+
openedExport: null,
|
|
399
451
|
};
|
|
400
452
|
let finalDuration = Number(editorState.duration);
|
|
401
453
|
if (durationMs) {
|
|
@@ -432,7 +484,24 @@ export function browserTools() {
|
|
|
432
484
|
await callCommand("editor.saveProject", {}, 180_000);
|
|
433
485
|
applied.saved = true;
|
|
434
486
|
}
|
|
435
|
-
|
|
487
|
+
if (args.export === true) {
|
|
488
|
+
const exportResult = await callCommand(
|
|
489
|
+
"editor.export.start",
|
|
490
|
+
{
|
|
491
|
+
...(args.exportFilePath ? { filePath: args.exportFilePath } : {}),
|
|
492
|
+
format: "mp4",
|
|
493
|
+
resolution: args.exportResolution || "1080p",
|
|
494
|
+
fps: Math.max(1, Math.min(120, Math.round(Number(args.exportFps) || 60))),
|
|
495
|
+
quality: args.exportQuality || "high",
|
|
496
|
+
openWhenFinished: args.openExport !== false,
|
|
497
|
+
},
|
|
498
|
+
30_000,
|
|
499
|
+
);
|
|
500
|
+
applied.export = exportResult;
|
|
501
|
+
applied.openedExport =
|
|
502
|
+
args.openExport !== false ? "scheduled when export finishes" : null;
|
|
503
|
+
}
|
|
504
|
+
return textResult({ ok: true, url: args.url, browser: cleanWindow ? "Creavit Capture" : browser, cleanWindow, page: openResult, window: browserWindow, duration: finalDuration, rawDuration: editorState.duration, projectFilePath: editorState.projectFilePath, exportStartedInBackground: applied.export?.started === true, exportJob: applied.export?.job || null, applied });
|
|
436
505
|
} finally {
|
|
437
506
|
if (recordingStarted) {
|
|
438
507
|
try { await callCommand("recording.stop", {}, 600_000); } catch (_) {}
|
|
@@ -441,6 +510,20 @@ export function browserTools() {
|
|
|
441
510
|
try { await callCommand("browser.close", { captureWindowId }); } catch (_) {}
|
|
442
511
|
}
|
|
443
512
|
}
|
|
513
|
+
})();
|
|
514
|
+
activeUrlWalkthroughPromise = operation;
|
|
515
|
+
return operation.then((result) => {
|
|
516
|
+
recentUrlWalkthrough = {
|
|
517
|
+
key: requestKey,
|
|
518
|
+
result,
|
|
519
|
+
expiresAt: Date.now() + 60_000,
|
|
520
|
+
};
|
|
521
|
+
return result;
|
|
522
|
+
}).finally(() => {
|
|
523
|
+
if (activeUrlWalkthroughPromise === operation) {
|
|
524
|
+
activeUrlWalkthroughPromise = null;
|
|
525
|
+
}
|
|
526
|
+
});
|
|
444
527
|
},
|
|
445
528
|
},
|
|
446
529
|
];
|
|
@@ -314,7 +314,7 @@ export function editorTools() {
|
|
|
314
314
|
name: "creavit_editor_export",
|
|
315
315
|
command: "editor.export",
|
|
316
316
|
description:
|
|
317
|
-
"Exports the video. This takes a LONG time (minutes). Follow progress with creavit_events (export.started / export.finished / export.failed)
|
|
317
|
+
"Exports the video idempotently; concurrent retries share one render. This takes a LONG time (minutes). Follow progress with creavit_events (export.started / export.finished / export.failed), then use creavit_file_open when the user asks to open/play it. If filePath is omitted, the export is written beside the open .crvt project.",
|
|
318
318
|
inputSchema: schema({
|
|
319
319
|
filePath: S.string("Target file path (.mp4)"),
|
|
320
320
|
format: S.string("mp4 or gif", { enum: ["mp4", "gif"] }),
|
|
@@ -325,6 +325,22 @@ export function editorTools() {
|
|
|
325
325
|
timeoutMs: 3_600_000,
|
|
326
326
|
}),
|
|
327
327
|
|
|
328
|
+
bridgeTool({
|
|
329
|
+
name: "creavit_editor_export_start",
|
|
330
|
+
command: "editor.export.start",
|
|
331
|
+
description:
|
|
332
|
+
"Starts one export in the background and returns immediately. Use this when tool latency matters. Progress/completion is reported by creavit_events; set openWhenFinished=true to open/play the result automatically without showing export UI.",
|
|
333
|
+
inputSchema: schema({
|
|
334
|
+
filePath: S.string("Target file path (.mp4); omit to export beside the project"),
|
|
335
|
+
format: S.string("mp4 or gif", { enum: ["mp4", "gif"] }),
|
|
336
|
+
resolution: S.string("e.g. 720p, 1080p, 4k"),
|
|
337
|
+
fps: S.number("Frame rate, e.g. 30 or 60"),
|
|
338
|
+
quality: S.string("compact | high | max"),
|
|
339
|
+
openWhenFinished: S.bool("Open the exported file automatically when rendering completes"),
|
|
340
|
+
}),
|
|
341
|
+
timeoutMs: 30_000,
|
|
342
|
+
}),
|
|
343
|
+
|
|
328
344
|
{
|
|
329
345
|
name: "creavit_editor_history",
|
|
330
346
|
description: "Undoes the last editor change, or redoes it.",
|
|
@@ -71,6 +71,14 @@ export function projectTools() {
|
|
|
71
71
|
inputSchema: schema({ filePath: S.string("Absolute path to the file") }, ["filePath"]),
|
|
72
72
|
}),
|
|
73
73
|
|
|
74
|
+
bridgeTool({
|
|
75
|
+
name: "creavit_file_open",
|
|
76
|
+
command: "project.openFile",
|
|
77
|
+
description:
|
|
78
|
+
"Opens an existing exported video or other file with its default macOS application. Use this after creavit_editor_export when the user asks to open/play the result.",
|
|
79
|
+
inputSchema: schema({ filePath: S.string("Absolute path to the file") }, ["filePath"]),
|
|
80
|
+
}),
|
|
81
|
+
|
|
74
82
|
bridgeTool({
|
|
75
83
|
name: "creavit_project_save",
|
|
76
84
|
command: "editor.saveProject",
|
|
@@ -74,7 +74,7 @@ export function systemTools() {
|
|
|
74
74
|
name: "creavit_recording_start",
|
|
75
75
|
command: "recording.start",
|
|
76
76
|
description:
|
|
77
|
-
"Starts a screen recording. Get source IDs from creavit_devices_list first.
|
|
77
|
+
"Starts a screen recording idempotently: retries while starting share the same native operation, and calling it while active does not restart. Get source IDs from creavit_devices_list first. For a multi-step website recording, use ONE creavit_record_url_walkthrough call instead of this manual tool.",
|
|
78
78
|
inputSchema: schema({
|
|
79
79
|
options: S.object(
|
|
80
80
|
"Recording options: {sourceType:'display'|'window'|'area', sourceId, cameraEnabled, micEnabled, systemAudioEnabled, delayMs, area:{x,y,width,height}}. cameraEnabled:false also turns the camera off and hides its window, so it does not appear in a screen recording; cameraEnabled:true opens and starts it.",
|
|
@@ -88,7 +88,7 @@ export function systemTools() {
|
|
|
88
88
|
name: "creavit_recording_stop",
|
|
89
89
|
command: "recording.stop",
|
|
90
90
|
description:
|
|
91
|
-
"Stops the recording. Processing can take a few minutes; the editor opens automatically when it finishes. Track progress with creavit_events (watch for recording.stopped and project.loaded).",
|
|
91
|
+
"Stops the recording idempotently. Processing can take a few minutes; concurrent retries share the same operation and the editor opens automatically when it finishes. Track progress with creavit_events (watch for recording.stopped and project.loaded).",
|
|
92
92
|
inputSchema: schema(),
|
|
93
93
|
timeoutMs: 600_000,
|
|
94
94
|
}),
|