@clipform/mcp-server 1.28.0 → 1.29.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.
@@ -16986,6 +16986,20 @@ var PUBLIC_COMPOSITIONS = [
16986
16986
  "GlobeToCity",
16987
16987
  "CityToGlobe"
16988
16988
  ];
16989
+ var LABS_COMPOSITIONS = [
16990
+ "CountrySilhouetteClip",
16991
+ "FlagRevealGB",
16992
+ "FlagRevealJP",
16993
+ "FlagRevealBR",
16994
+ "ColorCards",
16995
+ "TextReveal",
16996
+ "EmojiPuzzle",
16997
+ "StatCounter",
16998
+ "ListReveal",
16999
+ "BeforeAfter",
17000
+ "MediaSlideshow"
17001
+ ];
17002
+ var EXPOSED_COMPOSITIONS = process.env.CLIPFORM_LABS === "1" ? [...PUBLIC_COMPOSITIONS, ...LABS_COMPOSITIONS] : PUBLIC_COMPOSITIONS;
16989
17003
 
16990
17004
  // src/lib/schemas.ts
16991
17005
  var ACTIVE_NODE_TYPES = Object.entries(NODE_TYPES).filter(([, def]) => def.is_active && !def.is_system).map(([type]) => type);
@@ -18212,14 +18226,14 @@ function registerRenderCompositionTool(server) {
18212
18226
 
18213
18227
  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.`,
18214
18228
  inputSchema: {
18215
- compositionId: external_exports.string().describe(`The composition ID. Call clipform_list_compositions to see available options (e.g. ${PUBLIC_COMPOSITIONS.map((id) => `'${id}'`).join(", ")})`),
18229
+ compositionId: external_exports.string().describe(`The composition ID. Call clipform_list_compositions to see available options (e.g. ${EXPOSED_COMPOSITIONS.map((id) => `'${id}'`).join(", ")})`),
18216
18230
  outputFormat: external_exports.enum(["mp4", "png"]).default("mp4").describe("Output format (default: mp4)"),
18217
- inputProps: external_exports.record(external_exports.unknown()).optional().describe("Props object matching the composition's expected schema. For map compositions (GlobeToCity, CityToGlobe), round lat/lng to 1 decimal place (e.g. 48.9 instead of 48.8566) \u2014 this improves cache hit rates.")
18231
+ inputProps: external_exports.record(external_exports.unknown()).optional().describe("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 (GlobeToCity, CityToGlobe), round lat/lng to 1 decimal place (e.g. 48.9 instead of 48.8566) \u2014 this improves cache hit rates.")
18218
18232
  },
18219
18233
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }
18220
18234
  },
18221
18235
  async ({ compositionId, outputFormat, inputProps }) => {
18222
- if (!PUBLIC_COMPOSITIONS.includes(compositionId)) {
18236
+ if (!EXPOSED_COMPOSITIONS.includes(compositionId)) {
18223
18237
  return errorResult(`Composition "${compositionId}" is not available. Call clipform_list_compositions to see available options.`);
18224
18238
  }
18225
18239
  const result = await callApi("/internal/render", {
@@ -18280,6 +18294,25 @@ function registerSearchMusicTool(server) {
18280
18294
  }
18281
18295
 
18282
18296
  // src/tools/list-compositions.ts
18297
+ function summarizeSchema(schema) {
18298
+ if (!schema || typeof schema !== "object" || !schema.properties) {
18299
+ return "none (renders built-in defaults only - omit inputProps)";
18300
+ }
18301
+ const required2 = new Set(schema.required ?? []);
18302
+ const typeOf = (prop) => {
18303
+ if (prop.enum) {
18304
+ return prop.enum.length <= 8 ? prop.enum.map((v) => JSON.stringify(v)).join("|") : `enum(${prop.enum.length} values, e.g. ${JSON.stringify(prop.enum[0])})`;
18305
+ }
18306
+ if (prop.anyOf) return prop.anyOf.map(typeOf).join(" | ");
18307
+ if (Array.isArray(prop.type)) return prop.type.join("|");
18308
+ if (prop.type === "array") return `${typeOf(prop.items ?? {})}[]`;
18309
+ return prop.type ?? "object";
18310
+ };
18311
+ const parts = Object.entries(schema.properties).map(
18312
+ ([name, prop]) => `${name}${required2.has(name) ? "*" : "?"}: ${typeOf(prop)}`
18313
+ );
18314
+ return parts.join(", ");
18315
+ }
18283
18316
  function registerListCompositionsTool(server) {
18284
18317
  server.registerTool(
18285
18318
  "clipform_list_compositions",
@@ -18300,16 +18333,17 @@ function registerListCompositionsTool(server) {
18300
18333
  return errorResult(result.error);
18301
18334
  }
18302
18335
  const all = result.data.compositions;
18303
- const compositions = all.filter((c) => PUBLIC_COMPOSITIONS.includes(c.id));
18336
+ const compositions = all.filter((c) => EXPOSED_COMPOSITIONS.includes(c.id));
18304
18337
  if (!compositions.length) return textResult("No compositions found.");
18305
18338
  const lines = [`Available compositions (${compositions.length}):
18306
18339
  `];
18307
18340
  for (const comp of compositions) {
18308
18341
  const duration3 = (comp.durationInFrames / comp.fps).toFixed(0);
18309
18342
  lines.push(`- **${comp.id}** \u2014 ${comp.width}x${comp.height}, ${duration3}s`);
18343
+ lines.push(` props: ${summarizeSchema(comp.propsSchema)}`);
18310
18344
  }
18311
18345
  lines.push("");
18312
- lines.push("Use clipform_render_composition with a compositionId to render. Pass inputProps matching the composition's schema.");
18346
+ lines.push("Use clipform_render_composition with a compositionId to render. inputProps are validated strictly against the schema - unknown or missing props fail.");
18313
18347
  return textResult(lines.join("\n"));
18314
18348
  }
18315
18349
  );
@@ -18795,4 +18829,4 @@ export {
18795
18829
  JSONRPCMessageSchema,
18796
18830
  createServer
18797
18831
  };
18798
- //# sourceMappingURL=chunk-GA2W3F3Y.js.map
18832
+ //# sourceMappingURL=chunk-5FG2V7B7.js.map