@clipform/mcp-server 1.13.1 → 1.16.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.
@@ -2,8 +2,9 @@ import {
2
2
  __commonJS,
3
3
  __export,
4
4
  __toESM,
5
- getMcpAuth
6
- } from "./chunk-YEDU3G7I.js";
5
+ getMcpAuth,
6
+ runWithMcpTool
7
+ } from "./chunk-VWGIVJMQ.js";
7
8
 
8
9
  // ../../node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/codegen/code.js
9
10
  var require_code = __commonJS({
@@ -20906,7 +20907,7 @@ var NODE_TYPES = {
20906
20907
  shorthand: "Start",
20907
20908
  icon: "ArrowRight",
20908
20909
  color: "#D1FAE5",
20909
- description: "Entry point for the form - connects to the first question",
20910
+ description: "Entry point for the form - connects to the first node",
20910
20911
  category: "start",
20911
20912
  sort_order: 0,
20912
20913
  has_options: false,
@@ -20917,6 +20918,7 @@ var NODE_TYPES = {
20917
20918
  supports_media: false,
20918
20919
  is_system: true,
20919
20920
  is_active: true,
20921
+ show_in_results: false,
20920
20922
  default_config: null,
20921
20923
  config_schema: null,
20922
20924
  response_schema: null,
@@ -20927,11 +20929,12 @@ var NODE_TYPES = {
20927
20929
  shorthand: "Multi",
20928
20930
  icon: "CheckSquare",
20929
20931
  color: "#DBEAFE",
20930
- description: "Single or multiple choice questions with predefined options",
20931
- category: "question",
20932
+ description: "Single or multiple choice node with predefined options",
20933
+ category: "input",
20932
20934
  sort_order: 1,
20933
20935
  has_options: true,
20934
20936
  min_options: 1,
20937
+ max_options: 6,
20935
20938
  max_option_length: 36,
20936
20939
  is_terminal: false,
20937
20940
  show_nav_bar: true,
@@ -20940,6 +20943,7 @@ var NODE_TYPES = {
20940
20943
  supports_media: true,
20941
20944
  is_system: false,
20942
20945
  is_active: true,
20946
+ show_in_results: true,
20943
20947
  default_config: {
20944
20948
  selection_mode: "single",
20945
20949
  choice: { enable_branching: true, record_scores: false },
@@ -21005,7 +21009,7 @@ var NODE_TYPES = {
21005
21009
  enum: ["upload", "recorded"],
21006
21010
  type: "string",
21007
21011
  label: "Content media type",
21008
- description: "How the question media was provided"
21012
+ description: "How the node media was provided"
21009
21013
  }
21010
21014
  }
21011
21015
  },
@@ -21029,7 +21033,7 @@ var NODE_TYPES = {
21029
21033
  icon: "Type",
21030
21034
  color: "#DBEAFE",
21031
21035
  description: "Free-form text responses from users",
21032
- category: "question",
21036
+ category: "input",
21033
21037
  sort_order: 2,
21034
21038
  has_options: false,
21035
21039
  is_terminal: false,
@@ -21039,6 +21043,7 @@ var NODE_TYPES = {
21039
21043
  supports_media: true,
21040
21044
  is_system: false,
21041
21045
  is_active: true,
21046
+ show_in_results: true,
21042
21047
  default_config: {
21043
21048
  formats: [
21044
21049
  { order: 0, format: "text" },
@@ -21065,7 +21070,7 @@ var NODE_TYPES = {
21065
21070
  enum: ["upload", "recorded"],
21066
21071
  type: "string",
21067
21072
  label: "Content media type",
21068
- description: "How the question media was provided"
21073
+ description: "How the node media was provided"
21069
21074
  }
21070
21075
  }
21071
21076
  },
@@ -21081,7 +21086,17 @@ var NODE_TYPES = {
21081
21086
  type: "object",
21082
21087
  properties: {
21083
21088
  type: { enum: ["audio", "video"], type: "string" },
21084
- storage_path: { type: "string" }
21089
+ storage_path: { type: "string" },
21090
+ facing_mode: { enum: ["user", "environment"], type: "string" },
21091
+ preview_crop: {
21092
+ type: "object",
21093
+ properties: {
21094
+ stream_width: { type: "number" },
21095
+ stream_height: { type: "number" },
21096
+ preview_width: { type: "number" },
21097
+ preview_height: { type: "number" }
21098
+ }
21099
+ }
21085
21100
  },
21086
21101
  description: "Media response (when response_type is audio or video)"
21087
21102
  },
@@ -21111,7 +21126,17 @@ var NODE_TYPES = {
21111
21126
  type: "object",
21112
21127
  properties: {
21113
21128
  type: { enum: ["audio", "video"], type: "string" },
21114
- storage_path: { type: "string" }
21129
+ storage_path: { type: "string" },
21130
+ facing_mode: { enum: ["user", "environment"], type: "string" },
21131
+ preview_crop: {
21132
+ type: "object",
21133
+ properties: {
21134
+ stream_width: { type: "number" },
21135
+ stream_height: { type: "number" },
21136
+ preview_width: { type: "number" },
21137
+ preview_height: { type: "number" }
21138
+ }
21139
+ }
21115
21140
  }
21116
21141
  },
21117
21142
  transcription: {
@@ -21129,8 +21154,8 @@ var NODE_TYPES = {
21129
21154
  shorthand: "Scale",
21130
21155
  icon: "BarChart3",
21131
21156
  color: "#DBEAFE",
21132
- description: "Numerical rating or scale questions (1-10, etc.)",
21133
- category: "question",
21157
+ description: "Numerical rating or scale node (1-10, etc.)",
21158
+ category: "input",
21134
21159
  sort_order: 3,
21135
21160
  has_options: false,
21136
21161
  is_terminal: false,
@@ -21140,6 +21165,7 @@ var NODE_TYPES = {
21140
21165
  supports_media: true,
21141
21166
  is_system: false,
21142
21167
  is_active: false,
21168
+ show_in_results: true,
21143
21169
  default_config: null,
21144
21170
  config_schema: {
21145
21171
  type: "object",
@@ -21174,6 +21200,7 @@ var NODE_TYPES = {
21174
21200
  supports_media: false,
21175
21201
  is_system: false,
21176
21202
  is_active: false,
21203
+ show_in_results: true,
21177
21204
  default_config: null,
21178
21205
  config_schema: {
21179
21206
  type: "object",
@@ -21205,7 +21232,7 @@ var NODE_TYPES = {
21205
21232
  }
21206
21233
  }
21207
21234
  ],
21208
- description: "Booking provider configuration (one provider per question)"
21235
+ description: "Booking provider configuration (one provider per node)"
21209
21236
  },
21210
21237
  response_schema: {
21211
21238
  type: "object",
@@ -21248,6 +21275,7 @@ var NODE_TYPES = {
21248
21275
  supports_media: false,
21249
21276
  is_system: false,
21250
21277
  is_active: true,
21278
+ show_in_results: false,
21251
21279
  default_config: {
21252
21280
  contact: {
21253
21281
  fields: [
@@ -21257,7 +21285,7 @@ var NODE_TYPES = {
21257
21285
  prompt: ""
21258
21286
  },
21259
21287
  consent_items: [
21260
- { id: "default-consent", label: "I agree to the privacy policy and terms of service", order: 0, required: true }
21288
+ { id: "default-consent", name: "Privacy policy", label: "I agree to the privacy policy and terms of service", order: 0, type: "consent" }
21261
21289
  ]
21262
21290
  },
21263
21291
  config_schema: {
@@ -21286,9 +21314,10 @@ var NODE_TYPES = {
21286
21314
  type: "object",
21287
21315
  properties: {
21288
21316
  id: { type: "string" },
21317
+ name: { type: "string" },
21289
21318
  label: { type: "string" },
21290
21319
  order: { type: "number" },
21291
- required: { type: "boolean" }
21320
+ type: { type: "string", enum: ["consent", "opt_in"] }
21292
21321
  }
21293
21322
  }
21294
21323
  }
@@ -21316,11 +21345,13 @@ var NODE_TYPES = {
21316
21345
  type: "array",
21317
21346
  items: {
21318
21347
  type: "object",
21319
- required: ["id", "label", "accepted"],
21348
+ required: ["id", "name", "label", "accepted", "type"],
21320
21349
  properties: {
21321
21350
  id: { type: "string" },
21351
+ name: { type: "string" },
21322
21352
  label: { type: "string" },
21323
- accepted: { type: "boolean" }
21353
+ accepted: { type: "boolean" },
21354
+ type: { type: "string", enum: ["consent", "opt_in"] }
21324
21355
  }
21325
21356
  },
21326
21357
  description: "Consent checkbox entries"
@@ -21362,6 +21393,7 @@ var NODE_TYPES = {
21362
21393
  supports_media: false,
21363
21394
  is_system: false,
21364
21395
  is_active: false,
21396
+ show_in_results: true,
21365
21397
  default_config: null,
21366
21398
  config_schema: {
21367
21399
  type: "object",
@@ -21399,7 +21431,7 @@ var NODE_TYPES = {
21399
21431
  size: { type: "integer", minimum: 1, description: "File size in bytes" },
21400
21432
  mime_type: { type: "string", description: "File MIME type" },
21401
21433
  uploaded_at: { type: "string", format: "date-time", description: "ISO timestamp of upload completion" },
21402
- storage_path: { type: "string", description: "Full storage path: workspace_id/form_id/question_id/uuid.ext" }
21434
+ storage_path: { type: "string", description: "Full storage path: workspace_id/form_id/node_id/uuid.ext" }
21403
21435
  }
21404
21436
  },
21405
21437
  minItems: 1,
@@ -21425,6 +21457,7 @@ var NODE_TYPES = {
21425
21457
  supports_media: false,
21426
21458
  is_system: false,
21427
21459
  is_active: false,
21460
+ show_in_results: true,
21428
21461
  default_config: { amount: null, currency: "usd", provider: "stripe" },
21429
21462
  config_schema: {
21430
21463
  type: "object",
@@ -21496,7 +21529,7 @@ var NODE_TYPES = {
21496
21529
  icon: "CircleCheck",
21497
21530
  color: "#D1FAE5",
21498
21531
  description: "Two-option choice - yes/no, true/false, or this vs that",
21499
- category: "question",
21532
+ category: "input",
21500
21533
  sort_order: 1.5,
21501
21534
  has_options: true,
21502
21535
  min_options: 2,
@@ -21509,6 +21542,7 @@ var NODE_TYPES = {
21509
21542
  supports_media: true,
21510
21543
  is_system: false,
21511
21544
  is_active: false,
21545
+ show_in_results: true,
21512
21546
  default_config: {
21513
21547
  choice: { enable_branching: true },
21514
21548
  options: [
@@ -21534,7 +21568,7 @@ var NODE_TYPES = {
21534
21568
  enum: ["upload", "recorded"],
21535
21569
  type: "string",
21536
21570
  label: "Content media type",
21537
- description: "How the question media was provided"
21571
+ description: "How the node media was provided"
21538
21572
  }
21539
21573
  }
21540
21574
  },
@@ -21558,7 +21592,7 @@ var NODE_TYPES = {
21558
21592
  icon: "RectangleHorizontal",
21559
21593
  color: "#DBEAFE",
21560
21594
  description: "Simple button for acknowledgment or navigation",
21561
- category: "question",
21595
+ category: "input",
21562
21596
  sort_order: 3,
21563
21597
  has_options: true,
21564
21598
  min_options: 1,
@@ -21571,6 +21605,7 @@ var NODE_TYPES = {
21571
21605
  supports_media: true,
21572
21606
  is_system: false,
21573
21607
  is_active: true,
21608
+ show_in_results: false,
21574
21609
  default_config: { options: [{ content: "Continue" }] },
21575
21610
  config_schema: {
21576
21611
  type: "object",
@@ -21609,6 +21644,7 @@ var NODE_TYPES = {
21609
21644
  supports_media: false,
21610
21645
  is_system: false,
21611
21646
  is_active: true,
21647
+ show_in_results: false,
21612
21648
  default_config: { url: "", auto_redirect: true },
21613
21649
  config_schema: {
21614
21650
  type: "object",
@@ -21639,6 +21675,7 @@ var NODE_TYPES = {
21639
21675
  supports_media: false,
21640
21676
  is_system: false,
21641
21677
  is_active: false,
21678
+ show_in_results: false,
21642
21679
  default_config: { links: [{ id: "default", url: "", title: "", description: "", order: 0 }], auto_redirect: false },
21643
21680
  config_schema: {
21644
21681
  type: "object",
@@ -21684,6 +21721,7 @@ var NODE_TYPES = {
21684
21721
  supports_media: false,
21685
21722
  is_system: false,
21686
21723
  is_active: false,
21724
+ show_in_results: true,
21687
21725
  default_config: null,
21688
21726
  config_schema: {
21689
21727
  type: "object",
@@ -21742,6 +21780,7 @@ var NODE_TYPES = {
21742
21780
  supports_media: false,
21743
21781
  is_system: false,
21744
21782
  is_active: false,
21783
+ show_in_results: false,
21745
21784
  default_config: { provider: "shopify", cta_mode: "checkout", source_mode: "manual" },
21746
21785
  config_schema: {
21747
21786
  type: "object",
@@ -21795,7 +21834,7 @@ var NODE_TYPES = {
21795
21834
  output_schema: null
21796
21835
  },
21797
21836
  end_screen: {
21798
- label: "End Screen",
21837
+ label: "Ending",
21799
21838
  shorthand: "End",
21800
21839
  icon: "Flag",
21801
21840
  color: "#FEE2E2",
@@ -21807,9 +21846,10 @@ var NODE_TYPES = {
21807
21846
  show_nav_bar: false,
21808
21847
  loading: "eager",
21809
21848
  supports_prompt: false,
21810
- supports_media: true,
21849
+ supports_media: false,
21811
21850
  is_system: false,
21812
21851
  is_active: true,
21852
+ show_in_results: false,
21813
21853
  default_config: null,
21814
21854
  config_schema: {
21815
21855
  type: "object",
@@ -21883,6 +21923,7 @@ var NODE_TYPE_METADATA = Object.fromEntries(
21883
21923
  var TERMINAL_TYPES = Object.entries(NODE_TYPES).filter(([, v]) => v.is_terminal).map(([k]) => k);
21884
21924
  var NON_COUNTABLE_TYPES = Object.entries(NODE_TYPES).filter(([, v]) => v.is_system || v.is_terminal).map(([k]) => k);
21885
21925
  var NON_COUNTABLE_FILTER = `(${NON_COUNTABLE_TYPES.map((t) => `"${t}"`).join(",")})`;
21926
+ var RESULTS_EXCLUDED_TYPES = Object.entries(NODE_TYPES).filter(([, v]) => !v.show_in_results).map(([k]) => k);
21886
21927
 
21887
21928
  // ../config/integration-catalog.js
21888
21929
  var INTEGRATION_CATALOG = {
@@ -22235,6 +22276,7 @@ async function callApi(path, options = {}) {
22235
22276
  headers["Authorization"] = `Bearer ${INTERNAL_SECRET}`;
22236
22277
  headers["X-Mcp-User"] = mcpAuth.user_id;
22237
22278
  headers["X-Mcp-Workspace"] = mcpAuth.workspace_id;
22279
+ if (mcpAuth.tool_name) headers["X-Mcp-Tool"] = mcpAuth.tool_name;
22238
22280
  } else if (_apiKey) {
22239
22281
  headers["Authorization"] = `Bearer ${_apiKey}`;
22240
22282
  }
@@ -22282,6 +22324,7 @@ async function callInternalApi(path, options = {}) {
22282
22324
  headers["Authorization"] = `Bearer ${INTERNAL_SECRET}`;
22283
22325
  headers["X-Mcp-User"] = mcpAuth.user_id;
22284
22326
  headers["X-Mcp-Workspace"] = mcpAuth.workspace_id;
22327
+ if (mcpAuth.tool_name) headers["X-Mcp-Tool"] = mcpAuth.tool_name;
22285
22328
  } else if (_apiKey) {
22286
22329
  headers["Authorization"] = `Bearer ${_apiKey}`;
22287
22330
  } else if (INTERNAL_SECRET) {
@@ -22340,10 +22383,12 @@ ${NODE_TYPES_DESCRIPTION}
22340
22383
 
22341
22384
  All type definitions and config schemas are derived from @vid-master/config (answer-types). Refer to the config descriptions above for the correct keys and shapes.
22342
22385
 
22386
+ Fields marked AI-PROTECTED must only be set when the user explicitly requests them. Do not invent values for font, branding, or embed settings.
22387
+
22343
22388
  Example: A form that asks a question, collects contact info, then finishes:
22344
22389
  {
22345
22390
  title: "Quick Survey",
22346
- questions: [
22391
+ nodes: [
22347
22392
  { type: "open", prompt: "What's your biggest challenge?" },
22348
22393
  { type: "contact", prompt: "Leave your details", config: { fields: [{ id: "first_name", required: true }, { id: "email", required: true }] } },
22349
22394
  { type: "end_screen", prompt: "Thanks for your response!" }
@@ -22351,13 +22396,13 @@ Example: A form that asks a question, collects contact info, then finishes:
22351
22396
  }`,
22352
22397
  inputSchema: {
22353
22398
  title: external_exports.string().describe("Form title"),
22354
- questions: external_exports.array(NodeSchema).min(1).describe("Ordered list of questions/steps"),
22399
+ nodes: external_exports.array(NodeSchema).min(1).describe("Ordered list of nodes/steps for the form"),
22355
22400
  show_step_counter: external_exports.boolean().optional().describe("Show step counter (e.g. '1/5'). Set true for quizzes."),
22356
22401
  disable_back_navigation: external_exports.boolean().optional().describe("Prevent going back. Set true for quizzes."),
22357
- primary_color: external_exports.string().optional().describe("Primary/brand color (hex or CSS color). Used for buttons and accents."),
22358
- background_color: external_exports.string().optional().describe("Background color (hex, rgba, or CSS color)."),
22359
- font_family: external_exports.string().optional().describe("Font family name (e.g. 'Inter', 'Roboto', 'Playfair Display')."),
22360
- embed_autoplay: external_exports.boolean().optional().describe("Auto-play video when embedded (default: false). When off, embeds show a thumbnail + play button."),
22402
+ primary_color: external_exports.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().describe("Primary/brand color as 6-digit hex (e.g. '#FF5500'). Used for buttons and accents."),
22403
+ background_color: external_exports.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().describe("Background color as 6-digit hex (e.g. '#1A1A2E')."),
22404
+ font_family: external_exports.string().optional().describe("AI-PROTECTED: Only set when the user explicitly requests a specific font."),
22405
+ embed_autoplay: external_exports.boolean().optional().describe("AI-PROTECTED: Only set when the user explicitly requests autoplay. Default: false."),
22361
22406
  tags: external_exports.array(external_exports.string()).optional().describe("Tags for indexing (e.g. ['quiz', 'trivia', 'arsenal']). Include format, genre, and topics.")
22362
22407
  },
22363
22408
  annotations: {
@@ -22367,7 +22412,7 @@ Example: A form that asks a question, collects contact info, then finishes:
22367
22412
  openWorldHint: true
22368
22413
  }
22369
22414
  },
22370
- async ({ title, questions, show_step_counter, disable_back_navigation, primary_color, background_color, font_family, embed_autoplay, tags }) => {
22415
+ async ({ title, nodes, show_step_counter, disable_back_navigation, primary_color, background_color, font_family, embed_autoplay, tags }) => {
22371
22416
  let planContext = null;
22372
22417
  const meResult = await callApi("/me", { method: "GET" });
22373
22418
  if (!meResult.ok) {
@@ -22387,10 +22432,10 @@ Example: A form that asks a question, collects contact info, then finishes:
22387
22432
  plan_name: me.plan?.name ?? "Free",
22388
22433
  node_limit: me.plan?.node_limit ?? null
22389
22434
  };
22390
- const contentCount = questions.filter((q) => q.type !== "end_screen").length;
22435
+ const contentCount = nodes.filter((q) => q.type !== "end_screen").length;
22391
22436
  if (planContext.node_limit !== null && contentCount > planContext.node_limit) {
22392
22437
  const upgradeUrl = `${BUSINESS.urls.dashboard}/billing`;
22393
- const message = planContext.auth_mode === "oauth" ? `Your '${planContext.workspace_name}' workspace is on the ${planContext.plan_name} plan, capped at ${planContext.node_limit} questions per form. You asked for ${contentCount}. Either rerun with ${planContext.node_limit} questions or upgrade at ${upgradeUrl}.` : `Anonymous sessions are capped at ${planContext.node_limit} questions per form (${planContext.plan_name} plan). You asked for ${contentCount}. Either rerun with ${planContext.node_limit} questions, or connect your Clipform account in claude.ai \u2192 Settings \u2192 Connectors so forms land in your workspace with your real plan limits.`;
22438
+ const message = planContext.auth_mode === "oauth" ? `Your '${planContext.workspace_name}' workspace is on the ${planContext.plan_name} plan, capped at ${planContext.node_limit} nodes per form. You asked for ${contentCount}. Either rerun with ${planContext.node_limit} nodes or upgrade at ${upgradeUrl}.` : `Anonymous sessions are capped at ${planContext.node_limit} nodes per form (${planContext.plan_name} plan). You asked for ${contentCount}. Either rerun with ${planContext.node_limit} nodes, or connect your Clipform account in claude.ai \u2192 Settings \u2192 Connectors so forms land in your workspace with your real plan limits.`;
22394
22439
  return errorResult(message);
22395
22440
  }
22396
22441
  const createResult = await callApi("/forms", {
@@ -22416,34 +22461,18 @@ Example: A form that asks a question, collects contact info, then finishes:
22416
22461
  body: settingsBody
22417
22462
  });
22418
22463
  }
22419
- for (const q of questions) {
22420
- if (q.type === "end_screen") {
22421
- const getResult = await callApi(`/forms/${formId}`, {
22422
- method: "GET"
22423
- });
22424
- if (getResult.ok) {
22425
- const questions_list = getResult.data.questions;
22426
- const endScreen = questions_list?.find((qq) => qq.type === "end_screen");
22427
- if (endScreen) {
22428
- await callApi(`/forms/${formId}/nodes/${endScreen.id}`, {
22429
- method: "PATCH",
22430
- body: { prompt: q.prompt, ...q.config ? { config: q.config } : {} }
22431
- });
22432
- continue;
22433
- }
22434
- }
22435
- }
22464
+ for (const q of nodes) {
22436
22465
  const addResult = await callApi(`/forms/${formId}/nodes`, {
22437
22466
  method: "POST",
22438
- body: { question: q }
22467
+ body: { node: q }
22439
22468
  });
22440
22469
  if (!addResult.ok) {
22441
- return errorResult(`Failed to add question "${q.prompt}": ${addResult.error}`);
22470
+ return errorResult(`Failed to add node "${q.prompt}": ${addResult.error}`);
22442
22471
  }
22443
22472
  }
22444
22473
  await callApi(`/forms/${formId}`, {
22445
22474
  method: "PATCH",
22446
- body: { is_published: true }
22475
+ body: { is_live: true }
22447
22476
  });
22448
22477
  if (tags && tags.length > 0) {
22449
22478
  await callApi(`/forms/${formId}/tags`, {
@@ -22455,7 +22484,7 @@ Example: A form that asks a question, collects contact info, then finishes:
22455
22484
  `Form created successfully!`,
22456
22485
  ``,
22457
22486
  `Title: ${title}`,
22458
- `Questions: ${questions.length}`,
22487
+ `Nodes: ${nodes.length}`,
22459
22488
  `Form ID: ${formId}`,
22460
22489
  ``,
22461
22490
  `FORM URL (share this with respondents): ${data.viewer_url}`
@@ -22469,7 +22498,7 @@ Example: A form that asks a question, collects contact info, then finishes:
22469
22498
  `Pass form_id on follow-up tools (get_form, add_node, upload_node_media, etc.).`
22470
22499
  );
22471
22500
  if (planContext) {
22472
- const limitNote = planContext.node_limit === null ? "unlimited questions" : `up to ${planContext.node_limit} questions per form`;
22501
+ const limitNote = planContext.node_limit === null ? "unlimited nodes" : `up to ${planContext.node_limit} nodes per form`;
22473
22502
  if (planContext.auth_mode === "oauth") {
22474
22503
  lines.push(
22475
22504
  ``,
@@ -22534,7 +22563,7 @@ function registerListFormsTool(server) {
22534
22563
  const lines = [`Found ${data.forms.length} form(s):
22535
22564
  `];
22536
22565
  for (const f of data.forms) {
22537
- const status = f.is_published ? "published" : "draft";
22566
+ const status = f.is_live ? "live" : "draft";
22538
22567
  const tagStr = f.tags.length > 0 ? ` [${f.tags.map((t) => t.name).join(", ")}]` : "";
22539
22568
  lines.push(`- **${f.title || "(untitled)"}** [${status}]${tagStr}`);
22540
22569
  lines.push(` ID: ${f.id}`);
@@ -22554,16 +22583,16 @@ function registerListFormsTool(server) {
22554
22583
 
22555
22584
  // src/lib/format-form.ts
22556
22585
  function formatFormState(data) {
22557
- const questions = data.questions;
22586
+ const nodes = data.nodes;
22558
22587
  const lines = [
22559
22588
  `Form: ${data.title}`,
22560
22589
  `Form ID: ${data.form_id}`,
22561
- `Published: ${data.is_published}`,
22590
+ `Live: ${data.is_live}`,
22562
22591
  ``,
22563
22592
  `Nodes (in order):`
22564
22593
  ];
22565
- for (let i = 0; i < questions.length; i++) {
22566
- const q = questions[i];
22594
+ for (let i = 0; i < nodes.length; i++) {
22595
+ const q = nodes[i];
22567
22596
  lines.push(` ${i + 1}. [${q.type}] ${q.prompt || "(no prompt)"}`);
22568
22597
  lines.push(` Node ID: ${q.id}`);
22569
22598
  if (q.required) lines.push(` Required: yes`);
@@ -22590,7 +22619,7 @@ function registerGetFormTool(server) {
22590
22619
  "clipform_get_form",
22591
22620
  {
22592
22621
  title: "Get Clipform",
22593
- description: `Retrieve a form's details including all questions in sequential order. Use this to see the current state of a form before making changes.`,
22622
+ description: `Retrieve a form's details including all nodes in sequential order. Use this to see the current state of a form before making changes.`,
22594
22623
  inputSchema: {
22595
22624
  form_id: external_exports.string().uuid().describe("The form UUID (returned by clipform_create_form, not the short share_id from the URL)")
22596
22625
  },
@@ -22617,22 +22646,22 @@ function registerUpdateFormTool(server) {
22617
22646
  "clipform_update_form",
22618
22647
  {
22619
22648
  title: "Update Clipform",
22620
- description: `Update a form's title, publish status, settings, or tags. Use clipform_get_form first to see current values.`,
22649
+ description: `Update a form's title, publish status, settings, or tags. Use clipform_get_form first to see current values. Fields marked AI-PROTECTED must only be set when the user explicitly requests them.`,
22621
22650
  inputSchema: {
22622
22651
  form_id: external_exports.string().uuid().describe("The form UUID (returned by clipform_create_form, not the short share_id from the URL)"),
22623
22652
  title: external_exports.string().optional().describe("New form title"),
22624
- is_published: external_exports.boolean().optional().describe("Set to true to publish, false to unpublish"),
22653
+ is_live: external_exports.boolean().optional().describe("AI-PROTECTED: Only change live state when the user explicitly asks. Do not auto-publish."),
22625
22654
  show_step_counter: external_exports.boolean().optional().describe("Show step counter (e.g. '1/5'). Recommended for quizzes."),
22626
22655
  disable_back_navigation: external_exports.boolean().optional().describe("Prevent respondents from going back. Recommended for quizzes."),
22627
22656
  total_steps: external_exports.number().nullable().optional().describe("Override the total step count shown in the step counter. Set null to auto-calculate."),
22628
- primary_color: external_exports.string().optional().describe("Primary/brand color (hex or CSS color). Used for buttons and accents."),
22629
- background_color: external_exports.string().optional().describe("Background color (hex, rgba, or CSS color)."),
22630
- font_family: external_exports.string().optional().describe("Font family name (e.g. 'Inter', 'Roboto', 'Playfair Display')."),
22631
- embed_autoplay: external_exports.boolean().optional().describe("Auto-play video when embedded (default: false). When off, embeds show a thumbnail + play button."),
22657
+ primary_color: external_exports.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().describe("Primary/brand color as 6-digit hex (e.g. '#FF5500'). Used for buttons and accents."),
22658
+ background_color: external_exports.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().describe("Background color as 6-digit hex (e.g. '#1A1A2E')."),
22659
+ font_family: external_exports.string().optional().describe("AI-PROTECTED: Only set when the user explicitly requests a specific font."),
22660
+ embed_autoplay: external_exports.boolean().optional().describe("AI-PROTECTED: Only set when the user explicitly requests autoplay. Default: false."),
22632
22661
  description: external_exports.string().nullable().optional().describe("SEO description (meta description, og:description). Set null to clear."),
22633
- author: external_exports.string().nullable().optional().describe("Author/brand name shown to respondents. Set null to clear."),
22634
- brand_name: external_exports.string().nullable().optional().describe("Brand name shown alongside the logo in the viewer. Set null to clear."),
22635
- logo_url: external_exports.string().nullable().optional().describe("URL to a logo image shown in the viewer header. Set null to clear."),
22662
+ author: external_exports.string().nullable().optional().describe("AI-PROTECTED: Only set when the user explicitly provides an author name. Set null to clear."),
22663
+ brand_name: external_exports.string().nullable().optional().describe("AI-PROTECTED: Only set when the user explicitly provides a brand name. Set null to clear."),
22664
+ logo_url: external_exports.string().nullable().optional().describe("AI-PROTECTED: Only set when the user explicitly provides a logo URL. Set null to clear."),
22636
22665
  tags: external_exports.array(external_exports.string()).optional().describe("Replace all tags on this form. Pass the full desired set (e.g. ['quiz', 'trivia', 'slug:elephants']). Omit to leave tags unchanged.")
22637
22666
  },
22638
22667
  annotations: {
@@ -22642,10 +22671,10 @@ function registerUpdateFormTool(server) {
22642
22671
  openWorldHint: true
22643
22672
  }
22644
22673
  },
22645
- async ({ form_id, title, is_published, show_step_counter, disable_back_navigation, total_steps, primary_color, background_color, font_family, embed_autoplay, description, author, brand_name, logo_url, tags }) => {
22674
+ async ({ form_id, title, is_live, show_step_counter, disable_back_navigation, total_steps, primary_color, background_color, font_family, embed_autoplay, description, author, brand_name, logo_url, tags }) => {
22646
22675
  const body = {};
22647
22676
  if (title !== void 0) body.title = title;
22648
- if (is_published !== void 0) body.is_published = is_published;
22677
+ if (is_live !== void 0) body.is_live = is_live;
22649
22678
  if (show_step_counter !== void 0) body.show_step_counter = show_step_counter;
22650
22679
  if (disable_back_navigation !== void 0) body.disable_back_navigation = disable_back_navigation;
22651
22680
  if (total_steps !== void 0) body.total_steps = total_steps;
@@ -22677,8 +22706,8 @@ function registerUpdateFormTool(server) {
22677
22706
  }
22678
22707
  const updates = [];
22679
22708
  if (title !== void 0) updates.push(`Title \u2192 "${title}"`);
22680
- if (is_published !== void 0)
22681
- updates.push(`Published \u2192 ${is_published}`);
22709
+ if (is_live !== void 0)
22710
+ updates.push(`Live \u2192 ${is_live}`);
22682
22711
  if (show_step_counter !== void 0)
22683
22712
  updates.push(`Step counter \u2192 ${show_step_counter}`);
22684
22713
  if (disable_back_navigation !== void 0)
@@ -22721,7 +22750,7 @@ function registerDeleteFormTool(server) {
22721
22750
  "clipform_delete_form",
22722
22751
  {
22723
22752
  title: "Delete Clipform",
22724
- description: `Permanently delete an unclaimed form and all its questions. This cannot be undone. Only works on forms that haven't been claimed yet.`,
22753
+ description: `Permanently delete an unclaimed form and all its nodes. This cannot be undone. Only works on forms that haven't been claimed yet.`,
22725
22754
  inputSchema: {
22726
22755
  form_id: external_exports.string().uuid().describe("The form UUID to delete (returned by clipform_create_form, not the short share_id from the URL)")
22727
22756
  },
@@ -22757,7 +22786,7 @@ ${NODE_TYPES_DESCRIPTION}
22757
22786
  All type definitions and config schemas are derived from @vid-master/config (answer-types).`,
22758
22787
  inputSchema: {
22759
22788
  form_id: external_exports.string().uuid().describe("The form UUID (returned by clipform_create_form, not the short share_id from the URL)"),
22760
- question: NodeSchema.describe("The node to add"),
22789
+ node: NodeSchema.describe("The node to add"),
22761
22790
  after_node_id: external_exports.string().optional().describe(
22762
22791
  "Insert after this node ID. Omit to append before the end screen."
22763
22792
  )
@@ -22769,8 +22798,8 @@ All type definitions and config schemas are derived from @vid-master/config (ans
22769
22798
  openWorldHint: true
22770
22799
  }
22771
22800
  },
22772
- async ({ form_id, question, after_node_id }) => {
22773
- const body = { question };
22801
+ async ({ form_id, node, after_node_id }) => {
22802
+ const body = { node };
22774
22803
  if (after_node_id) body.after_node_id = after_node_id;
22775
22804
  const result = await callApi(`/forms/${form_id}/nodes`, {
22776
22805
  method: "POST",
@@ -22782,8 +22811,8 @@ All type definitions and config schemas are derived from @vid-master/config (ans
22782
22811
  const confirmMsg = [
22783
22812
  `Node added successfully!`,
22784
22813
  `Node ID: ${result.data.node_id}`,
22785
- `Type: ${question.type}`,
22786
- `Prompt: ${question.prompt}`
22814
+ `Type: ${node.type}`,
22815
+ `Prompt: ${node.prompt}`
22787
22816
  ].join("\n");
22788
22817
  const formState = await fetchAndFormatFormState(form_id);
22789
22818
  return textResult(formState ? `${confirmMsg}
@@ -22805,13 +22834,13 @@ function registerUpdateNodeTool(server) {
22805
22834
  inputSchema: {
22806
22835
  form_id: external_exports.string().uuid().describe("The form UUID (returned by clipform_create_form, not the short share_id from the URL)"),
22807
22836
  node_id: external_exports.string().describe("The node ID to update"),
22808
- prompt: external_exports.string().optional().describe("New question text"),
22809
- label: external_exports.string().optional().describe("Short label for the question (used in logic builder)"),
22810
- type: external_exports.enum(ACTIVE_NODE_TYPES).optional().describe("Change the question type"),
22837
+ prompt: external_exports.string().optional().describe("New node prompt text"),
22838
+ label: external_exports.string().optional().describe("Short label for the node (used in logic builder)"),
22839
+ type: external_exports.enum(ACTIVE_NODE_TYPES).optional().describe("Change the node type"),
22811
22840
  required: external_exports.boolean().optional().describe("Whether an answer is required"),
22812
22841
  config: external_exports.record(external_exports.unknown()).optional().describe(CONFIG_DESCRIPTION),
22813
22842
  options: external_exports.array(OptionSchema).optional().describe(
22814
- "Replace all options (for choice questions). Omit to keep existing options."
22843
+ "Replace all options (for choice nodes). Omit to keep existing options."
22815
22844
  )
22816
22845
  },
22817
22846
  annotations: {
@@ -22922,7 +22951,7 @@ var MediaItemSchema = external_exports.object({
22922
22951
  ).optional().describe("Per-word timestamps within the segment")
22923
22952
  })
22924
22953
  ).optional().describe("Word-level captions from clipform_generate_tts. Always include when uploading narrated video - pass the full objects including 'words' so per-word highlighting works."),
22925
- show_captions: external_exports.boolean().optional().default(true).describe("Display captions/subtitles on the question")
22954
+ show_captions: external_exports.boolean().optional().default(true).describe("Display captions/subtitles on the node")
22926
22955
  });
22927
22956
  function registerUploadNodeMediaTool(server) {
22928
22957
  server.registerTool(
@@ -23421,8 +23450,8 @@ var v4_default = v4;
23421
23450
  var jobs = /* @__PURE__ */ new Map();
23422
23451
  var MAX_AGE_MS = 30 * 60 * 1e3;
23423
23452
  var RENDER_TIMING = {
23424
- expectedRange: "15-45 seconds",
23425
- pollDelay: "~10 seconds"
23453
+ expectedRange: "15-120 seconds",
23454
+ pollDelay: "~15 seconds"
23426
23455
  };
23427
23456
  function createJob(tool) {
23428
23457
  const job = {
@@ -23508,7 +23537,7 @@ You are the director. Every stylistic choice below is yours - defaults exist for
23508
23537
  1. Source images (use clipform_search_media with kind: "image"). Prefer portrait sources for 9:16 output; landscape works too via blur-pad fallback.
23509
23538
  2. Produce narration audio (clipform_generate_tts).
23510
23539
  3. Call this tool with images + audio_url + your creative direction.
23511
- 4. Attach the returned public URL to a question via clipform_upload_media with media_type "video".
23540
+ 4. Attach the returned public URL to a node via clipform_upload_media with media_type "video".
23512
23541
 
23513
23542
  ## Image framing (per-image)
23514
23543
 
@@ -24620,9 +24649,9 @@ Each question is a micro variable-reward event - the same dopamine loop that kee
24620
24649
 
24621
24650
  For numeric questions (population, speed, weight), scale the real answer by random multipliers (0.3x to 3x) rounded to the same magnitude. Makes wrong answers plausible but clearly different.
24622
24651
 
24623
- ## Color Brain Questions (ColorSwatch composition)
24652
+ ## Color Brain Questions (ColorCards composition)
24624
24653
 
24625
- Inspired by the Color Brain board game - every answer is identified by its colours. Show flat colour chips, ask "what has these colours?". Use the \`ColorSwatch\` composition for the question card.
24654
+ Inspired by the Color Brain board game - every answer is identified by its colours. Show flat colour chips, ask "what has these colours?". Use the \`ColorCards\` composition for the question card.
24626
24655
 
24627
24656
  **Colour palette constraint:** Swatches are solid flat chips. Only use clearly distinguishable basic colours: red, blue, green, yellow, white, black, orange, purple, pink, brown, grey. No navy vs blue, no teal vs cyan - they look the same as flat chips. The skill is picking subjects where a combo of basic colours is unique enough to identify.
24628
24657
 
@@ -25008,6 +25037,14 @@ function createServer() {
25008
25037
  name: "clipform-mcp-server",
25009
25038
  version: MCP_VERSION
25010
25039
  });
25040
+ const _registerTool = server.registerTool.bind(server);
25041
+ server.registerTool = (name, config2, handler) => {
25042
+ return _registerTool(
25043
+ name,
25044
+ config2,
25045
+ (...args) => runWithMcpTool(name, () => handler(...args))
25046
+ );
25047
+ };
25011
25048
  registerCreateFormTool(server);
25012
25049
  registerListFormsTool(server);
25013
25050
  registerGetFormTool(server);
@@ -25044,4 +25081,4 @@ export {
25044
25081
  setApiKey,
25045
25082
  createServer
25046
25083
  };
25047
- //# sourceMappingURL=chunk-NW2BYSDR.js.map
25084
+ //# sourceMappingURL=chunk-D64XBDOX.js.map