@clipform/mcp-server 1.45.0 → 1.46.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.
package/README.md CHANGED
@@ -59,7 +59,7 @@ You can also pass the key as a CLI flag: `npx -y @clipform/mcp-server --api-key=
59
59
 
60
60
  ## Tools
61
61
 
62
- Your MCP client lists these automatically on connect (via `tools/list`). Full reference - arguments, schemas, and examples - is in the [docs](https://clipform.io/docs/guides/mcp).
62
+ Your MCP client lists these automatically on connect (via `tools/list`). Full reference - arguments, schemas, and examples - is in the [docs](https://clipform.io/docs/api/mcp).
63
63
 
64
64
  | Tool | Description |
65
65
  |------|-------------|
@@ -107,6 +107,6 @@ Forms are created with a start node and end screen automatically - you just add
107
107
  ## Links
108
108
 
109
109
  - [Clipform](https://clipform.io) - Create interactive video forms
110
- - [Documentation](https://clipform.io/docs/guides/mcp) - Full guide with node types and more
110
+ - [Documentation](https://clipform.io/docs/api/mcp) - Full guide with node types and more
111
111
 
112
112
  > Craft guides (`clipform://guides/*`, `clipform_get_guide`) are fetched from the Clipform API at runtime, so the server needs a reachable `API_URL` and valid `CLIPFORM_API_KEY` to serve them.
@@ -36,6 +36,14 @@ var FEATURES = {
36
36
  branching: {
37
37
  name: "Conditional branching",
38
38
  description: "Choice nodes route to different destinations based on which option is picked",
39
+ enabled: true,
40
+ status: "live",
41
+ wentLiveAt: "2026-06-25",
42
+ category: "builder"
43
+ },
44
+ funnel: {
45
+ name: "Lead qualification funnels",
46
+ description: "Multi-step funnels with conditional routing built on branching",
39
47
  enabled: false,
40
48
  status: "planned",
41
49
  category: "builder"
@@ -153,6 +161,16 @@ var FEATURES = {
153
161
  status: "planned",
154
162
  category: "node-types"
155
163
  },
164
+ node_draw: {
165
+ name: "Drawing",
166
+ description: "Drawing canvas; respondents draw and it is saved as a PNG image",
167
+ // Live (#1012): builder picker shows it, the API accepts it, the viewer renders
168
+ // the canvas, and dashboard results render the saved PNG (#1012 results pass).
169
+ enabled: true,
170
+ status: "live",
171
+ wentLiveAt: "2026-06-27",
172
+ category: "node-types"
173
+ },
156
174
  node_payment: {
157
175
  name: "Payment",
158
176
  description: "Collect payments via Stripe or Paddle inline",
@@ -244,6 +262,7 @@ var FEATURES = {
244
262
  description: "Export response data to CSV, including transcriptions and media links",
245
263
  enabled: true,
246
264
  status: "live",
265
+ wentLiveAt: "2026-06-07",
247
266
  category: "analytics"
248
267
  },
249
268
  tracked_links: {
@@ -273,6 +292,7 @@ var FEATURES = {
273
292
  description: "Embed forms on external websites via script tag or JavaScript API",
274
293
  enabled: true,
275
294
  status: "live",
295
+ wentLiveAt: "2026-05-27",
276
296
  category: "sharing"
277
297
  },
278
298
  embed_widget: {
@@ -322,16 +342,18 @@ var FEATURES = {
322
342
  api_keys: {
323
343
  name: "API key management",
324
344
  description: "Self-service API key creation in the dashboard",
325
- enabled: false,
326
- status: "planned",
345
+ enabled: true,
346
+ status: "live",
347
+ wentLiveAt: "2026-06-22",
327
348
  category: "api"
328
349
  },
329
350
  // -- Integrations --
330
351
  webhooks: {
331
352
  name: "Webhooks",
332
353
  description: "Send response data to external URLs on form completion",
333
- enabled: false,
334
- status: "planned",
354
+ enabled: true,
355
+ status: "live",
356
+ wentLiveAt: "2026-06-24",
335
357
  category: "integrations"
336
358
  },
337
359
  // Per-integration lifecycle (split from the old coarse `integrations` flag so
@@ -382,15 +404,25 @@ var FEATURES = {
382
404
  integration_zapier: {
383
405
  name: "Zapier",
384
406
  description: "Connect to 5,000+ apps and automate workflows",
385
- enabled: false,
386
- status: "planned",
407
+ enabled: true,
408
+ status: "live",
409
+ wentLiveAt: "2026-06-21",
387
410
  category: "integrations"
388
411
  },
389
412
  integration_make: {
390
413
  name: "Make",
391
414
  description: "Advanced automation and integrations",
392
- enabled: false,
393
- status: "planned",
415
+ enabled: true,
416
+ status: "live",
417
+ wentLiveAt: "2026-06-22",
418
+ category: "integrations"
419
+ },
420
+ integration_n8n: {
421
+ name: "n8n",
422
+ description: "Open-source workflow automation - trigger flows on form responses",
423
+ enabled: true,
424
+ status: "live",
425
+ wentLiveAt: "2026-06-26",
394
426
  category: "integrations"
395
427
  },
396
428
  integration_hubspot: {
@@ -421,6 +453,13 @@ var FEATURES = {
421
453
  status: "planned",
422
454
  category: "integrations"
423
455
  },
456
+ integration_google_sheets: {
457
+ name: "Google Sheets",
458
+ description: "Sync form responses to a Google Sheet, one row per submission",
459
+ enabled: false,
460
+ status: "planned",
461
+ category: "integrations"
462
+ },
424
463
  notifications: {
425
464
  name: "Response notifications",
426
465
  description: "Email or webhook alerts when new responses arrive",
@@ -443,6 +482,22 @@ var FEATURES = {
443
482
  category: "integrations"
444
483
  }
445
484
  };
485
+ for (const [key, f] of Object.entries(FEATURES)) {
486
+ if (f.status === "live" && !f.enabled) {
487
+ throw new Error(`FEATURES.${key}: status 'live' requires enabled:true (shipped features must be exposed)`);
488
+ }
489
+ if (f.status === "planned" && f.enabled) {
490
+ throw new Error(`FEATURES.${key}: status 'planned' requires enabled:false (planned features must not be exposed)`);
491
+ }
492
+ if (f.wentLiveAt !== void 0) {
493
+ if (f.status === "planned") {
494
+ throw new Error(`FEATURES.${key}: wentLiveAt set but status is 'planned' (only shipped features have a go-live date)`);
495
+ }
496
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(f.wentLiveAt)) {
497
+ throw new Error(`FEATURES.${key}: wentLiveAt must be 'YYYY-MM-DD' (got ${JSON.stringify(f.wentLiveAt)})`);
498
+ }
499
+ }
500
+ }
446
501
 
447
502
  // ../config/node-types.js
448
503
  var RESPONSE_FORMATS = ["text", "audio", "video"];
@@ -490,7 +545,7 @@ var NODE_TYPES = {
490
545
  show_in_results: true,
491
546
  default_config: {
492
547
  selection_mode: "single",
493
- choice: { enable_branching: true, record_scores: false },
548
+ choice: { enable_branching: false, record_scores: false },
494
549
  randomise_options: false,
495
550
  show_option_count: false,
496
551
  option_display: "list",
@@ -530,13 +585,13 @@ var NODE_TYPES = {
530
585
  type: "string",
531
586
  label: "Selection mode",
532
587
  default: "single",
533
- description: "Allow single or multiple selections"
588
+ description: 'Allow single or multiple selections. "multiple" is gated by the multi_select feature - while it is off, the API coerces it to "single" (the viewer renders single-select only).'
534
589
  },
535
590
  allow_text_response: {
536
591
  type: "boolean",
537
592
  label: "Allow text response",
538
593
  default: false,
539
- description: "Allow free-text response in addition to options (single choice only)"
594
+ description: "Allow free-text response in addition to options (single choice only). Gated by the text_response feature - while it is off, the API coerces it to false (the viewer renders no free-text field yet)."
540
595
  },
541
596
  randomise_options: {
542
597
  type: "boolean",
@@ -677,8 +732,8 @@ var NODE_TYPES = {
677
732
  type: "object",
678
733
  properties: {
679
734
  type: { enum: ["audio", "video"], type: "string" },
680
- // Durable quick-view link to the recording (resolves to a fresh
681
- // signed URL); never the internal storage path. See media-link-token.
735
+ // Direct, time-limited signed URL to the recording (~48h, supports
736
+ // HTTP range); minted per delivery, never the internal storage path.
682
737
  url: { type: "string", format: "uri" }
683
738
  }
684
739
  },
@@ -686,7 +741,19 @@ var NODE_TYPES = {
686
741
  type: "object",
687
742
  properties: {
688
743
  text: { type: "string" },
689
- language: { type: "string" }
744
+ language: { type: "string" },
745
+ segments: {
746
+ type: "array",
747
+ description: "Timestamped segments (seconds) for captions/seeking.",
748
+ items: {
749
+ type: "object",
750
+ properties: {
751
+ start: { type: "number" },
752
+ end: { type: "number" },
753
+ text: { type: "string" }
754
+ }
755
+ }
756
+ }
690
757
  }
691
758
  }
692
759
  }
@@ -983,6 +1050,74 @@ var NODE_TYPES = {
983
1050
  },
984
1051
  output_schema: null
985
1052
  },
1053
+ draw: {
1054
+ label: "Drawing",
1055
+ shorthand: "Draw",
1056
+ icon: "Pencil",
1057
+ color: "#FEF3C7",
1058
+ description: "Drawing canvas; respondents draw and it is saved as a PNG image",
1059
+ category: "input",
1060
+ sort_order: 6.5,
1061
+ has_options: false,
1062
+ is_terminal: false,
1063
+ show_nav_bar: true,
1064
+ loading: "lazy",
1065
+ supports_prompt: false,
1066
+ supports_media: false,
1067
+ is_system: false,
1068
+ show_in_results: true,
1069
+ default_config: {
1070
+ paper_color: "#FAF9F6",
1071
+ stroke_colors: ["#1F2937", "#EF4444", "#3B82F6", "#22C55E", "#FACC15"],
1072
+ stroke_widths: [3, 6, 12]
1073
+ },
1074
+ config_schema: {
1075
+ type: "object",
1076
+ properties: {
1077
+ paper_color: { type: "string", label: "Paper colour", default: "#FAF9F6", description: "Canvas background colour (off-white paper by default)" },
1078
+ stroke_colors: {
1079
+ type: "array",
1080
+ items: { type: "string" },
1081
+ minItems: 1,
1082
+ maxItems: 8,
1083
+ label: "Pen colours",
1084
+ default: ["#1F2937", "#EF4444", "#3B82F6", "#22C55E", "#FACC15"],
1085
+ description: "Colour swatches the respondent can draw with"
1086
+ },
1087
+ stroke_widths: {
1088
+ type: "array",
1089
+ items: { type: "number", minimum: 1, maximum: 60 },
1090
+ minItems: 1,
1091
+ maxItems: 5,
1092
+ label: "Pen thicknesses",
1093
+ default: [3, 6, 12],
1094
+ description: "Stroke widths (px) the respondent can choose"
1095
+ }
1096
+ }
1097
+ },
1098
+ response_schema: {
1099
+ // Mirrors the live `open` node's media response shape (the only complete
1100
+ // media path): the sketch image is uploaded to the `media-responses`
1101
+ // bucket via the same upload-client pipeline and referenced by
1102
+ // storage_path. Exported as PNG (crisp line art); the off-white paper
1103
+ // background is composited in before export so it is baked into the file.
1104
+ type: "object",
1105
+ required: ["media"],
1106
+ properties: {
1107
+ media: {
1108
+ type: "object",
1109
+ required: ["storage_path", "type"],
1110
+ properties: {
1111
+ type: { enum: ["image"], type: "string", description: 'Always "image" for a sketch' },
1112
+ storage_path: { type: "string", description: "Path in the media-responses bucket: bucket/workspace_id/.../uuid.png" },
1113
+ mime_type: { type: "string", description: "Image MIME type (image/png)" }
1114
+ },
1115
+ description: "The exported sketch, stored as a single image in media-responses"
1116
+ }
1117
+ }
1118
+ },
1119
+ output_schema: null
1120
+ },
986
1121
  payment: {
987
1122
  label: "Payment",
988
1123
  shorthand: "Payment",
@@ -1084,7 +1219,7 @@ var NODE_TYPES = {
1084
1219
  is_system: false,
1085
1220
  show_in_results: true,
1086
1221
  default_config: {
1087
- choice: { enable_branching: true },
1222
+ choice: { enable_branching: false },
1088
1223
  options: [
1089
1224
  { content: "Yes" },
1090
1225
  { content: "No" }
@@ -1441,6 +1576,7 @@ for (const [type, def] of Object.entries(NODE_TYPES)) {
1441
1576
  def.is_active = feature.enabled;
1442
1577
  }
1443
1578
  var NODE_TYPES_LIST = Object.entries(NODE_TYPES).map(([type, def]) => ({ type, ...def })).sort((a, b) => a.sort_order - b.sort_order);
1579
+ var ACTIVE_NODE_TYPES = Object.entries(NODE_TYPES).filter(([, def]) => def.is_active && !def.is_system).map(([type]) => type);
1444
1580
  var NODE_TYPE_KEYS = Object.keys(NODE_TYPES);
1445
1581
  var RESPONSE_SCHEMAS = Object.fromEntries(
1446
1582
  Object.entries(NODE_TYPES).map(([k, v]) => [k, v.response_schema])
@@ -1566,7 +1702,7 @@ var FORM_TYPES = {
1566
1702
  category: "qualification",
1567
1703
  aliases: ["lead-gen", "qualification", "lead-magnet"],
1568
1704
  variants: [],
1569
- required_features: ["branching"],
1705
+ required_features: ["branching", "funnel"],
1570
1706
  required_node_types: ["choice", "contact", "end_screen"],
1571
1707
  default_media_style: "text",
1572
1708
  guide_uri: "clipform://guides/funnel",
@@ -1680,11 +1816,11 @@ function getDiscoveryParams(formType, variant, mode = "full") {
1680
1816
 
1681
1817
  // ../config/help-links.js
1682
1818
  var HELP_TOPICS = {
1683
- goLive: { path: "/guides/sharing", anchor: "go-live" },
1684
- cantGoLive: { path: "/guides/sharing", anchor: "cant-go-live" },
1685
- liveAndDraft: { path: "/guides/managing-forms", anchor: "live-and-draft" },
1686
- publishingChanges: { path: "/guides/managing-forms", anchor: "publishing-changes" },
1687
- embedding: { path: "/guides/embed", anchor: null }
1819
+ goLive: { path: "/help/sharing", anchor: "go-live" },
1820
+ cantGoLive: { path: "/help/sharing", anchor: "cant-go-live" },
1821
+ liveAndDraft: { path: "/help/managing-forms", anchor: "live-and-draft" },
1822
+ publishingChanges: { path: "/help/managing-forms", anchor: "publishing-changes" },
1823
+ embedding: { path: "/help/embed", anchor: null }
1688
1824
  };
1689
1825
  function buildHelpLinks(docsBaseUrl) {
1690
1826
  const links = {};
@@ -1707,10 +1843,27 @@ var ENV = {
1707
1843
  };
1708
1844
  var COOKIE_DOMAIN = ENV.isDevelopment ? void 0 : ".clipform.io";
1709
1845
  var isTunnelBrowser = isBrowser && window.location.hostname.endsWith(".smith-forge.com");
1846
+ var PROD_URLS = {
1847
+ marketing: "https://www.clipform.io",
1848
+ // Dedicated CDN host for the embed SDK, decoupled from the marketing deploy
1849
+ // (ships in seconds via packages/embed-sdk `deploy:cf`). www.clipform.io/embed.js
1850
+ // stays as the legacy bridge until all installs migrate here.
1851
+ embed: "https://js.clipform.io",
1852
+ dashboard: "https://app.clipform.io",
1853
+ viewer: "https://clipform.io",
1854
+ api: "https://api.clipform.io",
1855
+ // Dedicated subdomain for the remote MCP server. Same Render service
1856
+ // as `api`, just routed by Host header. Token audience is bound here.
1857
+ mcp: "https://mcp.clipform.io",
1858
+ docs: "https://clipform.io/docs"
1859
+ };
1710
1860
  function getUrls() {
1711
1861
  if (isTunnelBrowser) {
1712
1862
  return {
1713
1863
  marketing: "https://marketing-dev.smith-forge.com",
1864
+ // Dev/tunnel: the marketing dev server 308-redirects /embed.js to js.clipform.io
1865
+ // (public/embed.js was retired in #1288), so embeds load the live SDK.
1866
+ embed: "https://marketing-dev.smith-forge.com",
1714
1867
  dashboard: "https://dashboard-dev.smith-forge.com",
1715
1868
  viewer: "https://viewer-dev.smith-forge.com",
1716
1869
  api: "https://api-dev.smith-forge.com",
@@ -1722,6 +1875,9 @@ function getUrls() {
1722
1875
  const api = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3003";
1723
1876
  return {
1724
1877
  marketing: process.env.NEXT_PUBLIC_MARKETING_URL || "http://localhost:3002",
1878
+ // Local: the marketing dev server redirects /embed.js to js.clipform.io
1879
+ // (public/embed.js was retired in #1288), so embeds load the live SDK.
1880
+ embed: process.env.NEXT_PUBLIC_MARKETING_URL || "http://localhost:3002",
1725
1881
  dashboard: process.env.NEXT_PUBLIC_DASHBOARD_URL || "http://localhost:3000",
1726
1882
  viewer: process.env.NEXT_PUBLIC_VIEWER_URL || "http://localhost:3001",
1727
1883
  api,
@@ -1732,16 +1888,7 @@ function getUrls() {
1732
1888
  docs: process.env.NEXT_PUBLIC_DOCS_URL || "http://localhost:3004/docs"
1733
1889
  };
1734
1890
  }
1735
- return {
1736
- marketing: "https://www.clipform.io",
1737
- dashboard: "https://app.clipform.io",
1738
- viewer: "https://clipform.io",
1739
- api: "https://api.clipform.io",
1740
- // Dedicated subdomain for the remote MCP server. Same Render service
1741
- // as `api`, just routed by Host header. Token audience is bound here.
1742
- mcp: "https://mcp.clipform.io",
1743
- docs: "https://clipform.io/docs"
1744
- };
1891
+ return { ...PROD_URLS };
1745
1892
  }
1746
1893
  var BUSINESS = {
1747
1894
  name: "Clipform",
@@ -1756,6 +1903,7 @@ var BUSINESS = {
1756
1903
  },
1757
1904
  urls: getUrls()
1758
1905
  };
1906
+ var MEDIA_DIRECT_MAX_BYTES = 10 * 1024 * 1024;
1759
1907
  var HELP_LINKS = buildHelpLinks(BUSINESS.urls.docs);
1760
1908
  var CONTACT_FIELDS = [
1761
1909
  { id: "first_name", label: "First Name", type: "text", placeholder: "Enter first name", order: 1 },
@@ -2046,6 +2194,7 @@ async function getSessionContext() {
2046
2194
 
2047
2195
  export {
2048
2196
  NODE_TYPES,
2197
+ ACTIVE_NODE_TYPES,
2049
2198
  NON_COUNTABLE_TYPES,
2050
2199
  FORM_TYPE_KEYS,
2051
2200
  WORKFLOW_TYPES,
@@ -2070,4 +2219,4 @@ export {
2070
2219
  getSessionContextWithAuth,
2071
2220
  getSessionContext
2072
2221
  };
2073
- //# sourceMappingURL=chunk-2DQUET75.js.map
2222
+ //# sourceMappingURL=chunk-AHWMH66L.js.map