@clipform/mcp-server 1.44.3 → 1.44.4
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 +1 -1
- package/dist/{chunk-VTRO7ZC3.js → chunk-6IRFVCIG.js} +3 -3
- package/dist/{chunk-VTRO7ZC3.js.map → chunk-6IRFVCIG.js.map} +1 -1
- package/dist/{chunk-MIH452WS.js → chunk-MSXDS22S.js} +119 -54
- package/dist/{chunk-MIH452WS.js.map → chunk-MSXDS22S.js.map} +1 -1
- package/dist/index.js +2 -2
- package/dist/prompts.js +1 -1
- package/dist/server.js +2 -2
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
getWorkflowText,
|
|
7
7
|
objectType,
|
|
8
8
|
registerPrompts
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-6IRFVCIG.js";
|
|
10
10
|
import {
|
|
11
11
|
GUIDE_TYPES,
|
|
12
12
|
QUIZ_VARIANTS,
|
|
@@ -18295,6 +18295,27 @@ function pruneJobs() {
|
|
|
18295
18295
|
}
|
|
18296
18296
|
|
|
18297
18297
|
// src/tools/render-composition.ts
|
|
18298
|
+
var RenderItemSchema = external_exports.object({
|
|
18299
|
+
compositionId: external_exports.string().describe("The composition ID (see clipform_list_compositions)"),
|
|
18300
|
+
outputFormat: external_exports.enum(["mp4", "png"]).default("mp4").describe("Output format (default: mp4)"),
|
|
18301
|
+
inputProps: external_exports.record(external_exports.unknown()).optional().describe("Props matching the composition's schema - validated strictly")
|
|
18302
|
+
});
|
|
18303
|
+
function fireRender(item) {
|
|
18304
|
+
const job = createJob("clipform_render_composition");
|
|
18305
|
+
callApi("/internal/render", {
|
|
18306
|
+
timeoutMs: 3e5,
|
|
18307
|
+
// local renders pay a cold webpack bundle; Lambda is fast but bursty
|
|
18308
|
+
body: {
|
|
18309
|
+
compositionId: item.compositionId,
|
|
18310
|
+
outputFormat: item.outputFormat,
|
|
18311
|
+
inputProps: item.inputProps ?? {}
|
|
18312
|
+
}
|
|
18313
|
+
}).then((r) => {
|
|
18314
|
+
if (r.ok) completeJob(job.id, r.data);
|
|
18315
|
+
else failJob(job.id, r.error);
|
|
18316
|
+
}).catch((err) => failJob(job.id, err instanceof Error ? err.message : String(err)));
|
|
18317
|
+
return job;
|
|
18318
|
+
}
|
|
18298
18319
|
function registerRenderCompositionTool(server) {
|
|
18299
18320
|
server.registerTool(
|
|
18300
18321
|
"clipform_render_composition",
|
|
@@ -18304,34 +18325,40 @@ function registerRenderCompositionTool(server) {
|
|
|
18304
18325
|
|
|
18305
18326
|
For narrated Ken Burns slideshows from images, use clipform_generate_video instead. Output formats: mp4 (H.264, best for social media) or png (single frame). Returns a public URL when complete.
|
|
18306
18327
|
|
|
18307
|
-
For multi-render builds (e.g.
|
|
18328
|
+
For multi-render builds (e.g. one clip per quiz question), pass everything in ONE call via items (max 10): all renders fire in parallel and you get one job ID each - then collect the URLs in a single clipform_check_render call with job_ids. Single render: pass compositionId/inputProps at the top level (wait: true blocks and returns the URL; wait: false returns a job ID).`,
|
|
18308
18329
|
inputSchema: {
|
|
18309
|
-
compositionId: external_exports.string().describe(`
|
|
18310
|
-
outputFormat: external_exports.enum(["mp4", "png"]).default("mp4").describe("
|
|
18311
|
-
inputProps: external_exports.record(external_exports.unknown()).optional().describe("
|
|
18312
|
-
wait: external_exports.boolean().optional().default(true).describe("true (default) blocks until the render is ready and returns its URL
|
|
18330
|
+
compositionId: external_exports.string().optional().describe(`Single render: the composition ID. Call clipform_list_compositions to see available options (e.g. ${EXPOSED_COMPOSITIONS.map((id) => `'${id}'`).join(", ")}). Use items instead for multiple renders.`),
|
|
18331
|
+
outputFormat: external_exports.enum(["mp4", "png"]).default("mp4").describe("Single render: output format (default: mp4)"),
|
|
18332
|
+
inputProps: external_exports.record(external_exports.unknown()).optional().describe("Single render: props object matching the composition's schema from clipform_list_compositions. Validated STRICTLY - unknown or missing props fail with the schema in the error, nothing renders silently with defaults. For map compositions (Map), wide shots (camera.zoom < 9) may round lat/lng to 1 decimal place to improve cache hit rates; close shots and pin drops should keep full precision \u2014 the pin lands exactly on `target`."),
|
|
18333
|
+
wait: external_exports.boolean().optional().default(true).describe("Single render only: true (default) blocks until the render is ready and returns its URL; false returns a job ID for clipform_check_render. Batch items always run fire-and-poll."),
|
|
18334
|
+
items: external_exports.array(RenderItemSchema).min(1).max(10).optional().describe("Batch mode: multiple renders in one call. All fire in parallel; returns one job ID per item - collect with clipform_check_render (job_ids). Use this whenever rendering more than one clip.")
|
|
18313
18335
|
},
|
|
18314
18336
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }
|
|
18315
18337
|
},
|
|
18316
|
-
async ({ compositionId, outputFormat, inputProps, wait }) => {
|
|
18317
|
-
if (
|
|
18318
|
-
return errorResult(
|
|
18338
|
+
async ({ compositionId, outputFormat, inputProps, wait, items }) => {
|
|
18339
|
+
if (items?.length && compositionId) {
|
|
18340
|
+
return errorResult("Pass EITHER items (batch) OR compositionId (single render), not both.");
|
|
18341
|
+
}
|
|
18342
|
+
if (!items?.length && !compositionId) {
|
|
18343
|
+
return errorResult("Pass compositionId for a single render, or items for a batch.");
|
|
18344
|
+
}
|
|
18345
|
+
const unknown2 = (items ?? [{ compositionId, outputFormat, inputProps }]).map((i) => i.compositionId).filter((id) => !EXPOSED_COMPOSITIONS.includes(id));
|
|
18346
|
+
if (unknown2.length) {
|
|
18347
|
+
return errorResult(`Composition${unknown2.length > 1 ? "s" : ""} ${unknown2.map((id) => `"${id}"`).join(", ")} not available. Call clipform_list_compositions to see available options.`);
|
|
18348
|
+
}
|
|
18349
|
+
if (items?.length) {
|
|
18350
|
+
const jobs2 = items.map((item) => ({ item, job: fireRender(item) }));
|
|
18351
|
+
return textResult(
|
|
18352
|
+
[
|
|
18353
|
+
`${jobs2.length} renders started in parallel:`,
|
|
18354
|
+
...jobs2.map(({ item, job }) => `- ${item.compositionId} (${item.outputFormat}): job ${job.id}`),
|
|
18355
|
+
``,
|
|
18356
|
+
`Collect with ONE clipform_check_render call (job_ids: [...]). Renders typically take ${RENDER_TIMING.expectedRange}.`
|
|
18357
|
+
].join("\n")
|
|
18358
|
+
);
|
|
18319
18359
|
}
|
|
18320
|
-
const apiCall = () => callApi("/internal/render", {
|
|
18321
|
-
timeoutMs: 3e5,
|
|
18322
|
-
// local renders pay a cold webpack bundle; Lambda is fast but bursty
|
|
18323
|
-
body: {
|
|
18324
|
-
compositionId,
|
|
18325
|
-
outputFormat,
|
|
18326
|
-
inputProps: inputProps ?? {}
|
|
18327
|
-
}
|
|
18328
|
-
});
|
|
18329
18360
|
if (wait === false) {
|
|
18330
|
-
const job =
|
|
18331
|
-
apiCall().then((r) => {
|
|
18332
|
-
if (r.ok) completeJob(job.id, r.data);
|
|
18333
|
-
else failJob(job.id, r.error);
|
|
18334
|
-
}).catch((err) => failJob(job.id, err instanceof Error ? err.message : String(err)));
|
|
18361
|
+
const job = fireRender({ compositionId, outputFormat, inputProps });
|
|
18335
18362
|
return textResult(
|
|
18336
18363
|
[
|
|
18337
18364
|
`Render started (${compositionId}).`,
|
|
@@ -18340,7 +18367,10 @@ For multi-render builds (e.g. composition quizzes with a clue + reveal clip per
|
|
|
18340
18367
|
].join("\n")
|
|
18341
18368
|
);
|
|
18342
18369
|
}
|
|
18343
|
-
const result = await
|
|
18370
|
+
const result = await callApi("/internal/render", {
|
|
18371
|
+
timeoutMs: 3e5,
|
|
18372
|
+
body: { compositionId, outputFormat, inputProps: inputProps ?? {} }
|
|
18373
|
+
});
|
|
18344
18374
|
if (!result.ok) return errorResult(result.error);
|
|
18345
18375
|
const data = result.data;
|
|
18346
18376
|
return textResult(
|
|
@@ -18607,53 +18637,88 @@ For multi-question builds, pass wait: false on every render: each call returns a
|
|
|
18607
18637
|
}
|
|
18608
18638
|
|
|
18609
18639
|
// src/tools/check-render.ts
|
|
18640
|
+
var LOST_JOB_RECOVERY = [
|
|
18641
|
+
`Jobs expire after 30 minutes and are lost if the server restarts - the render itself may still have completed.`,
|
|
18642
|
+
`To recover, re-run the original render call with identical arguments:`,
|
|
18643
|
+
`- clipform_render_composition results are content-cached: a finished render returns its URL immediately without re-rendering.`,
|
|
18644
|
+
`- clipform_generate_video is not cached and will re-render.`
|
|
18645
|
+
].join("\n");
|
|
18646
|
+
function describeJob(id, job) {
|
|
18647
|
+
if (!job) {
|
|
18648
|
+
return { line: `- ${id}: NOT FOUND`, done: true, failed: true };
|
|
18649
|
+
}
|
|
18650
|
+
if (job.status === "rendering") {
|
|
18651
|
+
const elapsed = Math.round((Date.now() - job.createdAt) / 1e3);
|
|
18652
|
+
return { line: `- ${id}: rendering (${elapsed}s elapsed)`, done: false, failed: false };
|
|
18653
|
+
}
|
|
18654
|
+
if (job.status === "failed") {
|
|
18655
|
+
return { line: `- ${id}: FAILED - ${job.error}`, done: true, failed: true };
|
|
18656
|
+
}
|
|
18657
|
+
const data = job.result;
|
|
18658
|
+
const extras = [
|
|
18659
|
+
...data.public_url ? [data.public_url] : [],
|
|
18660
|
+
...data.duration_seconds ? [`${data.duration_seconds}s`] : []
|
|
18661
|
+
].join(" | ");
|
|
18662
|
+
return { line: `- ${id}: complete${extras ? ` - ${extras}` : ""}`, done: true, failed: false };
|
|
18663
|
+
}
|
|
18610
18664
|
function registerCheckRenderTool(server) {
|
|
18611
18665
|
server.registerTool(
|
|
18612
18666
|
"clipform_check_render",
|
|
18613
18667
|
{
|
|
18614
18668
|
title: "Check Render Status",
|
|
18615
|
-
description: `Check the status of
|
|
18669
|
+
description: `Check the status of render jobs started by clipform_generate_video or clipform_render_composition.
|
|
18616
18670
|
|
|
18617
|
-
|
|
18671
|
+
Pass job_ids to check a whole batch in ONE call - one line of status per job. Pass job_id for a single job. Returns the output URL for each completed render. Typical render time: 10-60 seconds.`,
|
|
18618
18672
|
inputSchema: {
|
|
18619
|
-
job_id: external_exports.string().uuid().describe("
|
|
18673
|
+
job_id: external_exports.string().uuid().optional().describe("A single job ID returned by the render tool"),
|
|
18674
|
+
job_ids: external_exports.array(external_exports.string().uuid()).min(1).max(20).optional().describe("Multiple job IDs - check the whole batch in one call instead of one call per job")
|
|
18620
18675
|
},
|
|
18621
18676
|
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
|
|
18622
18677
|
},
|
|
18623
|
-
async ({ job_id }) => {
|
|
18678
|
+
async ({ job_id, job_ids }) => {
|
|
18624
18679
|
pruneJobs();
|
|
18625
|
-
const
|
|
18626
|
-
if (!
|
|
18627
|
-
return errorResult(
|
|
18628
|
-
|
|
18629
|
-
|
|
18630
|
-
|
|
18631
|
-
|
|
18632
|
-
|
|
18633
|
-
|
|
18634
|
-
)
|
|
18635
|
-
|
|
18636
|
-
|
|
18637
|
-
|
|
18680
|
+
const ids = job_ids?.length ? job_ids : job_id ? [job_id] : [];
|
|
18681
|
+
if (!ids.length) {
|
|
18682
|
+
return errorResult("Pass job_id (single) or job_ids (batch).");
|
|
18683
|
+
}
|
|
18684
|
+
if (ids.length === 1) {
|
|
18685
|
+
const job = getJob(ids[0]);
|
|
18686
|
+
if (!job) {
|
|
18687
|
+
return errorResult(`No render job found with ID ${ids[0]}. ${LOST_JOB_RECOVERY}`);
|
|
18688
|
+
}
|
|
18689
|
+
if (job.status === "rendering") {
|
|
18690
|
+
const elapsed = Math.round((Date.now() - job.createdAt) / 1e3);
|
|
18691
|
+
return textResult(
|
|
18692
|
+
[
|
|
18693
|
+
`Status: rendering (${elapsed}s elapsed)`,
|
|
18694
|
+
`Tool: ${job.tool}`,
|
|
18695
|
+
``,
|
|
18696
|
+
`Still in progress. Check again in ${RENDER_TIMING.pollDelay}.`
|
|
18697
|
+
].join("\n")
|
|
18698
|
+
);
|
|
18699
|
+
}
|
|
18700
|
+
if (job.status === "failed") {
|
|
18701
|
+
return errorResult(`Render failed: ${job.error}`);
|
|
18702
|
+
}
|
|
18703
|
+
const data = job.result;
|
|
18638
18704
|
return textResult(
|
|
18639
18705
|
[
|
|
18640
|
-
`
|
|
18641
|
-
`
|
|
18642
|
-
|
|
18643
|
-
`
|
|
18706
|
+
`Render complete.`,
|
|
18707
|
+
...data.public_url ? [`Public URL: ${data.public_url}`] : [],
|
|
18708
|
+
...data.duration_seconds ? [`Duration: ${data.duration_seconds}s`] : [],
|
|
18709
|
+
`Attach via clipform_upload_node_media with fit_media: true (composed 9:16 frame - contain, never crop).`
|
|
18644
18710
|
].join("\n")
|
|
18645
18711
|
);
|
|
18646
18712
|
}
|
|
18647
|
-
|
|
18648
|
-
|
|
18649
|
-
|
|
18650
|
-
const data = job.result;
|
|
18713
|
+
const results = ids.map((id) => describeJob(id, getJob(id)));
|
|
18714
|
+
const pending = results.filter((r) => !r.done).length;
|
|
18715
|
+
const lost = results.some((r) => r.line.includes("NOT FOUND"));
|
|
18651
18716
|
return textResult(
|
|
18652
18717
|
[
|
|
18653
|
-
`Render
|
|
18654
|
-
...
|
|
18655
|
-
...
|
|
18656
|
-
|
|
18718
|
+
`Render batch: ${results.length - pending}/${results.length} finished${pending ? ` - check again in ${RENDER_TIMING.pollDelay}` : ""}.`,
|
|
18719
|
+
...results.map((r) => r.line),
|
|
18720
|
+
...pending === 0 ? [``, `Attach completed renders via clipform_upload_node_media with fit_media: true (composed 9:16 frames - contain, never crop).`] : [],
|
|
18721
|
+
...lost ? [``, LOST_JOB_RECOVERY] : []
|
|
18657
18722
|
].join("\n")
|
|
18658
18723
|
);
|
|
18659
18724
|
}
|
|
@@ -18970,4 +19035,4 @@ export {
|
|
|
18970
19035
|
JSONRPCMessageSchema,
|
|
18971
19036
|
createServer
|
|
18972
19037
|
};
|
|
18973
|
-
//# sourceMappingURL=chunk-
|
|
19038
|
+
//# sourceMappingURL=chunk-MSXDS22S.js.map
|