@clipform/mcp-server 1.5.1 → 1.7.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.
Files changed (63) hide show
  1. package/README.md +21 -9
  2. package/dist/lib/api-client.d.ts +1 -1
  3. package/dist/lib/api-client.js +21 -5
  4. package/dist/lib/api-client.js.map +1 -1
  5. package/dist/lib/auth-context.d.ts +17 -0
  6. package/dist/lib/auth-context.js +9 -0
  7. package/dist/lib/auth-context.js.map +1 -0
  8. package/dist/lib/session-context.d.ts +1 -0
  9. package/dist/lib/session-context.js +32 -0
  10. package/dist/lib/session-context.js.map +1 -0
  11. package/dist/prompts.js +241 -125
  12. package/dist/prompts.js.map +1 -1
  13. package/dist/resources.d.ts +2 -0
  14. package/dist/resources.js +336 -0
  15. package/dist/resources.js.map +1 -0
  16. package/dist/server.js +6 -2
  17. package/dist/server.js.map +1 -1
  18. package/dist/tools/add-question.js +2 -2
  19. package/dist/tools/add-question.js.map +1 -1
  20. package/dist/tools/attach-question-audio.js +2 -2
  21. package/dist/tools/attach-question-audio.js.map +1 -1
  22. package/dist/tools/create-form.js +65 -12
  23. package/dist/tools/create-form.js.map +1 -1
  24. package/dist/tools/delete-form.js +2 -2
  25. package/dist/tools/delete-form.js.map +1 -1
  26. package/dist/tools/delete-question-media.js +2 -2
  27. package/dist/tools/delete-question-media.js.map +1 -1
  28. package/dist/tools/delete-question.js +2 -2
  29. package/dist/tools/delete-question.js.map +1 -1
  30. package/dist/tools/generate-slideshow.js +114 -17
  31. package/dist/tools/generate-slideshow.js.map +1 -1
  32. package/dist/tools/generate-tts.js +3 -2
  33. package/dist/tools/generate-tts.js.map +1 -1
  34. package/dist/tools/generate-video.d.ts +2 -0
  35. package/dist/tools/generate-video.js +113 -0
  36. package/dist/tools/generate-video.js.map +1 -0
  37. package/dist/tools/get-form.js +1 -1
  38. package/dist/tools/get-form.js.map +1 -1
  39. package/dist/tools/get-question-media.js +2 -2
  40. package/dist/tools/get-question-media.js.map +1 -1
  41. package/dist/tools/list-assets.js +1 -1
  42. package/dist/tools/list-assets.js.map +1 -1
  43. package/dist/tools/list-compositions.js +3 -3
  44. package/dist/tools/list-compositions.js.map +1 -1
  45. package/dist/tools/log-generation.js +3 -3
  46. package/dist/tools/log-generation.js.map +1 -1
  47. package/dist/tools/render-composition.js +6 -4
  48. package/dist/tools/render-composition.js.map +1 -1
  49. package/dist/tools/search-media.js +1 -1
  50. package/dist/tools/search-media.js.map +1 -1
  51. package/dist/tools/search-music.js +1 -1
  52. package/dist/tools/search-music.js.map +1 -1
  53. package/dist/tools/search-news.js +1 -1
  54. package/dist/tools/search-news.js.map +1 -1
  55. package/dist/tools/set-question-logic.js +2 -2
  56. package/dist/tools/set-question-logic.js.map +1 -1
  57. package/dist/tools/update-form.js +39 -3
  58. package/dist/tools/update-form.js.map +1 -1
  59. package/dist/tools/update-question.js +2 -2
  60. package/dist/tools/update-question.js.map +1 -1
  61. package/dist/tools/upload-question-media.js +14 -6
  62. package/dist/tools/upload-question-media.js.map +1 -1
  63. package/package.json +3 -2
package/README.md CHANGED
@@ -4,19 +4,29 @@ MCP server for [Clipform](https://clipform.io) - build and manage video-style fo
4
4
 
5
5
  ## Install
6
6
 
7
- ```bash
8
- npm install @clipform/mcp-server
9
- ```
7
+ There are two ways to connect: **remote** (recommended, uses your Clipform account) and **local** (anonymous, free-tier limits).
8
+
9
+ ### Remote - claude.ai (recommended)
10
+
11
+ Use this if you have a Clipform account. Forms are created directly in your workspace and your plan tier applies (no 3-question cap on Pro).
12
+
13
+ 1. In claude.ai, open **Settings → Connectors → Add custom connector**.
14
+ 2. Enter the MCP URL: `https://mcp.clipform.io`
15
+ 3. Follow the OAuth prompts. You'll be redirected to Clipform to sign in and pick a workspace.
10
16
 
11
- ### Claude Code
17
+ No install or config file needed - claude.ai handles discovery and registration automatically via OAuth 2.1 + Dynamic Client Registration (RFC 7591).
18
+
19
+ ### Local - Claude Code / Claude Desktop
20
+
21
+ Use this for local development or if you don't have a Clipform account. Forms go into a shared anonymous workspace with the free-tier 3-question limit, and you claim them afterwards via the claim URL.
22
+
23
+ **Claude Code:**
12
24
 
13
25
  ```bash
14
26
  claude mcp add clipform -- npx -y @clipform/mcp-server
15
27
  ```
16
28
 
17
- ### Claude Desktop
18
-
19
- Add to your `claude_desktop_config.json`:
29
+ **Claude Desktop** - add to `claude_desktop_config.json`:
20
30
 
21
31
  ```json
22
32
  {
@@ -59,7 +69,7 @@ Add to your `claude_desktop_config.json`:
59
69
  |------|-------------|
60
70
  | `clipform_render_composition` | Render a Remotion composition to MP4, PNG, or GIF |
61
71
  | `clipform_generate_tts` | Generate narration audio with word-level captions |
62
- | `clipform_generate_slideshow` | Create Ken Burns slideshow videos from images + audio |
72
+ | `clipform_generate_slideshow` | Create slideshow videos from images + audio (director props: zoom, pan, easing, blur-pad, vignette, per-image style) |
63
73
  | `clipform_search_media` | Search royalty-free images and videos |
64
74
  | `clipform_search_music` | Search royalty-free music and ambient sounds |
65
75
  | `clipform_list_compositions` | List available Remotion compositions and prop schemas |
@@ -71,7 +81,9 @@ Add to your `claude_desktop_config.json`:
71
81
 
72
82
  ## How it works
73
83
 
74
- When you create a form, you get back an `edit_token` that authorizes all subsequent changes. The token works until the form is claimed by a user via the claim URL.
84
+ **Remote (claude.ai):** OAuth Bearer tokens are audience-bound to `https://mcp.clipform.io` (RFC 8707) and scoped to `mcp` only. Forms land directly in the workspace you approved during consent, and plan-tier limits come from that workspace's company.
85
+
86
+ **Local (npx):** When you create a form, you get back an `edit_token` that authorizes all subsequent changes. The token works until the form is claimed by a user via the claim URL.
75
87
 
76
88
  Forms are created with a start node and end screen automatically - you just add the questions in between.
77
89
 
@@ -15,7 +15,7 @@ type ApiResult = {
15
15
  export declare function callApi(path: string, options?: ApiOptions): Promise<ApiResult>;
16
16
  /**
17
17
  * Call an internal API endpoint (formgen, media, etc.)
18
- * Uses INTERNAL_API_KEY for auth and INTERNAL_API_URL as base.
18
+ * Uses INTERNAL_SECRET for auth and INTERNAL_API_URL as base.
19
19
  */
20
20
  export declare function callInternalApi(path: string, options?: Omit<ApiOptions, "token">): Promise<ApiResult>;
21
21
  export declare function errorResult(message: string): {
@@ -1,7 +1,8 @@
1
+ import { getMcpAuth } from "./auth-context.js";
1
2
  const DASHBOARD_URL = process.env.SERVER_URL || process.env.DASHBOARD_URL || "https://api.clipform.io";
2
3
  const API_VERSION = "v1";
3
4
  const INTERNAL_API_URL = process.env.INTERNAL_API_URL || DASHBOARD_URL;
4
- const INTERNAL_API_KEY = process.env.INTERNAL_API_KEY || "";
5
+ const INTERNAL_SECRET = process.env.INTERNAL_SERVICE_SECRET || "";
5
6
  export async function callApi(path, options = {}) {
6
7
  const { method = "GET", body, params, token } = options;
7
8
  let url = `${DASHBOARD_URL}/${API_VERSION}${path}`;
@@ -12,7 +13,22 @@ export async function callApi(path, options = {}) {
12
13
  const headers = {
13
14
  "Content-Type": "application/json",
14
15
  };
15
- if (token) {
16
+ // If the MCP server is running in-process inside the API host AND the
17
+ // request has been OAuth-authenticated, forward the end-user identity
18
+ // via internal headers instead of propagating the caller's Bearer.
19
+ //
20
+ // Token passthrough is forbidden by the MCP Authorization spec: the
21
+ // token that claude.ai presented was issued for audience `${api}/mcp`
22
+ // and must not be replayed against downstream routes like /v1/forms/*.
23
+ // Those routes instead trust the INTERNAL_SECRET + X-Mcp-User /
24
+ // X-Mcp-Workspace headers set below.
25
+ const mcpAuth = getMcpAuth();
26
+ if (mcpAuth && INTERNAL_SECRET) {
27
+ headers["Authorization"] = `Bearer ${INTERNAL_SECRET}`;
28
+ headers["X-Mcp-User"] = mcpAuth.user_id;
29
+ headers["X-Mcp-Workspace"] = mcpAuth.workspace_id;
30
+ }
31
+ else if (token) {
16
32
  headers["Authorization"] = `Bearer ${token}`;
17
33
  }
18
34
  const fetchOptions = { method, headers };
@@ -46,7 +62,7 @@ export async function callApi(path, options = {}) {
46
62
  }
47
63
  /**
48
64
  * Call an internal API endpoint (formgen, media, etc.)
49
- * Uses INTERNAL_API_KEY for auth and INTERNAL_API_URL as base.
65
+ * Uses INTERNAL_SECRET for auth and INTERNAL_API_URL as base.
50
66
  */
51
67
  export async function callInternalApi(path, options = {}) {
52
68
  const { method = "POST", body, params } = options;
@@ -58,8 +74,8 @@ export async function callInternalApi(path, options = {}) {
58
74
  const headers = {
59
75
  "Content-Type": "application/json",
60
76
  };
61
- if (INTERNAL_API_KEY) {
62
- headers["Authorization"] = `Bearer ${INTERNAL_API_KEY}`;
77
+ if (INTERNAL_SECRET) {
78
+ headers["Authorization"] = `Bearer ${INTERNAL_SECRET}`;
63
79
  }
64
80
  const fetchOptions = { method, headers };
65
81
  if (body && method !== "GET") {
@@ -1 +1 @@
1
- {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/lib/api-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Identity of the end-user a given MCP request is acting on behalf of.
3
+ *
4
+ * Populated by the in-process host (apps/api) after validating an OAuth
5
+ * Bearer on /mcp. The MCP tool layer reads it to decide which workspace
6
+ * forms should land in and which plan tier gates apply.
7
+ *
8
+ * NOT populated when the server runs as a local stdio process (npx) - in
9
+ * that mode tools fall back to edit-token based auth.
10
+ */
11
+ export interface McpAuthContext {
12
+ user_id: string;
13
+ workspace_id: string;
14
+ scopes: string[];
15
+ }
16
+ export declare function runWithMcpAuth<T>(ctx: McpAuthContext, fn: () => Promise<T> | T): Promise<T> | T;
17
+ export declare function getMcpAuth(): McpAuthContext | undefined;
@@ -0,0 +1,9 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ const storage = new AsyncLocalStorage();
3
+ export function runWithMcpAuth(ctx, fn) {
4
+ return storage.run(ctx, fn);
5
+ }
6
+ export function getMcpAuth() {
7
+ return storage.getStore();
8
+ }
9
+ //# sourceMappingURL=auth-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-context.js","sourceRoot":"","sources":["../../src/lib/auth-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAkBrD,MAAM,OAAO,GAAG,IAAI,iBAAiB,EAAkB,CAAC;AAExD,MAAM,UAAU,cAAc,CAAI,GAAmB,EAAE,EAAwB;IAC7E,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;AAC5B,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function getSessionContext(): Promise<string>;
@@ -0,0 +1,32 @@
1
+ import { BUSINESS } from "@vid-master/config";
2
+ import { callApi } from "./api-client.js";
3
+ export async function getSessionContext() {
4
+ const result = await callApi("/me", { method: "GET" });
5
+ if (!result.ok)
6
+ return "";
7
+ const me = result.data;
8
+ const plan = me.plan;
9
+ const lines = [];
10
+ lines.push("## Your Session");
11
+ if (me.auth_mode === "oauth") {
12
+ lines.push(`Workspace: ${me.workspace?.name ?? "Unknown"}`);
13
+ }
14
+ else {
15
+ lines.push("Mode: anonymous (not connected to a Clipform account)");
16
+ }
17
+ lines.push(`Plan: ${plan.name}`);
18
+ if (plan.node_limit !== null) {
19
+ lines.push(`Question limit: ${plan.node_limit} per form`);
20
+ }
21
+ else {
22
+ lines.push("Questions: unlimited");
23
+ }
24
+ if (plan.custom_theme === false) {
25
+ lines.push("Custom themes: not available on this plan");
26
+ }
27
+ if (me.auth_mode !== "oauth") {
28
+ lines.push(`\nTo unlock your full plan: connect your Clipform account in Settings > Connectors > ${BUSINESS.urls.mcp}`);
29
+ }
30
+ return lines.join("\n");
31
+ }
32
+ //# sourceMappingURL=session-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-context.js","sourceRoot":"","sources":["../../src/lib/session-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAE1C,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACvD,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,CAAC;IAE1B,MAAM,EAAE,GAAG,MAAM,CAAC,IAAW,CAAC;IAC9B,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC;IACrB,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAC9B,IAAI,EAAE,CAAC,SAAS,KAAK,OAAO,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,SAAS,EAAE,IAAI,IAAI,SAAS,EAAE,CAAC,CAAC;IAC9D,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAC;IACtE,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACjC,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,mBAAmB,IAAI,CAAC,UAAU,WAAW,CAAC,CAAC;IAC5D,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,IAAI,CAAC,YAAY,KAAK,KAAK,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;IAC1D,CAAC;IACD,IAAI,EAAE,CAAC,SAAS,KAAK,OAAO,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,wFAAwF,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC1H,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
package/dist/prompts.js CHANGED
@@ -1,135 +1,251 @@
1
+ import { getSessionContext } from "./lib/session-context.js";
1
2
  export function registerPrompts(server) {
2
3
  server.registerPrompt("create-quiz", {
3
- title: "Quiz Creation Guide",
4
- description: "Best practices and defaults for creating a quiz with Clipform",
5
- }, async () => ({
6
- messages: [
7
- {
8
- role: "user",
9
- content: { type: "text", text: "How should I create a quiz?" },
10
- },
11
- {
12
- role: "assistant",
13
- content: {
14
- type: "text",
15
- text: `When creating a quiz (knowledge questions with correct/wrong answers):
16
-
17
- **Form settings** (set during clipform_create_form):
18
- - show_step_counter: true (shows "1/5" progress)
19
- - disable_back_navigation: true (no going back to change answers)
20
- - Always set both of these when creating a quiz
21
-
22
- **Question defaults:**
23
- - Randomise answer options (set randomise_options: true in config)
24
- - 3-4 wrong answers per question for good difficulty
25
- - One clearly correct answer
26
-
27
- **Scoring:**
28
- - Set score: 1 on the correct answer option, score: 0 on wrong answers
29
- - This enables automatic score tracking throughout the quiz
30
- - On the end_screen, set show_score: true and icon: "trophy" in config to display the score and a trophy icon
31
-
32
- **Media workflow (if the user wants video):**
33
- 1. clipform_generate_tts - generate narration audio for each question (returns captions)
34
- 2. clipform_search_media (kind: "image") - find 3 images per question
35
- 3. clipform_generate_slideshow - create Ken Burns video from images + audio
36
- 4. clipform_upload_question_media - attach video to question. ALWAYS include the captions from step 1 and set show_captions: true. Captions must always be on for narrated content.
37
-
38
- **Image selection:**
39
- - ONLY use image URLs from clipform_search_media results. These are quality-filtered and high-res.
40
-
41
- **Writing the questions - factual accuracy (IMPORTANT):**
42
- For timeless topics (history, geography, science, nature, general knowledge up to your training cutoff), write questions directly from your own knowledge - a good language model is more accurate and more interesting than a generic trivia API.
43
-
44
- For any topic that is recent (post-training-cutoff) or any fact you are NOT confident about, follow this preference order BEFORE writing the question:
45
- 1. If your client exposes a native web search or web fetch tool (e.g. WebSearch / WebFetch), use that - it is broader and more current.
46
- 2. Otherwise, use clipform_search_news for structured current-news results.
47
- 3. If neither is available, REFUSE rather than fabricate. Tell the user you cannot verify facts about that topic and suggest they provide the source material.
48
-
49
- Never invent dates, names, statistics, or event details you are not sure of. It is always better to refuse a question than to ship a hallucinated one.
50
-
51
- **Narration style:**
52
- - The TTS narration should only read the question - do NOT repeat the answer options verbally. The options are displayed on screen for the user to read and tap.
53
- - Keep narration concise and conversational. Add a brief interesting fact or context if it fits naturally.
54
-
55
- **Audit log (final step):**
56
- After building the quiz, call clipform_log_generation with:
57
- - summary of what was built
58
- - sources used for research (Wikipedia articles, URLs)
59
- - images used with attribution and license
60
-
61
- **Before building, ask the user:**
4
+ title: "Create a Quiz",
5
+ description: "Build a scored knowledge quiz with narrated video questions",
6
+ }, async () => {
7
+ const sessionContext = await getSessionContext();
8
+ return {
9
+ messages: [
10
+ {
11
+ role: "user",
12
+ content: {
13
+ type: "text",
14
+ text: "I want to create a quiz. What's the best approach?",
15
+ },
16
+ },
17
+ {
18
+ role: "assistant",
19
+ content: {
20
+ type: "text",
21
+ 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.
22
+
23
+ ## Workflow
24
+
25
+ 1. **Research** the topic - find surprising facts, common misconceptions, myth-busters
26
+ 2. **Write questions** - follow the difficulty curve (easy start, hard middle, satisfying end). Target 5-8 questions.
27
+ 3. **Create the form** with clipform_create_form:
28
+ - show_step_counter: true
29
+ - disable_back_navigation: true
30
+ 4. **Add questions** with clipform_add_question (type: "choice"):
31
+ - config: { choice: { show_answer_feedback: true } }
32
+ - randomise_options: true in config
33
+ - score: 1 on correct option, score: 0 on wrong
34
+ - 3-4 wrong answers per question
35
+ 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.
36
+ 6. **Build video** for each question:
37
+ - clipform_search_media (kind: "image") - 3 images per question
38
+ - clipform_generate_video - creates Ken Burns video synced to audio
39
+ 7. **Attach media** with clipform_upload_question_media. Include captions, set show_captions: true.
40
+ 8. **Update end screen** - set show_score: true, icon: "trophy", and score_ranges for personalised messages
41
+ 9. **Publish** with clipform_update_form
42
+ 10. **Log** with clipform_log_generation (sources, images, attributions)
43
+
44
+ ## Before building, ask
45
+
62
46
  1. How many questions?
63
47
  2. Media style: text only, still images, or slideshow video with narration?
64
48
  3. Any topic or style preferences?`,
49
+ },
50
+ },
51
+ ],
52
+ };
53
+ });
54
+ server.registerPrompt("create-personality-quiz", {
55
+ title: "Create a Personality Quiz",
56
+ description: "Build a 'Which X are you?' personality quiz with category-based scoring and outcome screens",
57
+ }, async () => {
58
+ const sessionContext = await getSessionContext();
59
+ return {
60
+ messages: [
61
+ {
62
+ role: "user",
63
+ content: {
64
+ type: "text",
65
+ text: "I want to create a personality quiz. What's the best approach?",
66
+ },
67
+ },
68
+ {
69
+ role: "assistant",
70
+ content: {
71
+ type: "text",
72
+ 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.
73
+
74
+ ## How it differs from a knowledge quiz
75
+
76
+ There are NO correct answers. Each option maps to one or more outcome categories via \`scores\` (not \`score\`). The winning category at the end determines which result screen the respondent sees.
77
+
78
+ ## Workflow
79
+
80
+ 1. **Define 3-5 outcome categories** - these are the "personalities" (e.g. "Creative", "Analytical", "Leader", "Collaborator"). More than 5 gets muddy.
81
+ 2. **Write questions** - each question should feel revealing but fun. Target 5-8 questions.
82
+ 3. **Create the form** with clipform_create_form:
83
+ - show_step_counter: true
84
+ - disable_back_navigation: true
85
+ 4. **Add questions** with clipform_add_question (type: "choice"):
86
+ - config: { choice: { show_answer_feedback: false } } (no right/wrong!)
87
+ - Do NOT set randomise_options (option order matters for personality quizzes - lead with the most appealing)
88
+ - Each option gets \`scores: { "CategoryA": 2, "CategoryB": 1 }\` - weight towards relevant categories
89
+ - Every option should score in at least one category (no dead options)
90
+ 5. **Generate narration** with clipform_generate_tts - conversational, reflective tone. "What does this say about you?" not "Do you know the answer?"
91
+ 6. **Build video** + **attach media** (same as knowledge quiz workflow)
92
+ 7. **Update end screen** with scoring_results (NOT score_ranges):
93
+ \`\`\`json
94
+ {
95
+ "show_score": false,
96
+ "icon": "star",
97
+ "scoring_results": [
98
+ { "category": "Creative", "title": "You're a Creative!", "message": "You see the world differently..." },
99
+ { "category": "Analytical", "title": "You're an Analyst!", "message": "You love digging into the details..." }
100
+ ]
101
+ }
102
+ \`\`\`
103
+ 8. **Publish** with clipform_update_form
104
+ 9. **Log** with clipform_log_generation
105
+
106
+ ## Before building, ask
107
+
108
+ 1. What are the possible outcomes/personalities? (3-5 categories)
109
+ 2. What's the theme? ("Which city are you?", "What's your work style?", "Which character are you?")
110
+ 3. Media style: text only, still images, or slideshow video with narration?`,
111
+ },
112
+ },
113
+ ],
114
+ };
115
+ });
116
+ server.registerPrompt("create-interview", {
117
+ title: "Create an Interview",
118
+ description: "Build a form to collect testimonials, case studies, async video interviews, or journalist responses",
119
+ }, async () => {
120
+ const sessionContext = await getSessionContext();
121
+ return {
122
+ messages: [
123
+ {
124
+ role: "user",
125
+ content: {
126
+ type: "text",
127
+ text: "I want to collect responses or testimonials from people. What's the best approach?",
128
+ },
129
+ },
130
+ {
131
+ role: "assistant",
132
+ content: {
133
+ type: "text",
134
+ 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.
135
+
136
+ ## Workflow
137
+
138
+ 1. **Identify the ask** - what do you need from respondents? Testimonial, case study, expert comment, job application?
139
+ 2. **Create the form** with clipform_create_form:
140
+ - show_step_counter: true
141
+ - disable_back_navigation: false
142
+ 3. **Add a warm-up question** - something easy: "Tell us your name and role" (type: "open")
143
+ 4. **Add core questions** (type: "open") - 2-3 max, one topic per question. Enable text + audio + video responses.
144
+ 5. **Add contact collection** (type: "contact") - first name + email minimum
145
+ 6. **Add consent** if needed - "I agree that my response may be used in [context]"
146
+ 7. **Update end screen** - set expectations: "Thanks! We'll be in touch."
147
+ 8. **Optional: add narration** - warm, inviting tone. "We'd love to hear your story..."
148
+ 9. **Publish** with clipform_update_form
149
+
150
+ ## Before building, ask
151
+
152
+ 1. What are you collecting? (testimonial, case study, interview, application)
153
+ 2. Should respondents reply with video, audio, text, or all three?
154
+ 3. Do you need a consent statement?`,
155
+ },
156
+ },
157
+ ],
158
+ };
159
+ });
160
+ server.registerPrompt("create-survey", {
161
+ title: "Create a Survey",
162
+ description: "Build a feedback survey, NPS form, or research questionnaire",
163
+ }, async () => {
164
+ const sessionContext = await getSessionContext();
165
+ return {
166
+ messages: [
167
+ {
168
+ role: "user",
169
+ content: {
170
+ type: "text",
171
+ text: "I want to collect feedback or run a survey. What's the best approach?",
172
+ },
173
+ },
174
+ {
175
+ role: "assistant",
176
+ content: {
177
+ type: "text",
178
+ 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.
179
+
180
+ ## Workflow
181
+
182
+ 1. **Define the key metric** - what's the one number you care about? (NPS, satisfaction, likelihood to recommend)
183
+ 2. **Create the form** with clipform_create_form:
184
+ - show_step_counter: true
185
+ - disable_back_navigation: false
186
+ 3. **Add key metric question first** (type: "choice" or "rating") - put it first while attention is highest
187
+ 4. **Add one "why?" follow-up** (type: "open") - "What's the main reason for your score?"
188
+ 5. **Add 2-3 specific questions** (type: "choice") - only ask what you'll act on
189
+ 6. **Contact** (optional) - only if you need to follow up. Many surveys work better anonymous.
190
+ 7. **Update end screen** - "Thanks for your feedback!"
191
+ 8. **Publish** with clipform_update_form
192
+
193
+ ## Key rule: 5 questions max. Every extra question costs completions.
194
+
195
+ ## Before building, ask
196
+
197
+ 1. What feedback are you collecting? (NPS, satisfaction, event feedback, product research)
198
+ 2. Anonymous or identified?
199
+ 3. Any specific areas you want to ask about?`,
200
+ },
65
201
  },
66
- },
67
- ],
68
- }));
69
- server.registerPrompt("create-form", {
70
- title: "Form Creation Guide",
71
- description: "Best practices for creating a standard form or survey with Clipform",
72
- }, async () => ({
73
- messages: [
74
- {
75
- role: "user",
76
- content: { type: "text", text: "How should I create a form?" },
77
- },
78
- {
79
- role: "assistant",
80
- content: {
81
- type: "text",
82
- text: `When creating a standard form or survey:
83
-
84
- **Form settings:**
85
- - Back navigation is enabled by default (respondents can review answers)
86
- - Step counter is off by default (optional, enable for longer forms)
87
-
88
- **Common patterns:**
89
- - Use "contact" type for collecting user details (name, email, etc.)
90
- - Use "open" type for free-text responses
91
- - Use "choice" type for multiple choice / single select
92
- - End with an "end_screen" type for a thank you message
93
-
94
- **Media:** Forms can have video or image backgrounds on each question. Use clipform_upload_media to attach media after creation.`,
202
+ ],
203
+ };
204
+ });
205
+ server.registerPrompt("create-funnel", {
206
+ title: "Create a Funnel",
207
+ description: "Build a lead qualification funnel or product recommendation quiz with branching logic",
208
+ }, async () => {
209
+ const sessionContext = await getSessionContext();
210
+ return {
211
+ messages: [
212
+ {
213
+ role: "user",
214
+ content: {
215
+ type: "text",
216
+ text: "I want to qualify leads or recommend products based on answers. What's the best approach?",
217
+ },
95
218
  },
96
- },
97
- ],
98
- }));
99
- server.registerPrompt("create-journo-request", {
100
- title: "Journalist Request Guide",
101
- description: "Best practices for turning a journalist's callout into a Clipform",
102
- }, async () => ({
103
- messages: [
104
- {
105
- role: "user",
106
- content: { type: "text", text: "How should I create a journalist request form?" },
107
- },
108
- {
109
- role: "assistant",
110
- content: {
111
- type: "text",
112
- text: `When a journalist pastes their callout/request text, turn it into a Clipform. The form replaces "DM me" or "email me" with a shareable link.
113
-
114
- **Form structure (in order):**
115
- 1. **Open question** - The core ask from the journalist's text. Use "open" type. Default to text + audio + video responses.
116
- 2. **Additional short questions** (optional) - Only for distinct secondary asks. Don't over-split.
117
- 3. **Contact collection** - Always include. Minimum: first_name and email.
118
- 4. **Consent checkbox** - "I agree that my response may be used in published media coverage."
119
- 5. **End screen** - "Thanks for your response. The journalist will be in touch if they'd like to take things further."
120
-
121
- **Form settings:**
122
- - disable_back_navigation: false
123
- - show_step_counter: true
124
-
125
- **Two modes:**
126
- - One-shot: build immediately with sensible defaults when asked directly
127
- - Dialogue: ask about video-only/format and extra fields, show wireframe preview first
128
-
129
- Default to dialogue mode unless explicitly asked for one-shot.`,
219
+ {
220
+ role: "assistant",
221
+ content: {
222
+ type: "text",
223
+ 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.
224
+
225
+ ## Workflow
226
+
227
+ 1. **Define outcomes** - what segments or recommendations exist? (e.g., Basic/Pro/Enterprise, or product categories)
228
+ 2. **Create the form** with clipform_create_form:
229
+ - show_step_counter: false (funnels feel shorter without it)
230
+ - disable_back_navigation: true (prevents answer shopping that breaks scoring)
231
+ 3. **Add hook question** (type: "choice") - "What best describes you?" or "What are you looking for?" This segments the user.
232
+ 4. **Add qualifying questions** (type: "choice") - 2-3 questions that narrow down the need. Assign scores to each option.
233
+ 5. **Set branching logic** with clipform_set_question_logic - route based on answers
234
+ 6. **Add contact capture** (type: "contact") - name, email, phone. Place AFTER qualifying questions.
235
+ 7. **Update end screen** with score_ranges for personalised outcomes: "Based on your answers, we recommend..."
236
+ 8. **Publish** with clipform_update_form
237
+
238
+ ## Key rule: 3-5 questions max. Every extra step loses leads.
239
+
240
+ ## Before building, ask
241
+
242
+ 1. What outcomes are you routing to? (products, plans, team members, messages)
243
+ 2. What criteria determine the routing?
244
+ 3. Do you need contact capture?`,
245
+ },
130
246
  },
131
- },
132
- ],
133
- }));
247
+ ],
248
+ };
249
+ });
134
250
  }
135
251
  //# sourceMappingURL=prompts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,eAAe,CAAC,MAAiB;IAC/C,MAAM,CAAC,cAAc,CACnB,aAAa,EACb;QACE,KAAK,EAAE,qBAAqB;QAC5B,WAAW,EAAE,+DAA+D;KAC7E,EACD,KAAK,IAAI,EAAE,CAAC,CAAC;QACX,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAe;gBACrB,OAAO,EAAE,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,6BAA6B,EAAE;aACxE;YACD;gBACE,IAAI,EAAE,WAAoB;gBAC1B,OAAO,EAAE;oBACP,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mCAiDiB;iBACxB;aACF;SACF;KACF,CAAC,CACH,CAAC;IAEF,MAAM,CAAC,cAAc,CACnB,aAAa,EACb;QACE,KAAK,EAAE,qBAAqB;QAC5B,WAAW,EAAE,qEAAqE;KACnF,EACD,KAAK,IAAI,EAAE,CAAC,CAAC;QACX,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAe;gBACrB,OAAO,EAAE,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,6BAA6B,EAAE;aACxE;YACD;gBACE,IAAI,EAAE,WAAoB;gBAC1B,OAAO,EAAE;oBACP,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE;;;;;;;;;;;;iIAY+G;iBACtH;aACF;SACF;KACF,CAAC,CACH,CAAC;IAEF,MAAM,CAAC,cAAc,CACnB,uBAAuB,EACvB;QACE,KAAK,EAAE,0BAA0B;QACjC,WAAW,EAAE,mEAAmE;KACjF,EACD,KAAK,IAAI,EAAE,CAAC,CAAC;QACX,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAe;gBACrB,OAAO,EAAE,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,gDAAgD,EAAE;aAC3F;YACD;gBACE,IAAI,EAAE,WAAoB;gBAC1B,OAAO,EAAE;oBACP,IAAI,EAAE,MAAe;oBACrB,IAAI,EAAE;;;;;;;;;;;;;;;;;+DAiB6C;iBACpD;aACF;SACF;KACF,CAAC,CACH,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerResources(server: McpServer): void;