@clipform/mcp-server 1.29.0 → 1.31.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
@@ -55,7 +55,7 @@ You can also pass the key as a CLI flag: `npx -y @clipform/mcp-server --api-key=
55
55
 
56
56
  | Tool | Description |
57
57
  |------|-------------|
58
- | `clipform_create_form` | Create a new form with nodes, theming, and tags in one call |
58
+ | `clipform_create_form` | Create a new form with nodes, theming, and tags in one call. Returns the created node IDs (structured output) so media steps need no follow-up lookup |
59
59
  | `clipform_list_forms` | List forms in your workspace with filtering and pagination |
60
60
  | `clipform_get_form` | View a form and all its nodes |
61
61
  | `clipform_update_form` | Change title, publish status, or settings |
@@ -6,14 +6,14 @@ import {
6
6
  getWorkflowText,
7
7
  objectType,
8
8
  registerPrompts
9
- } from "./chunk-6SFMF2AQ.js";
9
+ } from "./chunk-ELO7KCMZ.js";
10
10
  import {
11
11
  GUIDE_TYPES,
12
12
  QUIZ_VARIANTS,
13
13
  getGuideContent,
14
14
  getGuideUri,
15
15
  registerResources
16
- } from "./chunk-B6ZWH3GD.js";
16
+ } from "./chunk-J4KJN4C4.js";
17
17
  import {
18
18
  BUSINESS,
19
19
  CONTACT_FIELDS,
@@ -16988,6 +16988,8 @@ var PUBLIC_COMPOSITIONS = [
16988
16988
  ];
16989
16989
  var LABS_COMPOSITIONS = [
16990
16990
  "CountrySilhouetteClip",
16991
+ "FourImageGrid",
16992
+ "OddOneOutReveal",
16991
16993
  "FlagRevealGB",
16992
16994
  "FlagRevealJP",
16993
16995
  "FlagRevealBR",
@@ -17094,7 +17096,7 @@ var CONFIG_DESCRIPTION = (() => {
17094
17096
  var STYLE_PRESET_ENUM = external_exports.enum(KEN_BURNS_PRESETS).describe(`Ken Burns style preset: ${KEN_BURNS_PRESETS.join(", ")}`);
17095
17097
  var OptionSchema = external_exports.object({
17096
17098
  content: external_exports.string().describe("Option text"),
17097
- score: external_exports.number().optional().describe("Score value for this option (for scored quizzes). Use 1 for correct, 0 for wrong."),
17099
+ score: external_exports.number().optional().describe("Score value for this option (for scored quizzes). Use 1 for correct, 0 for wrong. Scored options automatically enable correct-answer marking (record_scores) on the node."),
17098
17100
  scores: external_exports.record(external_exports.number()).optional().describe("Planned feature - category-based scores keyed by category name. Not yet editable in the dashboard.")
17099
17101
  });
17100
17102
  var OPTIONS_DESCRIPTION = (() => {
@@ -17118,6 +17120,21 @@ var NodeSchema = external_exports.object({
17118
17120
  config: external_exports.record(external_exports.unknown()).optional().describe(CONFIG_DESCRIPTION),
17119
17121
  options: external_exports.array(OptionSchema).optional().describe(OPTIONS_DESCRIPTION)
17120
17122
  });
17123
+ function applyNodeDefaults(node) {
17124
+ const def = NODE_TYPES[node.type];
17125
+ if (node.type === "button" && (!node.options || node.options.length === 0)) {
17126
+ const buttonDefault = def?.config_schema?.properties?.button_text?.default || "Continue";
17127
+ const label = node.config?.button_text || buttonDefault;
17128
+ node.options = [{ content: label }];
17129
+ }
17130
+ const supportsRecordScores = def?.config_schema?.properties?.choice?.properties?.record_scores !== void 0;
17131
+ const hasScoredOptions = node.options?.some((o) => o.score != null || o.scores != null);
17132
+ if (supportsRecordScores && hasScoredOptions) {
17133
+ const config2 = node.config ??= {};
17134
+ const choice = config2.choice ??= {};
17135
+ if (choice.record_scores === void 0) choice.record_scores = true;
17136
+ }
17137
+ }
17121
17138
 
17122
17139
  // src/tools/create-form.ts
17123
17140
  function registerCreateFormTool(server) {
@@ -17150,6 +17167,13 @@ Example: A form that asks a question, collects contact info, then finishes:
17150
17167
  font_family: external_exports.string().optional().describe("AI-PROTECTED: Only set when the user explicitly requests a specific font."),
17151
17168
  tags: external_exports.array(external_exports.string()).optional().describe("Tags for indexing (e.g. ['quiz', 'trivia', 'arsenal']). Include format, genre, and topics.")
17152
17169
  },
17170
+ outputSchema: {
17171
+ form_id: external_exports.string().describe("Form UUID - pass to follow-up tools"),
17172
+ viewer_url: external_exports.string().describe("Live form URL for respondents"),
17173
+ dashboard_url: external_exports.string().optional().describe("Builder URL (sign in to edit)"),
17174
+ nodes: external_exports.array(external_exports.object({ id: external_exports.string(), type: external_exports.string(), prompt: external_exports.string() })).describe("Created nodes in order - IDs for upload_node_media / update_node"),
17175
+ plan: external_exports.object({ name: external_exports.string(), auth_mode: external_exports.string() }).optional().describe("Plan and auth context for this session")
17176
+ },
17153
17177
  annotations: {
17154
17178
  readOnlyHint: false,
17155
17179
  destructiveHint: false,
@@ -17233,12 +17257,9 @@ Example: A form that asks a question, collects contact info, then finishes:
17233
17257
  body: settingsBody
17234
17258
  });
17235
17259
  }
17260
+ const createdNodes = [];
17236
17261
  for (const q of nodes) {
17237
- if (q.type === "button" && (!q.options || q.options.length === 0)) {
17238
- const buttonDefault = NODE_TYPES.button?.config_schema?.properties?.button_text?.default || "Continue";
17239
- const label = q.config?.button_text || buttonDefault;
17240
- q.options = [{ content: label }];
17241
- }
17262
+ applyNodeDefaults(q);
17242
17263
  const addResult = await callApi(`/forms/${formId}/nodes`, {
17243
17264
  method: "POST",
17244
17265
  body: { node: q }
@@ -17246,6 +17267,11 @@ Example: A form that asks a question, collects contact info, then finishes:
17246
17267
  if (!addResult.ok) {
17247
17268
  return errorResult(`Failed to add node "${q.prompt}": ${addResult.error}`);
17248
17269
  }
17270
+ createdNodes.push({
17271
+ id: addResult.data.node_id,
17272
+ type: q.type,
17273
+ prompt: q.prompt
17274
+ });
17249
17275
  }
17250
17276
  if (tags && tags.length > 0) {
17251
17277
  await callApi(`/forms/${formId}/tags`, {
@@ -17253,13 +17279,16 @@ Example: A form that asks a question, collects contact info, then finishes:
17253
17279
  body: { tags }
17254
17280
  });
17255
17281
  }
17282
+ const truncate = (s, n = 60) => s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
17256
17283
  const lines = [
17257
17284
  `Form created and live.`,
17258
17285
  ``,
17259
17286
  `Title: ${title}`,
17260
- `Nodes: ${nodes.length}`,
17261
17287
  `Form ID: ${formId}`,
17262
17288
  ``,
17289
+ `Node IDs (use directly with upload_node_media / update_node - no get_form round-trip needed):`,
17290
+ ...createdNodes.map((n) => `- [${n.type}] "${truncate(n.prompt)}": ${n.id}`),
17291
+ ``,
17263
17292
  `FORM URL (live - share this with respondents): ${data.viewer_url}`
17264
17293
  ];
17265
17294
  if (formUrl) {
@@ -17268,7 +17297,7 @@ Example: A form that asks a question, collects contact info, then finishes:
17268
17297
  lines.push(
17269
17298
  ``,
17270
17299
  `The form is live and accessible at the FORM URL above. Continue with media/narration steps if needed - changes appear immediately.`,
17271
- `CRITICAL: Copy the URLs above character-for-character into your response. Do NOT construct, guess, or invent any URLs. Only use the exact FORM URL and DASHBOARD URL provided here.`,
17300
+ `The URLs above are exact values; constructed or guessed URLs return 404. Use the FORM URL and DASHBOARD URL as shown.`,
17272
17301
  `Pass form_id on follow-up tools (get_form, add_node, upload_node_media, etc.).`
17273
17302
  );
17274
17303
  if (planContext) {
@@ -17287,7 +17316,16 @@ Example: A form that asks a question, collects contact info, then finishes:
17287
17316
  );
17288
17317
  }
17289
17318
  }
17290
- return textResult(lines.join("\n"));
17319
+ return {
17320
+ content: [{ type: "text", text: lines.join("\n") }],
17321
+ structuredContent: {
17322
+ form_id: formId,
17323
+ viewer_url: data.viewer_url,
17324
+ ...formUrl ? { dashboard_url: formUrl } : {},
17325
+ nodes: createdNodes,
17326
+ ...planContext ? { plan: { name: planContext.plan_name, auth_mode: planContext.auth_mode } } : {}
17327
+ }
17328
+ };
17291
17329
  }
17292
17330
  );
17293
17331
  }
@@ -17592,11 +17630,7 @@ function registerAddNodeTool(server) {
17592
17630
  }
17593
17631
  },
17594
17632
  async ({ form_id, node, after_node_id }) => {
17595
- if (node.type === "button" && (!node.options || node.options.length === 0)) {
17596
- const buttonDefault = NODE_TYPES.button?.config_schema?.properties?.button_text?.default || "Continue";
17597
- const label = node.config?.button_text || buttonDefault;
17598
- node.options = [{ content: label }];
17599
- }
17633
+ applyNodeDefaults(node);
17600
17634
  const body = { node };
17601
17635
  if (after_node_id) body.after_node_id = after_node_id;
17602
17636
  const result = await callApi(`/forms/${form_id}/nodes`, {
@@ -17669,6 +17703,10 @@ function registerUpdateNodeTool(server) {
17669
17703
  if (options !== void 0)
17670
17704
  changes.push(`Options \u2192 ${options.map((o) => o.content).join(", ")}`);
17671
17705
  allLines.push(`Node ${node_id} updated: ${changes.join(", ")}`);
17706
+ const warnings = result.data.warnings;
17707
+ if (warnings?.length) {
17708
+ for (const w of warnings) allLines.push(`Node ${node_id} warning: ${w}`);
17709
+ }
17672
17710
  }
17673
17711
  return textResult(allLines.join("\n"));
17674
17712
  }
@@ -18829,4 +18867,4 @@ export {
18829
18867
  JSONRPCMessageSchema,
18830
18868
  createServer
18831
18869
  };
18832
- //# sourceMappingURL=chunk-5FG2V7B7.js.map
18870
+ //# sourceMappingURL=chunk-46RSQK6L.js.map