@clipform/mcp-server 1.16.1 → 1.18.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,
5
6
  runWithMcpTool
6
- } from "./chunk-VWGIVJMQ.js";
7
+ } from "./chunk-HCZI2UJ5.js";
7
8
 
8
9
  // ../../node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/codegen/code.js
9
10
  var require_code = __commonJS({
@@ -22278,24 +22279,13 @@ if (!API_BASE_URL) {
22278
22279
  "API_URL must be set (e.g. http://localhost:3003 or https://api.clipform.io)"
22279
22280
  );
22280
22281
  }
22281
- var MCP_SERVICE_USER_ID = "00000000-0000-4000-8000-000000000001";
22282
22282
  function getAuthHeaders() {
22283
- const serviceSecret = process.env.INTERNAL_SERVICE_SECRET || process.env.CLIPFORM_INTERNAL_SERVICE_SECRET;
22284
- const workspaceId = process.env.MCP_WORKSPACE_ID;
22285
- if (serviceSecret && workspaceId) {
22286
- return {
22287
- "Authorization": `Bearer ${serviceSecret}`,
22288
- "X-Mcp-Workspace": workspaceId,
22289
- "X-Mcp-User": MCP_SERVICE_USER_ID
22290
- };
22291
- }
22292
- const apiKey = process.env.CLIPFORM_API_KEY;
22293
- if (apiKey) {
22294
- return { "Authorization": `Bearer ${apiKey}` };
22283
+ const authCtx = getMcpAuth();
22284
+ const apiKey = authCtx?.api_key || process.env.CLIPFORM_API_KEY;
22285
+ if (!apiKey) {
22286
+ throw new Error("CLIPFORM_API_KEY must be set.");
22295
22287
  }
22296
- throw new Error(
22297
- "No API authentication configured. Set INTERNAL_SERVICE_SECRET + MCP_WORKSPACE_ID, or CLIPFORM_API_KEY."
22298
- );
22288
+ return { "Authorization": `Bearer ${apiKey}` };
22299
22289
  }
22300
22290
  async function callApi(path, options = {}) {
22301
22291
  const { body, params } = options;
@@ -22361,9 +22351,7 @@ function registerCreateFormTool(server) {
22361
22351
 
22362
22352
  ${NODE_TYPES_DESCRIPTION}
22363
22353
 
22364
- 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.
22365
-
22366
- Fields marked AI-PROTECTED must only be set when the user explicitly requests them. Do not invent values for font, branding, or embed settings.
22354
+ 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. AI-PROTECTED parameters have restrictions noted in their descriptions.
22367
22355
 
22368
22356
  Example: A form that asks a question, collects contact info, then finishes:
22369
22357
  {
@@ -22402,7 +22390,7 @@ Example: A form that asks a question, collects contact info, then finishes:
22402
22390
  const workspaceId = me.workspace?.id ?? null;
22403
22391
  if (!workspaceId) {
22404
22392
  return errorResult(
22405
- "No workspace available. Connect your Clipform account in claude.ai \u2192 Settings \u2192 Connectors, or ensure MCP_WORKSPACE_ID is configured for anonymous mode."
22393
+ "No workspace available. This usually means authentication is misconfigured - check your API key or OAuth connection."
22406
22394
  );
22407
22395
  }
22408
22396
  planContext = {
@@ -22416,7 +22404,7 @@ Example: A form that asks a question, collects contact info, then finishes:
22416
22404
  const contentCount = nodes.filter((q) => !NON_COUNTABLE_TYPES.includes(q.type)).length;
22417
22405
  if (planContext.node_limit !== null && contentCount > planContext.node_limit) {
22418
22406
  const upgradeUrl = `${BUSINESS.urls.dashboard}/billing`;
22419
- 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.`;
22407
+ 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 sign in to your Clipform account so forms land in your workspace with your real plan limits.`;
22420
22408
  return errorResult(message);
22421
22409
  }
22422
22410
  if (planContext.form_limit !== null) {
@@ -22440,7 +22428,7 @@ Example: A form that asks a question, collects contact info, then finishes:
22440
22428
  }
22441
22429
  const { data } = createResult;
22442
22430
  const formId = data.form_id;
22443
- const claimUrl = data.claim_url ?? void 0;
22431
+ const formUrl = data.form_url ?? void 0;
22444
22432
  const settingsBody = {};
22445
22433
  if (show_step_counter !== void 0) settingsBody.show_step_counter = show_step_counter;
22446
22434
  if (disable_back_navigation !== void 0) settingsBody.disable_back_navigation = disable_back_navigation;
@@ -22473,7 +22461,8 @@ Example: A form that asks a question, collects contact info, then finishes:
22473
22461
  body: { is_live: true }
22474
22462
  });
22475
22463
  if (!publishResult.ok) {
22476
- return errorResult(`Form created but failed to publish: ${publishResult.error}. Form ID: ${formId}`);
22464
+ return errorResult(`Form created but failed to publish: ${publishResult.error}. Form ID: ${formId}
22465
+ FORM URL: ${data.viewer_url}`);
22477
22466
  }
22478
22467
  if (tags && tags.length > 0) {
22479
22468
  await callApi(`/forms/${formId}/tags`, {
@@ -22490,12 +22479,12 @@ Example: A form that asks a question, collects contact info, then finishes:
22490
22479
  ``,
22491
22480
  `FORM URL (share this with respondents): ${data.viewer_url}`
22492
22481
  ];
22493
- if (claimUrl) {
22494
- lines.push(`CLAIM URL (transfer ownership to your account): ${claimUrl}`);
22482
+ if (formUrl) {
22483
+ lines.push(`DASHBOARD URL (sign in to edit and manage): ${formUrl}`);
22495
22484
  }
22496
22485
  lines.push(
22497
22486
  ``,
22498
- `IMPORTANT: Always show the user the exact URLs above \u2014 do not rewrite or modify them.`,
22487
+ `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.`,
22499
22488
  `Pass form_id on follow-up tools (get_form, add_node, upload_node_media, etc.).`
22500
22489
  );
22501
22490
  if (planContext) {
@@ -22510,7 +22499,7 @@ Example: A form that asks a question, collects contact info, then finishes:
22510
22499
  } else {
22511
22500
  lines.push(
22512
22501
  ``,
22513
- `Plan: anonymous ${planContext.plan_name} (${limitNote}). Mention this once: the user can connect their Clipform account in claude.ai \u2192 Settings \u2192 Connectors \u2192 ${BUSINESS.urls.mcp} so future forms land directly in their workspace - do not repeat on follow-up calls.`
22502
+ `Plan: anonymous ${planContext.plan_name} (${limitNote}). Mention this once: the user can sign in to their Clipform account so future forms land directly in their workspace - do not repeat on follow-up calls.`
22514
22503
  );
22515
22504
  }
22516
22505
  }
@@ -23316,7 +23305,7 @@ Returns audio URL and word-level captions per item.`,
23316
23305
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
23317
23306
  },
23318
23307
  async ({ items }) => {
23319
- const workspace_id = process.env.MCP_WORKSPACE_ID;
23308
+ const workspace_id = getMcpAuth()?.workspace_id;
23320
23309
  const results = await Promise.allSettled(
23321
23310
  items.map(
23322
23311
  (item) => callApi("/internal/tts", {
@@ -23425,7 +23414,7 @@ Style presets via default_style.preset: cinematic (default), dramatic, calm, doc
23425
23414
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
23426
23415
  },
23427
23416
  async ({ images, audio_url, random_effects, transition, default_style, background_color }) => {
23428
- const workspace_id = process.env.MCP_WORKSPACE_ID;
23417
+ const workspace_id = getMcpAuth()?.workspace_id;
23429
23418
  const result = await callApi("/internal/slideshow", {
23430
23419
  body: {
23431
23420
  images,
@@ -23721,7 +23710,7 @@ Items: type "image" (Ken Burns motion) or "video" (cover-cropped, muted by defau
23721
23710
  annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
23722
23711
  },
23723
23712
  async ({ items, audio_url, duration_seconds, random_effects, transition, default_style, background_color }) => {
23724
- const workspace_id = process.env.MCP_WORKSPACE_ID;
23713
+ const workspace_id = getMcpAuth()?.workspace_id;
23725
23714
  const result = await callApi("/internal/generate-video", {
23726
23715
  body: {
23727
23716
  items,
@@ -23898,40 +23887,62 @@ async function getSessionContext() {
23898
23887
  }
23899
23888
  if (me.auth_mode !== "oauth") {
23900
23889
  lines.push(`
23901
- To unlock your full plan: connect your Clipform account in Settings > Connectors > ${BUSINESS.urls.mcp}`);
23890
+ To unlock your full plan: sign in to your Clipform account.`);
23902
23891
  }
23903
23892
  return lines.join("\n");
23904
23893
  }
23905
23894
 
23906
23895
  // src/prompts.ts
23896
+ var MEDIA_STYLE_ENUM = ["text", "images", "video"];
23897
+ function resourceLink(name, uri, title) {
23898
+ return {
23899
+ role: "assistant",
23900
+ content: {
23901
+ type: "resource_link",
23902
+ uri,
23903
+ name,
23904
+ title,
23905
+ mimeType: "text/markdown"
23906
+ }
23907
+ };
23908
+ }
23907
23909
  function registerPrompts(server) {
23908
23910
  server.registerPrompt(
23909
23911
  "create-quiz",
23910
23912
  {
23911
23913
  title: "Create a Quiz",
23912
- description: "Build a scored knowledge quiz with narrated video questions"
23914
+ description: "Build a scored knowledge quiz with narrated video questions",
23915
+ argsSchema: {
23916
+ topic: external_exports.string().optional().describe("Quiz topic (e.g. 'Space exploration', 'Premier League')"),
23917
+ question_count: external_exports.number().optional().default(8).describe("Number of questions (default: 8)"),
23918
+ media_style: external_exports.enum(MEDIA_STYLE_ENUM).optional().default("video").describe("Media style: text only, still images, or slideshow video with narration (default: video)")
23919
+ }
23913
23920
  },
23914
- async () => {
23921
+ async (args) => {
23915
23922
  const sessionContext = await getSessionContext();
23923
+ const topic = args.topic ? ` about "${args.topic}"` : "";
23924
+ const count = args.question_count ?? 8;
23925
+ const media = args.media_style ?? "video";
23916
23926
  return {
23917
23927
  messages: [
23918
23928
  {
23919
23929
  role: "user",
23920
23930
  content: {
23921
23931
  type: "text",
23922
- text: "I want to create a quiz. What's the best approach?"
23932
+ text: `I want to create a quiz${topic}. ${count} questions, ${media} style.`
23923
23933
  }
23924
23934
  },
23935
+ resourceLink("guide-quiz", "clipform://guides/quiz", "Quiz Writing Guide"),
23925
23936
  {
23926
23937
  role: "assistant",
23927
23938
  content: {
23928
23939
  type: "text",
23929
- text: `${sessionContext ? sessionContext + "\n\n" : ""}Here's how to build a great quiz with Clipform. Read the quiz writing guide (clipform://guides/quiz) for detailed craft knowledge on question design, difficulty curves, and narration style.
23940
+ text: `${sessionContext ? sessionContext + "\n\n" : ""}Here's how to build a great ${count}-question ${media} quiz${topic} with Clipform. Read the attached quiz guide for craft knowledge on question design, difficulty curves, and narration style.
23930
23941
 
23931
23942
  ## Workflow
23932
23943
 
23933
23944
  1. **Research** the topic - find surprising facts, common misconceptions, myth-busters
23934
- 2. **Write questions** - follow the difficulty curve (easy start, hard middle, satisfying end). Target 5-8 questions.
23945
+ 2. **Write questions** - follow the difficulty curve (easy start, hard middle, satisfying end). Target ${count} questions.
23935
23946
  3. **Create the form** with clipform_create_form:
23936
23947
  - show_step_counter: true
23937
23948
  - disable_back_navigation: true
@@ -23940,12 +23951,12 @@ function registerPrompts(server) {
23940
23951
  - randomise_options: true in config
23941
23952
  - score: 1 on correct option, score: 0 on wrong
23942
23953
  - 3-4 wrong answers per question
23943
- 5. **Generate narration** with clipform_generate_tts for each question. Tease the question - do NOT reveal the answer or read options aloud. Keep each narration 5-15 seconds.
23954
+ 5. **Generate narration** with clipform_generate_tts for each question. Tease the question - do NOT reveal the answer or read options aloud. Keep each narration 5-15 seconds.${media !== "text" ? `
23944
23955
  6. **Build video** for each question:
23945
23956
  - clipform_search_media (kind: "image") - 3 images per question
23946
23957
  - clipform_generate_video - creates Ken Burns video synced to audio
23947
- 7. **Attach media** with clipform_upload_node_media. Include captions, set show_captions: true.
23948
- 8. **Update end screen** with clipform_update_node - EVERY quiz must have:
23958
+ 7. **Attach media** with clipform_upload_node_media. Include captions, set show_captions: true.` : ""}
23959
+ ${media !== "text" ? "8" : "6"}. **Update end screen** with clipform_update_node - EVERY quiz must have:
23949
23960
  - show_score: true, icon: "trophy"
23950
23961
  - show_share_button: true (drives virality)
23951
23962
  - cta_type: "restart", cta_text: a short challenge like "Beat your score?" or "Try again?"
@@ -23955,15 +23966,9 @@ function registerPrompts(server) {
23955
23966
  { "min": 3, "max": 5, "title": "Frequent Flyer", "message": "Not bad! You know your way around." },
23956
23967
  { "min": 6, "max": 8, "title": "World Explorer", "message": "Impressive - you really know your stuff." }
23957
23968
  \`\`\`
23958
- 9. **Publish** with clipform_update_form
23959
- 10. **Tag the form** - pass tags: one format (quiz/survey/interview/feedback/lead-gen), one genre (trivia/personality/nps/poll/testimonial), and 2-3 topic words
23960
- 11. **Log** with clipform_log_generation (sources, images, attributions)
23961
-
23962
- ## Before building, ask
23963
-
23964
- 1. How many questions?
23965
- 2. Media style: text only, still images, or slideshow video with narration?
23966
- 3. Any topic or style preferences?`
23969
+ ${media !== "text" ? "9" : "7"}. **Publish** with clipform_update_form
23970
+ ${media !== "text" ? "10" : "8"}. **Tag the form** - pass tags: one format (quiz/survey/interview/feedback/lead-gen), one genre (trivia/personality/nps/poll/testimonial), and 2-3 topic words
23971
+ ${media !== "text" ? "11" : "9"}. **Log** with clipform_log_generation (sources, images, attributions)`
23967
23972
  }
23968
23973
  }
23969
23974
  ]
@@ -23974,24 +23979,37 @@ function registerPrompts(server) {
23974
23979
  "create-personality-quiz",
23975
23980
  {
23976
23981
  title: "Create a Personality Quiz",
23977
- description: "Build a 'Which X are you?' personality quiz with category-based scoring and outcome screens"
23982
+ description: "Build a 'Which X are you?' personality quiz with category-based scoring and outcome screens",
23983
+ argsSchema: {
23984
+ topic: external_exports.string().optional().describe("Quiz theme (e.g. 'Which city are you?', 'What's your work style?')"),
23985
+ categories: external_exports.string().optional().describe("Comma-separated outcome categories (e.g. 'Creative, Analytical, Leader, Collaborator')"),
23986
+ question_count: external_exports.number().optional().default(8).describe("Number of questions (default: 8)"),
23987
+ media_style: external_exports.enum(MEDIA_STYLE_ENUM).optional().default("video").describe("Media style (default: video)")
23988
+ }
23978
23989
  },
23979
- async () => {
23990
+ async (args) => {
23980
23991
  const sessionContext = await getSessionContext();
23992
+ const topic = args.topic ? ` - "${args.topic}"` : "";
23993
+ const count = args.question_count ?? 8;
23994
+ const media = args.media_style ?? "video";
23995
+ const categoriesNote = args.categories ? `
23996
+
23997
+ Outcome categories: ${args.categories}` : "";
23981
23998
  return {
23982
23999
  messages: [
23983
24000
  {
23984
24001
  role: "user",
23985
24002
  content: {
23986
24003
  type: "text",
23987
- text: "I want to create a personality quiz. What's the best approach?"
24004
+ text: `I want to create a personality quiz${topic}. ${count} questions, ${media} style.${categoriesNote}`
23988
24005
  }
23989
24006
  },
24007
+ resourceLink("guide-personality-quiz", "clipform://guides/personality-quiz", "Personality Quiz Guide"),
23990
24008
  {
23991
24009
  role: "assistant",
23992
24010
  content: {
23993
24011
  type: "text",
23994
- text: `${sessionContext ? sessionContext + "\n\n" : ""}Here's how to build a personality quiz with Clipform. Read the personality quiz guide (clipform://guides/personality-quiz) for craft knowledge on category design, option weighting, and outcome writing.
24012
+ text: `${sessionContext ? sessionContext + "\n\n" : ""}Here's how to build a ${count}-question personality quiz${topic} with Clipform. Read the attached personality quiz guide for craft knowledge on category design, option weighting, and outcome writing.
23995
24013
 
23996
24014
  ## How it differs from a knowledge quiz
23997
24015
 
@@ -24000,7 +24018,7 @@ There are NO correct answers. Each option maps to one or more outcome categories
24000
24018
  ## Workflow
24001
24019
 
24002
24020
  1. **Define 3-5 outcome categories** - these are the "personalities" (e.g. "Creative", "Analytical", "Leader", "Collaborator"). More than 5 gets muddy.
24003
- 2. **Write questions** - each question should feel revealing but fun. Target 5-8 questions.
24021
+ 2. **Write questions** - each question should feel revealing but fun. Target ${count} questions.
24004
24022
  3. **Create the form** with clipform_create_form:
24005
24023
  - show_step_counter: true
24006
24024
  - disable_back_navigation: true
@@ -24031,13 +24049,7 @@ There are NO correct answers. Each option maps to one or more outcome categories
24031
24049
  \`\`\`
24032
24050
  8. **Publish** with clipform_update_form
24033
24051
  9. **Tag the form** - pass tags: one format (quiz/survey/interview/feedback/lead-gen), one genre (trivia/personality/nps/poll/testimonial), and 2-3 topic words
24034
- 10. **Log** with clipform_log_generation
24035
-
24036
- ## Before building, ask
24037
-
24038
- 1. What are the possible outcomes/personalities? (3-5 categories)
24039
- 2. What's the theme? ("Which city are you?", "What's your work style?", "Which character are you?")
24040
- 3. Media style: text only, still images, or slideshow video with narration?`
24052
+ 10. **Log** with clipform_log_generation`
24041
24053
  }
24042
24054
  }
24043
24055
  ]
@@ -24048,45 +24060,49 @@ There are NO correct answers. Each option maps to one or more outcome categories
24048
24060
  "create-interview",
24049
24061
  {
24050
24062
  title: "Create an Interview",
24051
- description: "Build a form to collect testimonials, case studies, async video interviews, or journalist responses"
24063
+ description: "Build a form to collect testimonials, case studies, async video interviews, or journalist responses",
24064
+ argsSchema: {
24065
+ purpose: external_exports.string().optional().describe("What you're collecting (e.g. 'testimonial', 'case study', 'job application')"),
24066
+ response_format: external_exports.enum(["video", "audio", "text", "all"]).optional().default("all").describe("How respondents reply (default: all - video, audio, and text)"),
24067
+ needs_consent: external_exports.boolean().optional().default(true).describe("Include a consent statement (default: true)")
24068
+ }
24052
24069
  },
24053
- async () => {
24070
+ async (args) => {
24054
24071
  const sessionContext = await getSessionContext();
24072
+ const purpose = args.purpose ?? "responses";
24073
+ const format = args.response_format ?? "all";
24074
+ const consent = args.needs_consent ?? true;
24055
24075
  return {
24056
24076
  messages: [
24057
24077
  {
24058
24078
  role: "user",
24059
24079
  content: {
24060
24080
  type: "text",
24061
- text: "I want to collect responses or testimonials from people. What's the best approach?"
24081
+ text: `I want to collect ${purpose} from people. Responses via ${format}.${consent ? " Include consent." : ""}`
24062
24082
  }
24063
24083
  },
24084
+ resourceLink("guide-interview", "clipform://guides/interview", "Interview & Testimonial Guide"),
24064
24085
  {
24065
24086
  role: "assistant",
24066
24087
  content: {
24067
24088
  type: "text",
24068
- text: `${sessionContext ? sessionContext + "\n\n" : ""}Here's how to build an interview or testimonial form with Clipform. Read the interview guide (clipform://guides/interview) for detailed craft knowledge on question design and pacing.
24089
+ text: `${sessionContext ? sessionContext + "\n\n" : ""}Here's how to build an interview form for collecting ${purpose} with Clipform. Read the attached interview guide for craft knowledge on question design and pacing.
24069
24090
 
24070
24091
  ## Workflow
24071
24092
 
24072
- 1. **Identify the ask** - what do you need from respondents? Testimonial, case study, expert comment, job application?
24093
+ 1. **Identify the ask** - what do you need from respondents? ${purpose}.
24073
24094
  2. **Create the form** with clipform_create_form:
24074
24095
  - show_step_counter: true
24075
24096
  - disable_back_navigation: false
24076
24097
  3. **Add a warm-up question** - something easy: "Tell us your name and role" (type: "open")
24077
- 4. **Add core questions** (type: "open") - 2-3 max, one topic per question. Enable text + audio + video responses.
24078
- 5. **Add contact collection** (type: "contact") - first name + email minimum
24079
- 6. **Add consent** if needed - "I agree that my response may be used in [context]"
24098
+ 4. **Add core questions** (type: "open") - 2-3 max, one topic per question.${format === "all" ? " Enable text + audio + video responses." : ` Enable ${format} responses.`}
24099
+ 5. **Add contact collection** (type: "contact") - first name + email minimum${consent ? `
24100
+ 6. **Add consent** - "I agree that my response may be used in [context]"` : ""}
24080
24101
  7. **Update end screen** - set expectations: "Thanks! We'll be in touch."
24081
24102
  8. **Optional: add narration** - warm, inviting tone. "We'd love to hear your story..."
24082
24103
  9. **Publish** with clipform_update_form
24083
24104
  10. **Tag the form** - pass tags: one format (quiz/survey/interview/feedback/lead-gen), one genre (trivia/personality/nps/poll/testimonial), and 2-3 topic words
24084
-
24085
- ## Before building, ask
24086
-
24087
- 1. What are you collecting? (testimonial, case study, interview, application)
24088
- 2. Should respondents reply with video, audio, text, or all three?
24089
- 3. Do you need a consent statement?`
24105
+ 11. **Log** with clipform_log_generation`
24090
24106
  }
24091
24107
  }
24092
24108
  ]
@@ -24097,24 +24113,31 @@ There are NO correct answers. Each option maps to one or more outcome categories
24097
24113
  "create-survey",
24098
24114
  {
24099
24115
  title: "Create a Survey",
24100
- description: "Build a feedback survey, NPS form, or research questionnaire"
24116
+ description: "Build a feedback survey, NPS form, or research questionnaire",
24117
+ argsSchema: {
24118
+ topic: external_exports.string().optional().describe("What feedback you're collecting (e.g. 'NPS', 'event feedback', 'product satisfaction')"),
24119
+ anonymous: external_exports.boolean().optional().default(true).describe("Whether the survey is anonymous (default: true)")
24120
+ }
24101
24121
  },
24102
- async () => {
24122
+ async (args) => {
24103
24123
  const sessionContext = await getSessionContext();
24124
+ const topic = args.topic ? ` for ${args.topic}` : "";
24125
+ const anonymous = args.anonymous ?? true;
24104
24126
  return {
24105
24127
  messages: [
24106
24128
  {
24107
24129
  role: "user",
24108
24130
  content: {
24109
24131
  type: "text",
24110
- text: "I want to collect feedback or run a survey. What's the best approach?"
24132
+ text: `I want to create a survey${topic}. ${anonymous ? "Anonymous" : "Identified"} respondents.`
24111
24133
  }
24112
24134
  },
24135
+ resourceLink("guide-survey", "clipform://guides/survey", "Survey & Feedback Guide"),
24113
24136
  {
24114
24137
  role: "assistant",
24115
24138
  content: {
24116
24139
  type: "text",
24117
- text: `${sessionContext ? sessionContext + "\n\n" : ""}Here's how to build a survey with Clipform. Read the survey guide (clipform://guides/survey) for craft knowledge on question design and reducing respondent fatigue.
24140
+ text: `${sessionContext ? sessionContext + "\n\n" : ""}Here's how to build a survey${topic} with Clipform. Read the attached survey guide for craft knowledge on question design and reducing respondent fatigue.
24118
24141
 
24119
24142
  ## Workflow
24120
24143
 
@@ -24124,19 +24147,14 @@ There are NO correct answers. Each option maps to one or more outcome categories
24124
24147
  - disable_back_navigation: false
24125
24148
  3. **Add key metric question first** (type: "choice" or "rating") - put it first while attention is highest
24126
24149
  4. **Add one "why?" follow-up** (type: "open") - "What's the main reason for your score?"
24127
- 5. **Add 2-3 specific questions** (type: "choice") - only ask what you'll act on
24128
- 6. **Contact** (optional) - only if you need to follow up. Many surveys work better anonymous.
24150
+ 5. **Add 2-3 specific questions** (type: "choice") - only ask what you'll act on${!anonymous ? `
24151
+ 6. **Contact** - collect name + email for follow-up` : ""}
24129
24152
  7. **Update end screen** - "Thanks for your feedback!"
24130
24153
  8. **Publish** with clipform_update_form
24131
24154
  9. **Tag the form** - pass tags: one format (quiz/survey/interview/feedback/lead-gen), one genre (trivia/personality/nps/poll/testimonial), and 2-3 topic words
24155
+ 10. **Log** with clipform_log_generation
24132
24156
 
24133
- ## Key rule: 5 questions max. Every extra question costs completions.
24134
-
24135
- ## Before building, ask
24136
-
24137
- 1. What feedback are you collecting? (NPS, satisfaction, event feedback, product research)
24138
- 2. Anonymous or identified?
24139
- 3. Any specific areas you want to ask about?`
24157
+ ## Key rule: 5 questions max. Every extra question costs completions.`
24140
24158
  }
24141
24159
  }
24142
24160
  ]
@@ -24147,24 +24165,36 @@ There are NO correct answers. Each option maps to one or more outcome categories
24147
24165
  "create-comprehension-quiz",
24148
24166
  {
24149
24167
  title: "Create a YouTube Comprehension Quiz",
24150
- description: "Build a comprehension quiz from a YouTube video - tests whether the viewer actually watched and understood the content"
24168
+ description: "Build a comprehension quiz from a YouTube video - tests whether the viewer actually watched and understood the content",
24169
+ argsSchema: {
24170
+ youtube_url: external_exports.string().optional().describe("YouTube video URL to create the quiz from"),
24171
+ question_count: external_exports.number().optional().default(8).describe("Number of questions (default: 8)"),
24172
+ audience: external_exports.string().optional().describe("Target audience (e.g. 'children aged 5-8', 'university students', 'general')"),
24173
+ media_style: external_exports.enum(MEDIA_STYLE_ENUM).optional().default("video").describe("Media style (default: video)")
24174
+ }
24151
24175
  },
24152
- async () => {
24176
+ async (args) => {
24153
24177
  const sessionContext = await getSessionContext();
24178
+ const count = args.question_count ?? 8;
24179
+ const media = args.media_style ?? "video";
24180
+ const urlNote = args.youtube_url ? ` Start by extracting the transcript from: ${args.youtube_url}` : "";
24181
+ const audienceNote = args.audience ? ` Target audience: ${args.audience}.` : "";
24154
24182
  return {
24155
24183
  messages: [
24156
24184
  {
24157
24185
  role: "user",
24158
24186
  content: {
24159
24187
  type: "text",
24160
- text: "I want to create a comprehension quiz based on a YouTube video. What's the best approach?"
24188
+ text: `I want to create a comprehension quiz based on a YouTube video. ${count} questions, ${media} style.${urlNote}${audienceNote}`
24161
24189
  }
24162
24190
  },
24191
+ resourceLink("guide-quiz", "clipform://guides/quiz", "Quiz Writing Guide"),
24192
+ resourceLink("guide-comprehension-quiz", "clipform://guides/comprehension-quiz", "Comprehension Quiz Guide"),
24163
24193
  {
24164
24194
  role: "assistant",
24165
24195
  content: {
24166
24196
  type: "text",
24167
- text: `${sessionContext ? sessionContext + "\n\n" : ""}Here's how to build a comprehension quiz from a YouTube video. Read the quiz guide (clipform://guides/quiz) for general craft, and the comprehension guide (clipform://guides/comprehension-quiz) for video-specific techniques.
24197
+ text: `${sessionContext ? sessionContext + "\n\n" : ""}Here's how to build a ${count}-question comprehension quiz from a YouTube video. Read the attached quiz and comprehension guides for craft knowledge on question design and distractor techniques.
24168
24198
 
24169
24199
  ## Comprehension Quiz Workflow
24170
24200
 
@@ -24179,7 +24209,7 @@ There are NO correct answers. Each option maps to one or more outcome categories
24179
24209
  - "What example does the video use to illustrate...?"
24180
24210
  - Include 1-2 inference questions: "Based on the video, why does the presenter believe...?"
24181
24211
  - Avoid questions answerable without watching (e.g., common knowledge about the topic)
24182
- 4. **Adapt to the audience** - if specified (e.g., "for a 5-year-old"), simplify language, reduce option count, focus on concrete/visual details rather than abstract arguments
24212
+ 4. **Adapt to the audience**${args.audience ? ` (${args.audience})` : ""} - simplify language for younger audiences, focus on concrete/visual details rather than abstract arguments
24183
24213
  5. **Create the form** with clipform_create_form:
24184
24214
  - show_step_counter: true
24185
24215
  - disable_back_navigation: true
@@ -24188,12 +24218,12 @@ There are NO correct answers. Each option maps to one or more outcome categories
24188
24218
  - randomise_options: true in config
24189
24219
  - score: 1 on correct option, score: 0 on wrong
24190
24220
  - 3-4 wrong answers per question - make distractors plausible (things someone might guess without watching)
24191
- 7. **Generate narration** with clipform_generate_tts - reference the video naturally: "If you watched closely, you'll know this one..." Keep each narration 5-10 seconds.
24221
+ 7. **Generate narration** with clipform_generate_tts - reference the video naturally: "If you watched closely, you'll know this one..." Keep each narration 5-10 seconds.${media !== "text" ? `
24192
24222
  8. **Build video** for each question:
24193
24223
  - clipform_search_media (kind: "image") - 3 images per question
24194
24224
  - clipform_generate_video - Ken Burns video synced to audio
24195
- 9. **Attach media** with clipform_upload_node_media. Include captions, set show_captions: true.
24196
- 10. **Update end screen** with clipform_update_node:
24225
+ 9. **Attach media** with clipform_upload_node_media. Include captions, set show_captions: true.` : ""}
24226
+ ${media !== "text" ? "10" : "8"}. **Update end screen** with clipform_update_node:
24197
24227
  - show_score: true, icon: "trophy"
24198
24228
  - show_share_button: true
24199
24229
  - cta_type: "restart", cta_text: "Rewatch and try again?"
@@ -24203,9 +24233,9 @@ There are NO correct answers. Each option maps to one or more outcome categories
24203
24233
  { "min": 3, "max": 5, "title": "Casual Viewer", "message": "You caught the highlights but missed some details." },
24204
24234
  { "min": 6, "max": 8, "title": "Focused Student", "message": "You were paying attention - impressive." }
24205
24235
  \`\`\`
24206
- 11. **Publish** with clipform_update_form
24207
- 12. **Tag** - tags: ["quiz", "comprehension", "youtube"] + 2-3 topic words from the video
24208
- 13. **Log** with clipform_log_generation - include the YouTube URL, video title, and channel as sources
24236
+ ${media !== "text" ? "11" : "9"}. **Publish** with clipform_update_form
24237
+ ${media !== "text" ? "12" : "10"}. **Tag** - tags: ["quiz", "comprehension", "youtube"] + 2-3 topic words from the video
24238
+ ${media !== "text" ? "13" : "11"}. **Log** with clipform_log_generation - include the YouTube URL, video title, and channel as sources
24209
24239
 
24210
24240
  ## Question Types for Comprehension
24211
24241
 
@@ -24217,14 +24247,7 @@ There are NO correct answers. Each option maps to one or more outcome categories
24217
24247
  | Contrast | "The video compares X and Y. What was the key difference?" | Comprehension depth |
24218
24248
  | Conclusion | "What was the presenter's final point?" | Watched to the end |
24219
24249
 
24220
- Wrong answers should sound right to someone who didn't watch but googled the topic. The quiz should be unfair to non-watchers and fair to watchers.
24221
-
24222
- ## Before building, ask
24223
-
24224
- 1. What's the YouTube URL?
24225
- 2. How many questions? (default: 8)
24226
- 3. Who's the audience? (age, knowledge level)
24227
- 4. Media style: text only, still images, or slideshow video with narration?`
24250
+ Wrong answers should sound right to someone who didn't watch but googled the topic. The quiz should be unfair to non-watchers and fair to watchers.`
24228
24251
  }
24229
24252
  }
24230
24253
  ]
@@ -24235,46 +24258,50 @@ Wrong answers should sound right to someone who didn't watch but googled the top
24235
24258
  "create-funnel",
24236
24259
  {
24237
24260
  title: "Create a Funnel",
24238
- description: "Build a lead qualification funnel or product recommendation quiz with branching logic"
24261
+ description: "Build a lead qualification funnel or product recommendation quiz with branching logic",
24262
+ argsSchema: {
24263
+ outcomes: external_exports.string().optional().describe("What segments or recommendations exist (e.g. 'Basic/Pro/Enterprise', or product names)"),
24264
+ criteria: external_exports.string().optional().describe("What criteria determine routing (e.g. 'budget, team size, use case')"),
24265
+ needs_contact: external_exports.boolean().optional().default(true).describe("Include contact capture (default: true)")
24266
+ }
24239
24267
  },
24240
- async () => {
24268
+ async (args) => {
24241
24269
  const sessionContext = await getSessionContext();
24270
+ const outcomes = args.outcomes ?? "different outcomes";
24271
+ const contact = args.needs_contact ?? true;
24272
+ const criteriaNote = args.criteria ? ` Routing criteria: ${args.criteria}.` : "";
24242
24273
  return {
24243
24274
  messages: [
24244
24275
  {
24245
24276
  role: "user",
24246
24277
  content: {
24247
24278
  type: "text",
24248
- text: "I want to qualify leads or recommend products based on answers. What's the best approach?"
24279
+ text: `I want to qualify leads or recommend products based on answers. Outcomes: ${outcomes}.${criteriaNote}`
24249
24280
  }
24250
24281
  },
24282
+ resourceLink("guide-funnel", "clipform://guides/funnel", "Lead Qualification & Funnel Guide"),
24251
24283
  {
24252
24284
  role: "assistant",
24253
24285
  content: {
24254
24286
  type: "text",
24255
- text: `${sessionContext ? sessionContext + "\n\n" : ""}Here's how to build a qualification funnel with Clipform. Read the funnel guide (clipform://guides/funnel) for craft knowledge on branching logic and conversion.
24287
+ text: `${sessionContext ? sessionContext + "\n\n" : ""}Here's how to build a qualification funnel with Clipform. Read the attached funnel guide for craft knowledge on branching logic and conversion.
24256
24288
 
24257
24289
  ## Workflow
24258
24290
 
24259
- 1. **Define outcomes** - what segments or recommendations exist? (e.g., Basic/Pro/Enterprise, or product categories)
24291
+ 1. **Define outcomes** - what segments or recommendations exist? (${outcomes})
24260
24292
  2. **Create the form** with clipform_create_form:
24261
24293
  - show_step_counter: false (funnels feel shorter without it)
24262
24294
  - disable_back_navigation: true (prevents answer shopping that breaks scoring)
24263
24295
  3. **Add hook question** (type: "choice") - "What best describes you?" or "What are you looking for?" This segments the user.
24264
24296
  4. **Add qualifying questions** (type: "choice") - 2-3 questions that narrow down the need. Assign scores to each option.
24265
- 5. **Set branching logic** with clipform_set_logic - route based on answers
24266
- 6. **Add contact capture** (type: "contact") - name, email, phone. Place AFTER qualifying questions.
24297
+ 5. **Set branching logic** with clipform_set_logic - route based on answers${contact ? `
24298
+ 6. **Add contact capture** (type: "contact") - name, email, phone. Place AFTER qualifying questions.` : ""}
24267
24299
  7. **Update end screen** with score_ranges for personalised outcomes: "Based on your answers, we recommend..."
24268
24300
  8. **Publish** with clipform_update_form
24269
24301
  9. **Tag the form** - pass tags: one format (quiz/survey/interview/feedback/lead-gen), one genre (trivia/personality/nps/poll/testimonial), and 2-3 topic words
24302
+ 10. **Log** with clipform_log_generation
24270
24303
 
24271
- ## Key rule: 3-5 questions max. Every extra step loses leads.
24272
-
24273
- ## Before building, ask
24274
-
24275
- 1. What outcomes are you routing to? (products, plans, team members, messages)
24276
- 2. What criteria determine the routing?
24277
- 3. Do you need contact capture?`
24304
+ ## Key rule: 3-5 questions max. Every extra step loses leads.`
24278
24305
  }
24279
24306
  }
24280
24307
  ]
@@ -24328,7 +24355,8 @@ function registerResources(server) {
24328
24355
  "clipform://guides/quiz",
24329
24356
  {
24330
24357
  description: "Craft knowledge for writing engaging quizzes - difficulty curves, question psychology, narration style, scoring",
24331
- mimeType: "text/markdown"
24358
+ mimeType: "text/markdown",
24359
+ annotations: { audience: ["assistant"], priority: 0.8 }
24332
24360
  },
24333
24361
  async () => ({
24334
24362
  contents: [
@@ -24388,6 +24416,11 @@ Inspired by the Color Brain board game - every answer is identified by its colou
24388
24416
  - If two answer options would produce identical swatches, don't use that question.
24389
24417
  - Pair with a reveal composition (FlagReveal, image, or text) for the answer.
24390
24418
 
24419
+ ## Settings
24420
+
24421
+ - show_step_counter: true
24422
+ - disable_back_navigation: true (prevent going back to change answers after seeing feedback)
24423
+
24391
24424
  ## Narration Style
24392
24425
 
24393
24426
  You're a quiz master, not a question reader. Each question's narration should:
@@ -24413,7 +24446,8 @@ ${MEDIA_WORKFLOW}`
24413
24446
  "clipform://guides/personality-quiz",
24414
24447
  {
24415
24448
  description: "Craft knowledge for building personality quizzes - category design, option weighting, outcome writing, no right/wrong answers",
24416
- mimeType: "text/markdown"
24449
+ mimeType: "text/markdown",
24450
+ annotations: { audience: ["assistant"], priority: 0.8 }
24417
24451
  },
24418
24452
  async () => ({
24419
24453
  contents: [
@@ -24463,6 +24497,11 @@ Each category needs a \`scoring_results\` entry on the end screen:
24463
24497
  - **Message**: 2-3 sentences that feel like a personalised insight. Reference specific traits the quiz measured.
24464
24498
  - **Optional CTA**: link to relevant content, product, or next step
24465
24499
 
24500
+ ## Settings
24501
+
24502
+ - show_step_counter: true
24503
+ - disable_back_navigation: true (prevent re-answering which skews category scores)
24504
+
24466
24505
  ## Narration Style
24467
24506
 
24468
24507
  Reflective and curious, not quizmaster-y:
@@ -24484,7 +24523,8 @@ ${MEDIA_WORKFLOW}`
24484
24523
  "clipform://guides/comprehension-quiz",
24485
24524
  {
24486
24525
  description: "Craft knowledge for YouTube comprehension quizzes - extracting questions from transcripts, distractor design, audience adaptation",
24487
- mimeType: "text/markdown"
24526
+ mimeType: "text/markdown",
24527
+ annotations: { audience: ["assistant"], priority: 0.8 }
24488
24528
  },
24489
24529
  async () => ({
24490
24530
  contents: [
@@ -24535,6 +24575,11 @@ Make wrong answers plausible to someone who **didn't watch**:
24535
24575
 
24536
24576
  For young children: focus on "What did you SEE?" and "Who did what?" rather than abstract arguments.
24537
24577
 
24578
+ ## Settings
24579
+
24580
+ - show_step_counter: true
24581
+ - disable_back_navigation: true
24582
+
24538
24583
  ## Narration style
24539
24584
 
24540
24585
  Reference the video naturally but don't spoil:
@@ -24555,7 +24600,8 @@ ${MEDIA_WORKFLOW}`
24555
24600
  "clipform://guides/interview",
24556
24601
  {
24557
24602
  description: "Craft knowledge for building interview and testimonial collection forms - warm-up pacing, open questions, consent, video responses",
24558
- mimeType: "text/markdown"
24603
+ mimeType: "text/markdown",
24604
+ annotations: { audience: ["assistant"], priority: 0.8 }
24559
24605
  },
24560
24606
  async () => ({
24561
24607
  contents: [
@@ -24609,7 +24655,8 @@ ${MEDIA_WORKFLOW}`
24609
24655
  "clipform://guides/survey",
24610
24656
  {
24611
24657
  description: "Craft knowledge for feedback surveys, NPS, and research forms - brevity, rating scales, respondent fatigue",
24612
- mimeType: "text/markdown"
24658
+ mimeType: "text/markdown",
24659
+ annotations: { audience: ["assistant"], priority: 0.8 }
24613
24660
  },
24614
24661
  async () => ({
24615
24662
  contents: [
@@ -24662,7 +24709,8 @@ ${WRITING_PRINCIPLES}`
24662
24709
  "clipform://guides/funnel",
24663
24710
  {
24664
24711
  description: "Craft knowledge for lead qualification funnels and product recommendation quizzes - branching logic, progressive profiling, conversion",
24665
- mimeType: "text/markdown"
24712
+ mimeType: "text/markdown",
24713
+ annotations: { audience: ["assistant"], priority: 0.8 }
24666
24714
  },
24667
24715
  async () => ({
24668
24716
  contents: [
@@ -24794,4 +24842,4 @@ export {
24794
24842
  JSONRPCMessageSchema,
24795
24843
  createServer
24796
24844
  };
24797
- //# sourceMappingURL=chunk-MB6BZ2JN.js.map
24845
+ //# sourceMappingURL=chunk-GZH3JXEB.js.map