@clipform/mcp-server 1.36.0 → 1.38.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.
@@ -6,14 +6,15 @@ import {
6
6
  getWorkflowText,
7
7
  objectType,
8
8
  registerPrompts
9
- } from "./chunk-6MLZZSNM.js";
9
+ } from "./chunk-QT3RFCCN.js";
10
10
  import {
11
11
  GUIDE_TYPES,
12
12
  QUIZ_VARIANTS,
13
- getGuideContent,
13
+ fetchGuideText,
14
14
  getGuideUri,
15
+ guideFallbackText,
15
16
  registerResources
16
- } from "./chunk-2AJMWM5W.js";
17
+ } from "./chunk-ZTJU467D.js";
17
18
  import {
18
19
  BUSINESS,
19
20
  CONTACT_FIELDS,
@@ -22,6 +23,7 @@ import {
22
23
  KEN_BURNS_EFFECTS,
23
24
  KEN_BURNS_FIT_MODES,
24
25
  KEN_BURNS_PRESETS,
26
+ MCP_TOOL_TIERS,
25
27
  NODE_TYPES,
26
28
  NON_COUNTABLE_TYPES,
27
29
  SLIDESHOW_TRANSITIONS,
@@ -29,7 +31,7 @@ import {
29
31
  errorResult,
30
32
  resolveFormType,
31
33
  textResult
32
- } from "./chunk-BYJMRIB6.js";
34
+ } from "./chunk-KBWMI24E.js";
33
35
  import {
34
36
  __commonJS,
35
37
  __export,
@@ -16983,9 +16985,7 @@ var _endScreen = NODE_TYPES.end_screen.config_schema.properties;
16983
16985
  var END_SCREEN_ICONS = _endScreen.icon.enum;
16984
16986
  var END_SCREEN_CTA_TYPES = _endScreen.cta_type.enum;
16985
16987
  var PUBLIC_COMPOSITIONS = [
16986
- "GlobeToCity",
16987
- "CityToGlobe",
16988
- "MapPin",
16988
+ "Map",
16989
16989
  "Grid",
16990
16990
  "GridList",
16991
16991
  "ObscuredReveal",
@@ -17006,7 +17006,9 @@ var PUBLIC_COMPOSITIONS = [
17006
17006
  ];
17007
17007
  var LABS_COMPOSITIONS = [
17008
17008
  "StatCounter",
17009
- "ListReveal"
17009
+ "ListReveal",
17010
+ "Scene",
17011
+ "Spin3D"
17010
17012
  ];
17011
17013
  var EXPOSED_COMPOSITIONS = process.env.CLIPFORM_LABS === "1" ? [...PUBLIC_COMPOSITIONS, ...LABS_COMPOSITIONS] : PUBLIC_COMPOSITIONS;
17012
17014
 
@@ -17782,7 +17784,8 @@ var MediaItemSchema = external_exports.object({
17782
17784
  ).optional().describe("Per-word timestamps within the segment")
17783
17785
  })
17784
17786
  ).optional().describe("Word-level captions from clipform_generate_tts. Required for per-word highlighting - pass the full objects including 'words' arrays."),
17785
- show_captions: external_exports.boolean().optional().default(true).describe("Display captions/subtitles on the node")
17787
+ show_captions: external_exports.boolean().optional().default(true).describe("Display captions/subtitles on the node"),
17788
+ fit_media: external_exports.boolean().optional().describe("Show the whole frame (contain) instead of cover-cropping. ALWAYS set true when attaching renders from clipform_render_composition or clipform_generate_video - they are composed 9:16 frames that must never be cropped. Leave unset for user-supplied media (arbitrary aspect ratios want the cover default).")
17786
17789
  });
17787
17790
  function registerUploadNodeMediaTool(server) {
17788
17791
  server.registerTool(
@@ -17791,7 +17794,7 @@ function registerUploadNodeMediaTool(server) {
17791
17794
  title: "Upload Node Media",
17792
17795
  description: `Upload media for one or more nodes. Pass one item or many (max 10). Multiple items upload sequentially.
17793
17796
 
17794
- When a public URL is provided, the media is fetched and stored automatically. Only works on node types that support media (${MEDIA_SUPPORTED_TYPES.join(", ")}). For video: ingested via Mux. For image: stored in Supabase. Captions from clipform_generate_tts enable per-word highlighting in the viewer.`,
17797
+ When a public URL is provided, the media is fetched and stored automatically. Only works on node types that support media (${MEDIA_SUPPORTED_TYPES.join(", ")}). For video: ingested via Mux. For image: stored in Supabase. Captions from clipform_generate_tts enable per-word highlighting in the viewer. When attaching renders from clipform_render_composition or clipform_generate_video, set fit_media: true on each item.`,
17795
17798
  inputSchema: {
17796
17799
  form_id: external_exports.string().uuid().describe("The form UUID (returned by clipform_create_form, not the short share_id from the URL)"),
17797
17800
  items: external_exports.array(MediaItemSchema).min(1).max(10).describe("One or more media items to upload")
@@ -17816,6 +17819,7 @@ When a public URL is provided, the media is fetched and stored automatically. On
17816
17819
  if (item.url) body.url = item.url;
17817
17820
  if (item.captions) body.captions = item.captions;
17818
17821
  if (item.show_captions !== void 0) body.show_captions = item.show_captions;
17822
+ if (item.fit_media !== void 0) body.fit_media = item.fit_media;
17819
17823
  const result = await callApi(
17820
17824
  `/forms/${form_id}/nodes/${item.node_id}/media`,
17821
17825
  { method: "POST", body }
@@ -18380,7 +18384,7 @@ For multi-render builds (e.g. composition quizzes with a clue + reveal clip per
18380
18384
  inputSchema: {
18381
18385
  compositionId: external_exports.string().describe(`The composition ID. Call clipform_list_compositions to see available options (e.g. ${EXPOSED_COMPOSITIONS.map((id) => `'${id}'`).join(", ")})`),
18382
18386
  outputFormat: external_exports.enum(["mp4", "png"]).default("mp4").describe("Output format (default: mp4)"),
18383
- 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."),
18387
+ 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 (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`."),
18384
18388
  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 immediately - fire all renders first, then poll clipform_check_render. Use false whenever rendering more than one clip.")
18385
18389
  },
18386
18390
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }
@@ -18416,7 +18420,10 @@ For multi-render builds (e.g. composition quizzes with a clue + reveal clip per
18416
18420
  if (!result.ok) return errorResult(result.error);
18417
18421
  const data = result.data;
18418
18422
  return textResult(
18419
- `Render complete. Public URL: ${data.public_url}`
18423
+ [
18424
+ `Render complete. Public URL: ${data.public_url}`,
18425
+ `Attach via clipform_upload_node_media with fit_media: true (composed 9:16 frame - contain, never crop).`
18426
+ ].join("\n")
18420
18427
  );
18421
18428
  }
18422
18429
  );
@@ -18590,6 +18597,8 @@ For multi-question builds, pass wait: false on every render: each call returns a
18590
18597
  })
18591
18598
  ).min(1).max(20).describe("Media items (images, video clips, or a mix)"),
18592
18599
  audio_url: external_exports.string().url().optional().describe("Audio track URL. Video duration matches audio duration."),
18600
+ background_audio_url: external_exports.string().url().optional().describe("Ambience/music bed under the narration (crowd noise, room tone). Loops to fill the video. Find tracks with clipform_search_music."),
18601
+ background_audio_volume: external_exports.number().min(0).max(1).optional().describe("Background bed volume 0-1 (default 0.15 - sits under speech)"),
18593
18602
  duration_seconds: external_exports.number().positive().optional().describe("Video duration in seconds (required if no audio_url)"),
18594
18603
  random_effects: external_exports.boolean().optional().default(true).describe("Shuffle Ken Burns effects across image items (default: true)"),
18595
18604
  transition: external_exports.object({
@@ -18597,23 +18606,40 @@ For multi-question builds, pass wait: false on every render: each call returns a
18597
18606
  duration: external_exports.number().optional().default(1).describe("Transition duration in seconds (default: 1)")
18598
18607
  }).optional(),
18599
18608
  style_preset: STYLE_PRESET_ENUM.optional(),
18609
+ texture: external_exports.object({
18610
+ type: external_exports.enum(["checker", "halftone", "scanlines"]).describe("checker = square dither mesh, halftone = dot grid, scanlines = horizontal lines"),
18611
+ sizePx: external_exports.number().positive().optional().describe("Pattern cell size in px (default 4)"),
18612
+ opacity: external_exports.number().min(0).max(1).optional().describe("Pattern darkness 0-1 (default 0.35)")
18613
+ }).optional().describe("Print-style pattern overlay on image items - makes stock imagery read as designed (screen-print dither look)"),
18614
+ duotone: external_exports.object({
18615
+ shadow: external_exports.string().describe("Darker tone (maps to image shadows), e.g. '#1a1633'"),
18616
+ highlight: external_exports.string().describe("Lighter tone (maps to image highlights), e.g. '#f5c542'"),
18617
+ contrast: external_exports.number().positive().optional().describe("Contrast boost before mapping (default 1.1)")
18618
+ }).optional().describe("Two-tone editorial recolour on image items - desaturates then maps to a shadow->highlight palette. Pair with a halftone texture for a screen-print poster look."),
18600
18619
  background_color: external_exports.string().optional().describe("Background color (default '#000')"),
18601
18620
  wait: external_exports.boolean().optional().default(true).describe("true (default) blocks until the video is ready and returns its URL. false returns a job ID immediately - fire all renders first, then poll clipform_check_render. Use false whenever rendering more than one video.")
18602
18621
  },
18603
18622
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
18604
18623
  },
18605
- async ({ items, audio_url, duration_seconds, random_effects, transition, style_preset, background_color, wait }) => {
18624
+ async ({ items, audio_url, background_audio_url, background_audio_volume, duration_seconds, random_effects, transition, style_preset, texture, duotone, background_color, wait }) => {
18606
18625
  const workspace_id = getMcpAuth()?.workspace_id;
18626
+ const default_style = style_preset || texture || duotone ? {
18627
+ ...style_preset ? { preset: style_preset } : {},
18628
+ ...texture ? { texture } : {},
18629
+ ...duotone ? { duotone } : {}
18630
+ } : void 0;
18607
18631
  const apiCall = () => callApi("/internal/generate-video", {
18608
18632
  timeoutMs: 3e5,
18609
18633
  // local renders pay a cold webpack bundle; Lambda is fast but bursty
18610
18634
  body: {
18611
18635
  items,
18612
18636
  audio_url,
18637
+ background_audio_url,
18638
+ background_audio_volume,
18613
18639
  duration_seconds,
18614
18640
  random_effects: random_effects ?? true,
18615
18641
  transition: transition ?? { type: "fade", duration: 1 },
18616
- default_style: style_preset ? { preset: style_preset } : void 0,
18642
+ default_style,
18617
18643
  background_color,
18618
18644
  workspace_id
18619
18645
  }
@@ -18639,7 +18665,8 @@ For multi-question builds, pass wait: false on every render: each call returns a
18639
18665
  [
18640
18666
  `Video rendered (${items.length} item${items.length > 1 ? "s" : ""}).`,
18641
18667
  `Public URL: ${data.public_url}`,
18642
- data.duration_seconds ? `Duration: ${data.duration_seconds}s` : ""
18668
+ data.duration_seconds ? `Duration: ${data.duration_seconds}s` : "",
18669
+ `Attach via clipform_upload_node_media with fit_media: true (composed 9:16 frame - contain, never crop).`
18643
18670
  ].filter(Boolean).join("\n")
18644
18671
  );
18645
18672
  }
@@ -18685,7 +18712,8 @@ Returns the current status and, when complete, the output URL. Typical render ti
18685
18712
  [
18686
18713
  `Render complete.`,
18687
18714
  ...data.public_url ? [`Public URL: ${data.public_url}`] : [],
18688
- ...data.duration_seconds ? [`Duration: ${data.duration_seconds}s`] : []
18715
+ ...data.duration_seconds ? [`Duration: ${data.duration_seconds}s`] : [],
18716
+ `Attach via clipform_upload_node_media with fit_media: true (composed 9:16 frame - contain, never crop).`
18689
18717
  ].join("\n")
18690
18718
  );
18691
18719
  }
@@ -18718,7 +18746,7 @@ function registerFetchBoundaryTool(server) {
18718
18746
  "clipform_fetch_boundary",
18719
18747
  {
18720
18748
  title: "Fetch Geographic Boundary",
18721
- description: `Fetch a GeoJSON boundary polygon for a country, city, or region. Returns simplified GeoJSON ready to use as the 'boundary' prop in GlobeToCity/CityToGlobe compositions.
18749
+ description: `Fetch a GeoJSON boundary polygon for a country, city, or region. Returns simplified GeoJSON ready to use as the 'boundary' prop in the Map composition.
18722
18750
 
18723
18751
  mainlandOnly excludes small islands and overseas territories (e.g. Corsica for France, Hawaii for USA).`,
18724
18752
  inputSchema: {
@@ -18793,7 +18821,7 @@ mainlandOnly excludes small islands and overseas territories (e.g. Corsica for F
18793
18821
  `Size: ${sizeKb} KB`,
18794
18822
  mainlandOnly ? `Mainland filter: applied (islands < 5% of largest landmass removed)` : `Mainland filter: off`,
18795
18823
  ``,
18796
- `Use this as the boundary.geojson prop in GlobeToCity:`,
18824
+ `Use this as the boundary.geojson prop in Map:`,
18797
18825
  ``,
18798
18826
  "```json",
18799
18827
  geojsonStr,
@@ -18839,8 +18867,8 @@ Quiz variants (optional): ${QUIZ_VARIANTS.join(", ")} - appends variant-specific
18839
18867
  }
18840
18868
  const type = resolved;
18841
18869
  const validVariant = type === "quiz" ? variant : void 0;
18842
- const content = getGuideContent(type, validVariant);
18843
18870
  const uri = getGuideUri(type, validVariant);
18871
+ const content = await fetchGuideText(uri) ?? guideFallbackText(uri);
18844
18872
  return {
18845
18873
  content: [{
18846
18874
  type: "resource",
@@ -18948,6 +18976,10 @@ function createServer(options) {
18948
18976
  );
18949
18977
  const _registerTool = server.registerTool.bind(server);
18950
18978
  server.registerTool = (name, config2, handler) => {
18979
+ const requiredTier = MCP_TOOL_TIERS[name] ?? 0;
18980
+ if (options?.planTier !== void 0 && requiredTier > options.planTier) {
18981
+ return void 0;
18982
+ }
18951
18983
  return _registerTool(
18952
18984
  name,
18953
18985
  config2,
@@ -18998,4 +19030,4 @@ export {
18998
19030
  JSONRPCMessageSchema,
18999
19031
  createServer
19000
19032
  };
19001
- //# sourceMappingURL=chunk-CN4XBVFD.js.map
19033
+ //# sourceMappingURL=chunk-WHNDWLHI.js.map