@clipform/mcp-server 2.12.0 → 2.13.0

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.
@@ -18535,6 +18535,7 @@ var MediaAssetItemSchema = external_exports.object({
18535
18535
  ).min(1).describe("Per-word timestamps within the segment. Required - copy the full array from clipform_generate_tts verbatim.")
18536
18536
  })
18537
18537
  ).optional().describe("Word-level captions from clipform_generate_tts. `words` is a required field on each caption (schema-enforced) - pass the full objects returned by clipform_generate_tts unmodified."),
18538
+ caption_ref: external_exports.string().optional().describe("caption_ref from clipform_generate_tts - an opaque handle that attaches that TTS run's word-level captions to this asset, resolved server-side. Preferred over captions."),
18538
18539
  ai_generated: external_exports.boolean().optional().describe("Whether this media was AI-generated. Drives the viewer's disclosure chip - set true for AI-rendered/synthesized media.")
18539
18540
  });
18540
18541
  function registerUploadMediaAssetTool(server) {
@@ -18544,7 +18545,7 @@ function registerUploadMediaAssetTool(server) {
18544
18545
  title: "Upload Media Asset",
18545
18546
  description: `Put one or more media files into your workspace media library (max 10, uploaded sequentially). This is step one of attaching media to a node - follow up with clipform_attach_node_media to place the returned media_asset_id on a node.
18546
18547
 
18547
- When a public URL is provided, the media is fetched and stored automatically. For video: ingested via Mux. For image: stored in Supabase. Captions from clipform_generate_tts enable per-word highlighting in the viewer once attached.`,
18548
+ When a public URL is provided, the media is fetched and stored automatically. For video: ingested via Mux. For image: stored in Supabase. Word-level captions from clipform_generate_tts enable per-word highlighting in the viewer once attached - pass the caption_ref it returned instead of hand-copying the captions array.`,
18548
18549
  inputSchema: {
18549
18550
  items: external_exports.array(MediaAssetItemSchema).min(1).max(10).describe("One or more media items to create as workspace library assets")
18550
18551
  },
@@ -18592,6 +18593,7 @@ When a public URL is provided, the media is fetched and stored automatically. Fo
18592
18593
  };
18593
18594
  if (item.url) createBody.url = item.url;
18594
18595
  if (item.captions) createBody.captions = item.captions;
18596
+ if (item.caption_ref) createBody.caption_ref = item.caption_ref;
18595
18597
  if (item.ai_generated !== void 0) createBody.ai_generated = item.ai_generated;
18596
18598
  const created = await callApi(`/workspaces/${workspaceId}/media`, { method: "POST", body: createBody });
18597
18599
  if (!created.ok) {
@@ -18629,7 +18631,9 @@ When a public URL is provided, the media is fetched and stored automatically. Fo
18629
18631
  } else {
18630
18632
  resultLines.push(`Media stored in the workspace library.`);
18631
18633
  }
18632
- if (item.captions) {
18634
+ if (item.caption_ref) {
18635
+ resultLines.push(`Captions resolved from caption_ref.`);
18636
+ } else if (item.captions) {
18633
18637
  resultLines.push(`Captions: ${item.captions.length} segments saved`);
18634
18638
  }
18635
18639
  lines.push(resultLines.join("\n"));
@@ -19082,7 +19086,7 @@ Available voices: ryan (British male, clear), sonia (British female, warm), andr
19082
19086
 
19083
19087
  Use the tone parameter to direct HOW the voice speaks. Always set a tone that matches the form's mood - e.g. quizzes: "Energetic and playful, like a quiz show host teasing the audience", surveys: "Professional but warm, encouraging honest answers", personality quizzes: "Curious and reflective". This dramatically improves the narration quality.
19084
19088
 
19085
- Pass one item or many (max 10) - multiple items run in parallel. Returns audio URL and word-level captions per item.`,
19089
+ Pass one item or many (max 10) - multiple items run in parallel. Returns audio_url and caption_ref per item - pass caption_ref downstream to clipform_render_composition / clipform_render_video_template / clipform_upload_media_asset to attach this run's word-level captions instead of hand-copying the captions array.`,
19086
19090
  inputSchema: {
19087
19091
  items: external_exports.array(TtsItemSchema).min(1).max(10).describe("One or more TTS items to generate")
19088
19092
  },
@@ -19091,7 +19095,8 @@ Pass one item or many (max 10) - multiple items run in parallel. Returns audio U
19091
19095
  external_exports.object({
19092
19096
  ok: external_exports.boolean(),
19093
19097
  audio_url: external_exports.string().optional(),
19094
- captions: external_exports.array(CaptionSchema2).optional().describe("Pass as the captions param to upload_media_asset"),
19098
+ caption_ref: external_exports.string().optional().describe("Pass this as the caption_ref param on clipform_render_composition / clipform_render_video_template / clipform_upload_media_asset to attach this run's word-level captions - never hand-copy the captions array."),
19099
+ captions: external_exports.array(CaptionSchema2).optional().describe("Word-level captions, for reference only - prefer passing caption_ref downstream instead of copying this array."),
19095
19100
  error: external_exports.string().optional()
19096
19101
  })
19097
19102
  ).describe("One result per item, in order")
@@ -19118,6 +19123,9 @@ Pass one item or many (max 10) - multiple items run in parallel. Returns audio U
19118
19123
  successCount++;
19119
19124
  const data = r.value.data;
19120
19125
  lines.push(`Audio URL: ${data.audioUrl}`);
19126
+ if (data.captionRef) {
19127
+ lines.push(`Caption ref: ${data.captionRef} (pass as caption_ref to clipform_render_composition / clipform_render_video_template / clipform_upload_media_asset)`);
19128
+ }
19121
19129
  const rawCaptions = Array.isArray(data.captions) ? data.captions : [];
19122
19130
  const captions = rawCaptions.map((c) => ({
19123
19131
  start: c.start,
@@ -19129,6 +19137,7 @@ Pass one item or many (max 10) - multiple items run in parallel. Returns audio U
19129
19137
  structured.push({
19130
19138
  ok: true,
19131
19139
  ...data.audioUrl ? { audio_url: String(data.audioUrl) } : {},
19140
+ ...data.captionRef ? { caption_ref: String(data.captionRef) } : {},
19132
19141
  captions
19133
19142
  });
19134
19143
  } else {
@@ -19144,7 +19153,7 @@ Pass one item or many (max 10) - multiple items run in parallel. Returns audio U
19144
19153
  lines.unshift(`TTS: ${successCount}/${items.length} succeeded
19145
19154
  `);
19146
19155
  }
19147
- lines.push(`Pass the complete Captions JSON as the "captions" parameter when uploading.`);
19156
+ lines.push(`Pass caption_ref downstream (to clipform_render_composition / clipform_render_video_template / clipform_upload_media_asset) instead of hand-copying the captions array.`);
19148
19157
  if (successCount === 0) return errorResult(lines.join("\n"));
19149
19158
  return structuredResult(lines.join("\n"), { results: structured });
19150
19159
  }
@@ -19401,6 +19410,7 @@ async function completeViaPlaceholder(placeholder, input) {
19401
19410
  try {
19402
19411
  const body = { url: input.publicUrl };
19403
19412
  if (input.captions?.length) body.captions = input.captions;
19413
+ if (input.captionRef) body.caption_ref = input.captionRef;
19404
19414
  const completed = await callApi(`/workspaces/${placeholder.workspaceId}/media/${placeholder.assetId}/complete`, {
19405
19415
  method: "POST",
19406
19416
  body
@@ -19429,6 +19439,7 @@ async function legacyAutoAttach(input) {
19429
19439
  url: input.publicUrl
19430
19440
  };
19431
19441
  if (input.captions?.length) createBody.captions = input.captions;
19442
+ if (input.captionRef) createBody.caption_ref = input.captionRef;
19432
19443
  const created = await callApi(`/workspaces/${workspaceId}/media`, { method: "POST", body: createBody });
19433
19444
  if (!created.ok) return { attached: false, error: `Failed to create media asset: ${created.error}` };
19434
19445
  const mediaAssetId = created.data?.media_asset_id;
@@ -19474,7 +19485,8 @@ var RenderItemSchema = external_exports.object({
19474
19485
  outputFormat: external_exports.enum(["mp4", "png"]).default("mp4").describe("Output format (default: mp4)"),
19475
19486
  inputProps: external_exports.record(external_exports.unknown()).optional().describe("Props matching the composition's schema - validated strictly"),
19476
19487
  node_id: external_exports.string().optional().describe("Form node to attach this item's render to automatically once it completes. Requires the batch's top-level form_id."),
19477
- captions: external_exports.array(CaptionSchema).optional().describe("Word-level captions from clipform_generate_tts - carried onto the attached media asset. Only used when node_id is set.")
19488
+ captions: external_exports.array(CaptionSchema).optional().describe("Word-level captions from clipform_generate_tts - carried onto the attached media asset. Only used when node_id is set."),
19489
+ caption_ref: external_exports.string().optional().describe("caption_ref from clipform_generate_tts - an opaque handle that attaches that TTS run's word-level captions to the media asset, resolved server-side. Preferred over captions. Only used when node_id is set.")
19478
19490
  });
19479
19491
  function fireRender(item, formId) {
19480
19492
  const job = createJob("clipform_render_composition");
@@ -19506,6 +19518,7 @@ function fireRender(item, formId) {
19506
19518
  publicUrl,
19507
19519
  mediaType,
19508
19520
  captions: item.captions,
19521
+ captionRef: item.caption_ref,
19509
19522
  placeholder
19510
19523
  });
19511
19524
  } else {
@@ -19544,7 +19557,8 @@ ${RENDER_ATTACH_RULE} In batch mode, set node_id per item (see items) and form_i
19544
19557
  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."),
19545
19558
  node_id: external_exports.string().optional().describe("Single render only: form node to attach this render to automatically once it completes. Requires form_id. For batch mode, set node_id per item instead."),
19546
19559
  form_id: external_exports.string().uuid().optional().describe("The form UUID - required when node_id is set (single render) or any item sets node_id (batch)."),
19547
- captions: external_exports.array(CaptionSchema).optional().describe("Single render only: word-level captions from clipform_generate_tts, carried onto the attached media asset. Only used when node_id is set. For batch mode, set captions per item instead.")
19560
+ captions: external_exports.array(CaptionSchema).optional().describe("Single render only: word-level captions from clipform_generate_tts, carried onto the attached media asset. Only used when node_id is set. For batch mode, set captions per item instead."),
19561
+ caption_ref: external_exports.string().optional().describe("Single render only: caption_ref from clipform_generate_tts - an opaque handle that attaches that TTS run's word-level captions to the media asset, resolved server-side. Preferred over captions. Only used when node_id is set. For batch mode, set caption_ref per item instead.")
19548
19562
  },
19549
19563
  outputSchema: {
19550
19564
  jobs: external_exports.array(
@@ -19567,7 +19581,7 @@ ${RENDER_ATTACH_RULE} In batch mode, set node_id per item (see items) and form_i
19567
19581
  // node_id too (destructive/openWorld apply "even in only select modes").
19568
19582
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true }
19569
19583
  },
19570
- async ({ compositionId, outputFormat, inputProps, wait, items, node_id, form_id, captions }) => {
19584
+ async ({ compositionId, outputFormat, inputProps, wait, items, node_id, form_id, captions, caption_ref }) => {
19571
19585
  if (items?.length && compositionId) {
19572
19586
  return errorResult("Pass EITHER items (batch) OR compositionId (single render), not both.");
19573
19587
  }
@@ -19604,7 +19618,7 @@ ${RENDER_ATTACH_RULE} In batch mode, set node_id per item (see items) and form_i
19604
19618
  );
19605
19619
  }
19606
19620
  if (wait === false) {
19607
- const job = fireRender({ compositionId, outputFormat, inputProps, node_id, captions }, form_id);
19621
+ const job = fireRender({ compositionId, outputFormat, inputProps, node_id, captions, caption_ref }, form_id);
19608
19622
  return structuredResult(
19609
19623
  [
19610
19624
  `Render started (${compositionId}).`,
@@ -19638,6 +19652,7 @@ ${RENDER_ATTACH_RULE} In batch mode, set node_id per item (see items) and form_i
19638
19652
  publicUrl: data.public_url,
19639
19653
  mediaType: mediaTypeSingle,
19640
19654
  captions,
19655
+ captionRef: caption_ref,
19641
19656
  placeholder
19642
19657
  });
19643
19658
  } else if (placeholder) {
@@ -19893,7 +19908,8 @@ ${RENDER_ATTACH_RULE}`,
19893
19908
  wait: external_exports.boolean().optional().default(true).describe("true (default) blocks until the render is ready and returns its URL; false returns a job ID for clipform_check_render."),
19894
19909
  node_id: external_exports.string().optional().describe("Form node to attach this render to automatically once it completes - skips the manual clipform_upload_media_asset + clipform_attach_node_media steps and republishes the form if it's currently live. Requires form_id."),
19895
19910
  form_id: external_exports.string().uuid().optional().describe("The form UUID (required when node_id is set)."),
19896
- captions: external_exports.array(CaptionSchema).optional().describe("Word-level captions from clipform_generate_tts - carried onto the attached media asset. Only used when node_id is set.")
19911
+ captions: external_exports.array(CaptionSchema).optional().describe("Word-level captions from clipform_generate_tts - carried onto the attached media asset. Only used when node_id is set."),
19912
+ caption_ref: external_exports.string().optional().describe("caption_ref from clipform_generate_tts - an opaque handle that attaches that TTS run's word-level captions to the media asset, resolved server-side. Preferred over captions. Only used when node_id is set.")
19897
19913
  },
19898
19914
  outputSchema: {
19899
19915
  status: external_exports.enum(["rendering", "complete"]),
@@ -19912,7 +19928,7 @@ ${RENDER_ATTACH_RULE}`,
19912
19928
  // node_id too (destructive/openWorld apply "even in only select modes").
19913
19929
  annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true }
19914
19930
  },
19915
- async ({ template, controls, outputFormat, wait, node_id, form_id, captions }) => {
19931
+ async ({ template, controls, outputFormat, wait, node_id, form_id, captions, caption_ref }) => {
19916
19932
  if (node_id && !form_id) {
19917
19933
  return errorResult("form_id is required when node_id is set.");
19918
19934
  }
@@ -19936,7 +19952,7 @@ ${RENDER_ATTACH_RULE}`,
19936
19952
  const publicUrl = r.data.public_url;
19937
19953
  const placeholder = await placeholderPromise2;
19938
19954
  if (publicUrl) {
19939
- attach2 = await autoAttachRender({ formId: form_id, nodeId: node_id, publicUrl, mediaType, captions, placeholder });
19955
+ attach2 = await autoAttachRender({ formId: form_id, nodeId: node_id, publicUrl, mediaType, captions, captionRef: caption_ref, placeholder });
19940
19956
  } else {
19941
19957
  const message = "Render completed without a public_url; nothing to attach.";
19942
19958
  attach2 = { attached: false, error: message };
@@ -19974,7 +19990,7 @@ ${RENDER_ATTACH_RULE}`,
19974
19990
  if (node_id && form_id) {
19975
19991
  const placeholder = await placeholderPromise;
19976
19992
  if (data.public_url) {
19977
- attach = await autoAttachRender({ formId: form_id, nodeId: node_id, publicUrl: data.public_url, mediaType, captions, placeholder });
19993
+ attach = await autoAttachRender({ formId: form_id, nodeId: node_id, publicUrl: data.public_url, mediaType, captions, captionRef: caption_ref, placeholder });
19978
19994
  } else if (placeholder) {
19979
19995
  await failPlaceholder(placeholder, "Render completed without a public_url; nothing to attach.");
19980
19996
  }
@@ -20165,7 +20181,7 @@ ${RENDER_ATTACH_RULE}`,
20165
20181
  const publicUrl = r.data.public_url;
20166
20182
  const placeholder = await placeholderPromise2;
20167
20183
  if (publicUrl) {
20168
- attach2 = await autoAttachRender({ formId: form_id, nodeId: node_id, publicUrl, mediaType: "video", captions, placeholder });
20184
+ attach2 = await autoAttachRender({ formId: form_id, nodeId: node_id, publicUrl, mediaType: "video", captions, captionRef: audio_url, placeholder });
20169
20185
  } else {
20170
20186
  const message = "Render completed without a public_url; nothing to attach.";
20171
20187
  attach2 = { attached: false, error: message };
@@ -20200,7 +20216,7 @@ ${RENDER_ATTACH_RULE}`,
20200
20216
  if (node_id && form_id) {
20201
20217
  const placeholder = await placeholderPromise;
20202
20218
  if (data.public_url) {
20203
- attach = await autoAttachRender({ formId: form_id, nodeId: node_id, publicUrl: data.public_url, mediaType: "video", captions, placeholder });
20219
+ attach = await autoAttachRender({ formId: form_id, nodeId: node_id, publicUrl: data.public_url, mediaType: "video", captions, captionRef: audio_url, placeholder });
20204
20220
  } else if (placeholder) {
20205
20221
  await failPlaceholder(placeholder, "Render completed without a public_url; nothing to attach.");
20206
20222
  }
@@ -20704,4 +20720,4 @@ export {
20704
20720
  buildInstructions,
20705
20721
  createServer
20706
20722
  };
20707
- //# sourceMappingURL=chunk-UVRL2EXF.js.map
20723
+ //# sourceMappingURL=chunk-66KZWBI7.js.map