@clipform/mcp-server 1.31.0 → 1.33.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
@@ -49,6 +49,13 @@ You can also pass the key as a CLI flag: `npx -y @clipform/mcp-server --api-key=
49
49
 
50
50
  **Anonymous mode** (no API key): Forms go into a shared workspace with the free-tier 3-node limit. You'll get a claim URL to move forms into your account.
51
51
 
52
+ ### Environment variables
53
+
54
+ | Variable | Required | Purpose |
55
+ |----------|----------|---------|
56
+ | `CLIPFORM_API_KEY` | For workspace auth | Bearer key - forms land in the key's workspace with its plan limits. Also unlocks the creative tools (search, TTS, video rendering). Omit for anonymous mode. |
57
+ | `API_URL` | Yes | Clipform API base URL (`https://api.clipform.io`, or your local/self-hosted API). |
58
+
52
59
  ## Tools
53
60
 
54
61
  ### Form & node management
@@ -77,12 +84,12 @@ You can also pass the key as a CLI flag: `npx -y @clipform/mcp-server --api-key=
77
84
 
78
85
  | Tool | Description |
79
86
  |------|-------------|
80
- | `clipform_render_composition` | Render a video composition to MP4, PNG, or GIF (async - returns job ID) |
87
+ | `clipform_render_composition` | Render a video composition to MP4 or PNG (waits, returns URL) |
81
88
  | `clipform_generate_tts` | Generate narration audio with word-level captions |
82
- | `clipform_generate_slideshow` | Create slideshow videos from images + audio (async - returns job ID) |
83
- | `clipform_generate_video` | Generate video from images, clips, or both synced to audio (async - returns job ID) |
84
- | `clipform_check_render` | Check status of an async render job |
85
- | `clipform_search_media` | Search royalty-free images and videos |
89
+ | `clipform_generate_slideshow` | Create slideshow videos from images + audio (waits, returns URL) |
90
+ | `clipform_generate_video` | Generate video from images, clips, or both synced to audio. Pass `wait: false` to fire renders in parallel and poll for results |
91
+ | `clipform_check_render` | Check status of a render started with `wait: false` |
92
+ | `clipform_search_media` | Search royalty-free images and videos, with orientation filter and alt-text descriptions |
86
93
  | `clipform_search_music` | Search royalty-free music and ambient sounds |
87
94
  | `clipform_list_compositions` | List available video compositions and prop schemas |
88
95
  | `clipform_list_assets` | List available sound effects, animations, and fonts |
@@ -2,7 +2,7 @@ import {
2
2
  ALL_VARIANTS,
3
3
  FORM_TYPE_KEYS,
4
4
  getSessionContext
5
- } from "./chunk-NHFRYNJ3.js";
5
+ } from "./chunk-KWEBCAPF.js";
6
6
 
7
7
  // src/resources.ts
8
8
  var GUIDE_TYPES = FORM_TYPE_KEYS;
@@ -30,18 +30,18 @@ Search for the specific subject, not generic terms ("komodo dragon habitat" not
30
30
  For timeless topics (history, geography, science), write from your own knowledge. For anything recent or uncertain, use web search or clipform_search_news. If neither is available, refuse rather than fabricate.`;
31
31
  var MEDIA_WORKFLOW = `## Media Workflow
32
32
 
33
- 1. **clipform_generate_tts** - narration audio (returns word-level captions)
34
- 2. **clipform_search_media** (kind: "image") - find 3 images per question
35
- 3. **clipform_generate_video** - Ken Burns video from images + audio
36
- 4. **clipform_upload_node_media** - attach video with captions (set show_captions: true)
33
+ 1. **clipform_generate_tts** - narration audio (returns word-level captions) - batch ALL items in one call
34
+ 2. **clipform_search_media** (kind: "image") - find 3 images per question - batch ALL queries in one call
35
+ 3. **clipform_generate_video** - Ken Burns video from images + audio. Rendering more than one video? Pass wait: false on EVERY call (each returns a job ID immediately, renders run in parallel), then collect URLs with clipform_check_render.
36
+ 4. **clipform_upload_node_media** - attach video with captions (set show_captions: true) - batch all uploads in one call
37
37
 
38
38
  **Image selection:**
39
39
  - ONLY use URLs from clipform_search_media results
40
40
  - ALL returned images are pre-cleared for commercial use - pick by visual quality, not by provider or license
41
41
  - **Question media must not accidentally reveal the answer.** Unless the media IS the clue (emoji quiz, guess-the-flag - then choose it deliberately for difficulty), search the question's category or scene-setting context, not the answer. Answer-specific media belongs on the reveal. Test: could someone who knows nothing pick the right option just from the media?
42
42
  - For non-question media (intros, reveals, surveys), search the specific subject, not generic terms
43
- - Pick visually distinct images (different angles, colors, subjects)
44
- - Landscape images work best for pan effects
43
+ - Pick visually distinct images (different angles, colors, subjects) - use each result's description/alt text to judge
44
+ - Portrait images fill the 9:16 frame best and results come portrait-first by default; landscape images get automatic blur-pad framing. Request orientation: "landscape" only when you specifically want wide shots.
45
45
 
46
46
  **Slideshow defaults:**
47
47
  - 3 images per question, random_effects: true
@@ -535,4 +535,4 @@ export {
535
535
  getGuideUri,
536
536
  registerResources
537
537
  };
538
- //# sourceMappingURL=chunk-J4KJN4C4.js.map
538
+ //# sourceMappingURL=chunk-JID43EDM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/resources.ts"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { getSessionContext } from \"./lib/session-context.js\";\nimport { FORM_TYPE_KEYS, FORM_TYPES, ALL_VARIANTS } from \"@vid-master/config\";\n\nexport const GUIDE_TYPES = FORM_TYPE_KEYS as readonly string[] as readonly [string, ...string[]];\nexport type GuideType = (typeof FORM_TYPE_KEYS)[number];\n\nexport const QUIZ_VARIANTS = ALL_VARIANTS as readonly string[] as readonly [string, ...string[]];\nexport type QuizVariant = (typeof ALL_VARIANTS)[number];\n\nconst WRITING_PRINCIPLES = `## Writing Principles\n\n- **Write for the ear.** Narration is spoken aloud. Short sentences. Natural rhythm.\n- **Research before writing.** Find 2-3 genuinely interesting facts per topic. Generic content doesn't hold attention.\n- **Conversational, not encyclopaedic.** \"Here's what's wild about this...\" not \"The subject is characterized by...\"\n- **Cut ruthlessly.** Every word must earn its place.\n- **Never reveal answers in narration.** The user picks from options - narration teases and builds intrigue.\n- **Don't read answer options aloud.** The viewer can see them on screen.\n\n## Narration Tips\n\n- Tease the topic, don't summarise it\n- Give one interesting fact that makes the user curious\n- 5-15 seconds per question narration\n- If TTS comes back too long, trim the copy and regenerate\n\n## Research\n\nSearch for the specific subject, not generic terms (\"komodo dragon habitat\" not \"reptile\"). Cross-reference facts - quiz answers must be correct. Look for the surprising angle: what would make someone say \"wait, really?\"\n\nFor timeless topics (history, geography, science), write from your own knowledge. For anything recent or uncertain, use web search or clipform_search_news. If neither is available, refuse rather than fabricate.`;\n\nconst MEDIA_WORKFLOW = `## Media Workflow\n\n1. **clipform_generate_tts** - narration audio (returns word-level captions) - batch ALL items in one call\n2. **clipform_search_media** (kind: \"image\") - find 3 images per question - batch ALL queries in one call\n3. **clipform_generate_video** - Ken Burns video from images + audio. Rendering more than one video? Pass wait: false on EVERY call (each returns a job ID immediately, renders run in parallel), then collect URLs with clipform_check_render.\n4. **clipform_upload_node_media** - attach video with captions (set show_captions: true) - batch all uploads in one call\n\n**Image selection:**\n- ONLY use URLs from clipform_search_media results\n- ALL returned images are pre-cleared for commercial use - pick by visual quality, not by provider or license\n- **Question media must not accidentally reveal the answer.** Unless the media IS the clue (emoji quiz, guess-the-flag - then choose it deliberately for difficulty), search the question's category or scene-setting context, not the answer. Answer-specific media belongs on the reveal. Test: could someone who knows nothing pick the right option just from the media?\n- For non-question media (intros, reveals, surveys), search the specific subject, not generic terms\n- Pick visually distinct images (different angles, colors, subjects) - use each result's description/alt text to judge\n- Portrait images fill the 9:16 frame best and results come portrait-first by default; landscape images get automatic blur-pad framing. Request orientation: \"landscape\" only when you specifically want wide shots.\n\n**Slideshow defaults:**\n- 3 images per question, random_effects: true\n- transition: { type: \"fade\", duration: 1 }\n- Auto focal point detection, eased motion, and cinematic vignette are built in`;\n\nconst GUIDE_DESCRIPTIONS: Record<GuideType, string> = {\n \"quiz\": \"Craft knowledge for writing engaging quizzes - difficulty curves, question psychology, narration style, scoring\",\n \"survey\": \"Craft knowledge for feedback surveys, NPS, and research forms - brevity, rating scales, respondent fatigue\",\n \"interview\": \"Craft knowledge for building interview forms - warm-up pacing, open questions, consent, video responses\",\n \"funnel\": \"Craft knowledge for lead qualification funnels - planned feature, conditional routing coming soon\",\n \"testimonial\": \"Craft knowledge for collecting testimonials and customer stories on video - storytelling prompts, comfort techniques, consent\",\n \"application\": \"Craft knowledge for application and evaluation forms - multi-section structure, video responses for behavioural questions, screening\",\n \"booking\": \"Craft knowledge for event registration and booking forms - minimal friction, video welcome, confirmation flow\",\n};\n\nconst QUIZ_VARIANT_DESCRIPTIONS: Record<QuizVariant, string> = {\n \"personality\": \"Addendum for personality quizzes - category design, option weighting, outcome writing, no right/wrong answers\",\n \"comprehension\": \"Addendum for YouTube comprehension quizzes - extracting questions from transcripts, distractor design, audience adaptation\",\n};\n\nconst GUIDE_CONTENT: Record<GuideType, string> = {\n \"quiz\": `# Quiz Writing Guide\n\n## Psychology\n\nEach question is a micro variable-reward event - the same dopamine loop that keeps people watching. Once someone answers 2-3 questions, sunk cost kicks in and they finish. Viewers mentally compete, then want to compare scores.\n\n**Target 50-60% correct.** Too easy = no challenge. Too hard = people feel stupid and won't share.\n\n## Difficulty Curve\n\n| Position | Difficulty | Purpose |\n|----------|-----------|---------|\n| Q1-Q2 | Easy (80%+ get right) | Build confidence and commitment |\n| Q3-Q5 | Medium | Peak engagement |\n| Q6-Q8 | Hard (include one \"gotcha\") | The \"everyone gets this wrong\" moment |\n| Q9-Q10 | One hard, one satisfying medium | End on a smart feeling, not defeat |\n\n## Question Design\n\n- **No guessable answer pattern** - set randomise_options: true and the viewer shuffles options at render time. Do NOT hand-vary correct answer positions; it has no effect once options shuffle.\n- **Myth-busters**: \"Sushi means raw fish - True or False?\" (False - it means seasoned rice)\n- **Sounds fake but true**: counterintuitive correct answers make people rewatch\n- **Common misconceptions**: \"Capital of Australia?\" (not Sydney - Canberra)\n- Under 12-15 words per question for mobile readability\n- Trigger gut reactions, not deep thinking\n\n## Wrong Answer Generation\n\nFor 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.\n\n## Color Brain Questions (ColorCards composition)\n\nInspired 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.\n\n**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.\n\n**Question categories:**\n- Flags: \"Which country's flag has these colours?\" (pair with FlagReveal for answer)\n- Brand logos: red + yellow = McDonald's, red + white = Coca-Cola\n- Sports teams: red + white = Arsenal, red + blue + white = Barcelona\n- Superheroes/characters: red + blue = Spider-Man, yellow + black = Batman\n- Food: red + green = watermelon, yellow + brown = banana\n\n**Difficulty scaling:**\n- Easy: iconic subjects with unique colour combos (Japan flag: red + white)\n- Medium: common subjects but colours shared with others (Italy vs Ireland: both green + white + one more)\n- Hard: obscure subjects or very common colour combos that fit many answers\n\n**Design rules:**\n- 2-4 colours per question works best. 5+ gets messy and hard to distinguish.\n- If two answer options would produce identical swatches, don't use that question.\n- Pair with a reveal composition (FlagReveal, image, or text) for the answer.\n\n## Settings\n\n- show_step_counter: true\n- disable_back_navigation: true (prevent going back to change answers after seeing feedback)\n\n## Narration Style\n\nYou're a quiz master, not a question reader. Each question's narration should:\n\n1. **Tease** - set the scene, build intrigue (\"This one catches everyone out\")\n2. **Give context** - one interesting fact that makes the question richer\n3. **Pose the question** - \"So here's the question...\"\n\n**Don't say:**\n- \"You either know it or you don't\" (meaningless filler)\n- \"This is a really hard one\" on every question (loses impact)\n- \"Welcome to my quiz\" / \"Hey guys\" (wastes time, skip to Q1)\n\n${WRITING_PRINCIPLES}\n\n${MEDIA_WORKFLOW}`,\n\n \"survey\": `# Survey & Feedback Guide\n\n## Purpose\n\nCollect structured feedback - NPS, customer satisfaction, product research, post-event feedback. The goal is clean, analysable data with minimal respondent fatigue.\n\n## Structure\n\nSurveys should be ruthlessly short. Every extra question costs you completions.\n\n1. **Key metric** - the one number you care about (NPS, satisfaction rating, likelihood to recommend). Put it first while attention is highest.\n2. **Follow-up** - one open-ended \"why?\" question. \"What's the main reason for your score?\" This is where the insight lives.\n3. **Specific questions** (optional) - 2-3 targeted choice questions on specific areas. Don't fish - only ask what you'll act on.\n4. **Contact** (optional) - only if you need to follow up. Many surveys are better anonymous.\n5. **End screen** - \"Thanks for your feedback!\" Keep it simple.\n\n## Question Design\n\n- **Choice questions for data, open questions for insight.** Don't use open-ended where a rating scale would do, and don't use ratings where you need to understand why.\n- **Balanced scales.** Equal positive and negative options. \"Excellent / Good / Fair / Poor\" not \"Amazing / Great / Good / OK / Bad.\"\n- **No leading questions.** \"How much did you enjoy...?\" assumes they enjoyed it.\n- **5 questions max.** If you need more, you're running research, not a survey - split it up.\n\n## Narration for Surveys\n\nOptional - many surveys work fine as text only. If using narration:\n\n- Keep it brief (5-8 seconds). \"We'd love your quick feedback on...\"\n- Don't narrate every question - just the opener to set the tone\n- Friendly but efficient. Respect their time.\n\n## Settings\n\n- disable_back_navigation: false\n- show_step_counter: true (shows progress, reduces abandonment)\n\n${WRITING_PRINCIPLES}`,\n\n \"interview\": `# Interview Guide\n\n## Purpose\n\nCollect responses through structured conversations - journalist callouts, async video interviews, expert input, case study interviews. You're guiding someone through questions, with their answers as structured data or video responses.\n\n## Structure\n\n1. **Warm-up question** - something easy and low-stakes to get them comfortable. \"Tell us your name and what you do\" or \"What's your role?\"\n2. **Core questions** - the real ask. Open-ended, one topic per question. Don't over-split - 2-3 core questions max.\n3. **Follow-up** (optional) - \"Anything else you'd like to add?\" catches things you didn't think to ask.\n4. **Contact collection** - first name + email minimum. Add phone/company if relevant.\n5. **Consent** - \"I agree that my response may be used in [context].\" Always include for media use.\n6. **End screen** - set expectations: \"Thanks! We'll be in touch if we'd like to take things further.\"\n\n## Question Design\n\n- **Open-ended by default.** Use \"open\" type with text + audio + video response enabled. Let the respondent choose their format.\n- **One topic per question.** \"Tell us about your experience AND what you'd change\" is two questions.\n- **Prompt, don't interrogate.** \"What surprised you most about working with us?\" beats \"Rate your satisfaction.\"\n- **Keep it short.** 3-5 questions total. Every extra question loses respondents.\n\n## Narration for Interviews\n\nWarmer and more personal than quiz narration. You're inviting someone to share, not testing them.\n\n- \"We'd love to hear your perspective...\"\n- \"Take your time with this one - there's no wrong answer\"\n- Keep narration under 10 seconds - the respondent's answer is the content, not yours\n\n## Settings\n\n- disable_back_navigation: false (let people review their answers)\n- show_step_counter: true (so they know how much is left)\n\n${WRITING_PRINCIPLES}\n\n${MEDIA_WORKFLOW}`,\n\n \"funnel\": `# Lead Qualification Funnel - Coming Soon\n\nFunnel capabilities with conditional routing and branching logic are planned but not yet available. This guide will be expanded when the branching feature launches.\n\nIn the meantime, you can build a simple linear qualification form using the survey or quiz workflows.`,\n\n \"testimonial\": `# Testimonial & Story Collection Guide\n\n## Purpose\n\nCapture authentic stories from customers, users, or participants on camera. The respondent's video IS the deliverable - you're producing content, not collecting data. The questions are storytelling prompts designed to elicit a compelling narrative arc.\n\n## Storytelling Arc\n\nThe best testimonials follow a three-part structure. Each prompt maps to one part:\n\n1. **Before** - \"What was your situation before?\" (establishes the problem)\n2. **During** - \"What was the experience like?\" (the journey)\n3. **After** - \"What's different now?\" (the transformation/result)\n\nThis gives you a complete story you can edit into marketing content.\n\n## Prompt Design\n\n- **Open-ended, video-first.** Every core question should be type \"open\" with video response enabled.\n- **One idea per prompt.** \"Tell us about your experience and what you'd recommend\" is two prompts.\n- **Warm and specific.** \"What surprised you most about working with us?\" beats \"Tell us about your experience.\"\n- **3-5 prompts max.** Fewer = higher completion. The best testimonials come from 3 focused questions.\n- **Don't ask for ratings.** This isn't a survey. You want stories, not numbers.\n\n## Making Respondents Comfortable\n\n- Start with something easy: name, role, how long they've been a customer\n- Narration should feel like a friendly conversation, not a formal interview\n- \"Take your time - there's no rush and no wrong answer\"\n- Keep narration under 8 seconds per prompt - the respondent's answer is the content\n\n## Consent\n\nAlways include a consent node for testimonials. The response will be used in marketing, on your website, or in sales materials. Be explicit about where it may appear.\n\n## Settings\n\n- disable_back_navigation: false (let people re-record if they weren't happy)\n- show_step_counter: true (shows progress, reduces surprise)\n\n## Narration Style\n\nWarm, appreciative, and encouraging:\n- \"We'd love to hear your story...\"\n- \"What you share will help others just like you\"\n- \"There's no script - just speak from the heart\"\n\nDo NOT say \"this will only take a minute\" (it won't) or \"just a quick video\" (undermines the ask).\n\n${WRITING_PRINCIPLES}\n\n${MEDIA_WORKFLOW}`,\n\n \"application\": `# Application & Evaluation Guide\n\n## Purpose\n\nCollect structured applications - job applications, programme admissions, grant proposals, course enrolments. Applications mix structured data (contact, qualifications) with open-ended responses (motivation, experience). Video responses reveal personality and communication skills that text can't capture.\n\n## Structure\n\nApplications have a natural multi-section flow:\n\n1. **Identity** - name, email, phone. Get this first while motivation is highest.\n2. **Qualifications** - role-specific structured questions (experience level, skills, availability). Choice or short text.\n3. **Open responses** - \"Tell us about a time when...\" or \"Why are you interested?\" Video encouraged for behavioural questions.\n4. **Supporting material** (optional) - \"Anything else you'd like to share?\"\n5. **Confirmation** - clear next steps. \"We'll review your application and get back to you within X days.\"\n\n## Question Design\n\n- **Structured for screening, open for depth.** Use choice questions to filter (experience level, availability), open questions to understand (motivation, fit).\n- **Video for behavioural questions.** \"Walk us through how you'd approach...\" is far more revealing on video than in text.\n- **One question per screen.** Applications feel long - keep each step focused.\n- **8-10 questions max.** Even for detailed applications. Every extra question increases abandonment.\n- **Don't duplicate the CV.** Ask for what a resume doesn't show: motivation, culture fit, communication style.\n\n## Narration for Applications\n\nProfessional and welcoming. You're the organisation's first impression.\n\n- \"Thanks for your interest - let's get to know you\"\n- \"Take your time with this one\"\n- Keep narration factual and brief - applicants want to know what's expected, not be entertained\n\n## Settings\n\n- disable_back_navigation: false (applicants need to review and revise)\n- show_step_counter: true (essential - applicants need to know how much is left)\n\n${WRITING_PRINCIPLES}\n\n${MEDIA_WORKFLOW}`,\n\n \"booking\": `# Booking & Registration Guide\n\n## Purpose\n\nEvent registration, course signup, workshop booking, consultation scheduling. The goal is minimal friction to confirmed attendance. Video makes registrations feel personal - a welcome video from the host builds anticipation and reduces no-shows.\n\n## Structure\n\nRegistrations should be fast. Every field you add costs completions.\n\n1. **Welcome** - a short video or narrated intro that sells the event and sets expectations. This is where video adds the most value.\n2. **Essential info** - name, email. Absolute minimum.\n3. **Event-specific details** (if needed) - dietary requirements, session preferences, t-shirt size. Only ask what you'll actually use.\n4. **Confirmation** - \"You're registered! Here's what to expect.\" Include date, time, location/link.\n\n## Question Design\n\n- **3-5 fields maximum.** Name + email + 1-3 event-specific fields. That's it.\n- **Choice questions over open-ended.** \"Which session interests you most?\" not \"What are you hoping to learn?\" You need structured data for logistics.\n- **Contact is mandatory.** This isn't anonymous - you need to confirm their spot.\n- **Don't over-collect.** Phone number, company, job title - only if you genuinely need them for the event.\n\n## Video Welcome\n\nThe registration's killer feature. A 15-30 second video from the host:\n- Who you are and what the event is about\n- What attendees will get out of it\n- Why you're excited about it\n\nThis turns a boring form into a personal invitation. Completion rates go up when people feel welcomed.\n\n## Narration Style\n\nEnthusiastic but efficient. Respect their time.\n- \"We're excited to have you!\"\n- \"Just a few quick details and you're in\"\n- Keep total narration under 20 seconds across the whole form (excluding the welcome video)\n\n## Settings\n\n- disable_back_navigation: false\n- show_step_counter: false (it's so short, a counter adds unnecessary UI)\n\n${WRITING_PRINCIPLES}`,\n};\n\nconst QUIZ_VARIANT_ADDONS: Record<QuizVariant, string> = {\n \"personality\": `---\n\n# Personality Quiz Addendum\n\n> The following sections supplement the base quiz guide above. For personality quizzes, scoring works differently (categories, not right/wrong) and there is no answer feedback. Where guidance below conflicts with the base (e.g., narration style), follow the personality-specific version.\n\n## How Personality Quizzes Work\n\nPersonality quizzes have NO correct answers. Each option should feel equally valid and appealing. The experience is about self-expression and fun, not testing knowledge. Always set show_answer_feedback: false.\n\n**NOTE:** Full category-based scoring (where each option maps to outcome categories via the \\`scores\\` field and the winning category determines a personalised result screen) is coming soon. For now, design personality quizzes as engaging choice-based forms with a single end screen. The questions themselves are the experience.\n\n## Category Design (Conceptual)\n\nEven without automated scoring, design your questions around 3-5 outcome categories:\n\n- **3-5 categories** is the sweet spot. Fewer feels too binary, more feels random.\n- Categories should be **distinct but equally appealing**. Nobody wants to get the \"bad\" result.\n- Name them after the outcome, not the trait: \"Explorer\" not \"Adventurous\", \"The Architect\" not \"Organised\".\n- Use categories to guide question and option design, even though automated scoring is not yet available.\n\n## Personality Question Design\n\n- Questions should feel **personally revealing** but low-stakes: \"Pick your ideal Saturday morning\" not \"What's your biggest weakness?\"\n- Scenario-based questions work better than abstract preference questions\n- Each question should meaningfully differentiate between categories\n- Avoid questions where all options clearly map to one obvious personality\n\n## End Screen\n\nWrite a single end screen that feels personal and shareable:\n\n- **Title**: Something warm and inviting, like \"Thanks for playing!\" or \"That was fun!\"\n- **Message**: Encourage sharing - \"Compare your answers with friends!\" Personality quizzes are inherently social.\n- show_score: false (there is no score to show)\n- show_share_button: true (personality results are inherently shareable)\n- NOTE: Automated result screens per category (scoring_results) are coming soon.\n\n## Personality Narration Style\n\nReflective and curious, not quizmaster-y:\n- \"This one says a lot about you...\"\n- \"There's no wrong answer here - go with your gut\"\n- \"What does your choice reveal?\"\n\nDo NOT say \"let's see if you get this right\" - there is no right answer.`,\n\n \"comprehension\": `---\n\n# Comprehension Quiz Addendum\n\n> The following sections supplement the base quiz guide above. Comprehension quizzes test whether someone watched a specific piece of content, not general knowledge. Scoring and settings match the base quiz, but question sourcing and distractor design are completely different.\n\n## How Comprehension Differs from Trivia\n\nA trivia quiz tests general knowledge. A comprehension quiz tests whether someone watched a specific piece of content. The questions should be **unfair to non-watchers and fair to watchers**.\n\n| | Trivia Quiz | Comprehension Quiz |\n|---|---|---|\n| Source | Research + your knowledge | The video transcript |\n| Questions | General facts | Specific claims from the video |\n| Wrong answers | Common misconceptions | Things you'd guess without watching |\n| Goal | Entertainment + learning | Proof of watching + retention |\n\n## Extracting Questions from Transcripts\n\nRead the transcript looking for:\n\n1. **Specific numbers or data** - \"The presenter says it takes X days to...\" (detail recall)\n2. **Causal claims** - \"According to the video, this happens because...\" (comprehension)\n3. **Examples used** - \"What example does the presenter use to explain...?\" (attention)\n4. **Sequence of topics** - \"What does the presenter discuss right after...?\" (structure following)\n5. **The main argument** - \"What is the presenter's main point about...?\" (thesis comprehension)\n6. **Counterintuitive points** - anything the presenter says is surprising or commonly misunderstood\n\n## Distractor Design (Wrong Answers)\n\nMake wrong answers plausible to someone who **didn't watch**:\n\n- Use correct facts from other sources about the same topic (tests whether they watched THIS video)\n- Include things that sound likely based on the title alone\n- For number questions, use nearby values that seem reasonable\n- Never include obviously joke answers - every option should feel possible\n\n## Audience Adaptation\n\n| Audience | Question style | Language | Count |\n|----------|---------------|----------|-------|\n| Young children (5-8) | Concrete details, visual moments | Simple, short sentences | 4-6 |\n| Older children (9-12) | Details + basic inference | Clear, direct | 6-8 |\n| Teens (13-17) | Inference + sequence + argument | Natural, conversational | 6-10 |\n| Adults | Full range including critical analysis | Match the video's register | 6-10 |\n\nFor young children: focus on \"What did you SEE?\" and \"Who did what?\" rather than abstract arguments.\n\n## Comprehension Narration Style\n\nReference the video naturally but don't spoil:\n\n- \"If you were paying attention during the bit about...\"\n- \"This is one of those details most people miss...\"\n- \"The presenter made a really specific claim here...\"`,\n};\n\nexport function getGuideContent(type: GuideType, variant?: QuizVariant): string {\n const base = GUIDE_CONTENT[type];\n if (type === \"quiz\" && variant) {\n return base + \"\\n\\n\" + QUIZ_VARIANT_ADDONS[variant];\n }\n return base;\n}\n\nexport function getGuideUri(type: GuideType, variant?: QuizVariant): string {\n if (type === \"quiz\" && variant) {\n return `clipform://guides/quiz/${variant}`;\n }\n return `clipform://guides/${type}`;\n}\n\nexport function registerResources(server: McpServer) {\n for (const type of GUIDE_TYPES) {\n server.registerResource(\n `guide-${type}`,\n getGuideUri(type),\n {\n description: GUIDE_DESCRIPTIONS[type],\n mimeType: \"text/markdown\",\n annotations: { audience: [\"assistant\" as const], priority: 0.8 },\n },\n async () => ({\n contents: [{\n uri: getGuideUri(type),\n mimeType: \"text/markdown\",\n text: getGuideContent(type),\n }],\n }),\n );\n }\n\n for (const variant of QUIZ_VARIANTS) {\n server.registerResource(\n `guide-quiz-${variant}`,\n getGuideUri(\"quiz\", variant),\n {\n description: QUIZ_VARIANT_DESCRIPTIONS[variant],\n mimeType: \"text/markdown\",\n annotations: { audience: [\"assistant\" as const], priority: 0.8 },\n },\n async () => ({\n contents: [{\n uri: getGuideUri(\"quiz\", variant),\n mimeType: \"text/markdown\",\n text: getGuideContent(\"quiz\", variant),\n }],\n }),\n );\n }\n\n server.registerResource(\n \"context-session\",\n \"clipform://context/session\",\n {\n description:\n \"Current session info: auth mode, workspace, plan tier, node limits, feature flags. Read this before planning content to know your constraints.\",\n mimeType: \"text/markdown\",\n annotations: { audience: [\"assistant\"], priority: 1.0 },\n },\n async () => {\n const text = await getSessionContext();\n return {\n contents: [\n {\n uri: \"clipform://context/session\",\n mimeType: \"text/markdown\",\n text: text || \"Session context unavailable - API may not be reachable.\",\n },\n ],\n };\n }\n );\n}\n"],"mappings":"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","names":[]}
@@ -1926,9 +1926,6 @@ async function callApi(path, options = {}) {
1926
1926
  "Content-Type": "application/json",
1927
1927
  ...getAuthHeaders()
1928
1928
  };
1929
- if (path.startsWith("/internal/") && process.env.INTERNAL_SERVICE_SECRET) {
1930
- headers["X-Service-Secret"] = process.env.INTERNAL_SERVICE_SECRET;
1931
- }
1932
1929
  const fetchOptions = {
1933
1930
  method,
1934
1931
  headers,
@@ -2001,15 +1998,18 @@ async function getSessionContextWithAuth() {
2001
1998
  }
2002
1999
  if (!result.ok) return { text: "", authMode: "anonymous" };
2003
2000
  const me = result.data;
2004
- const authMode = me.auth_mode === "oauth" ? "oauth" : "anonymous";
2001
+ const apiAuthMode = me.auth_mode ?? "anonymous";
2002
+ const authMode = apiAuthMode === "anonymous" ? "anonymous" : "authenticated";
2005
2003
  const plan = me.plan;
2006
2004
  const lines = [];
2007
2005
  lines.push("## Your Session");
2008
- if (authMode === "oauth") {
2009
- lines.push(`Auth: OAuth (connected)`);
2006
+ if (authMode === "authenticated") {
2007
+ lines.push(
2008
+ apiAuthMode === "api_key" ? "Auth: API key (workspace-scoped)" : "Auth: signed in (connected to a Clipform account)"
2009
+ );
2010
2010
  lines.push(`Workspace: ${me.workspace?.name ?? "Unknown"} (${me.workspace?.id ?? "?"})`);
2011
- lines.push(`User: ${me.user_id ?? "?"}`);
2012
- lines.push(`Company: ${me.company_id ?? "none"}`);
2011
+ if (me.user_id) lines.push(`User: ${me.user_id}`);
2012
+ if (me.company_id) lines.push(`Company: ${me.company_id}`);
2013
2013
  } else {
2014
2014
  lines.push("Auth: anonymous (not connected to a Clipform account)");
2015
2015
  if (me.workspace) {
@@ -2030,7 +2030,7 @@ async function getSessionContextWithAuth() {
2030
2030
  if (plan.custom_theme === false) {
2031
2031
  lines.push("Custom themes: not available on this plan");
2032
2032
  }
2033
- if (authMode !== "oauth") {
2033
+ if (authMode === "anonymous") {
2034
2034
  lines.push(`
2035
2035
  To unlock your full plan: sign in to your Clipform account.`);
2036
2036
  }
@@ -2064,4 +2064,4 @@ export {
2064
2064
  getSessionContextWithAuth,
2065
2065
  getSessionContext
2066
2066
  };
2067
- //# sourceMappingURL=chunk-NHFRYNJ3.js.map
2067
+ //# sourceMappingURL=chunk-KWEBCAPF.js.map