@clipform/mcp-server 4.2.1 → 4.2.2

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.
@@ -29,7 +29,7 @@ import {
29
29
  union,
30
30
  unknown,
31
31
  url
32
- } from "./chunk-QG354AVT.js";
32
+ } from "./chunk-APCG2RRD.js";
33
33
  import {
34
34
  GUIDE_TYPES,
35
35
  QUIZ_VARIANTS,
@@ -38,13 +38,18 @@ import {
38
38
  getGuideUri,
39
39
  guideFallbackText,
40
40
  registerResources
41
- } from "./chunk-RKMGVU57.js";
41
+ } from "./chunk-5AAKB4S5.js";
42
42
  import {
43
+ ANON_PREVIEW_LINK_LABEL,
43
44
  ANON_PREVIEW_NOTICE,
45
+ ANON_PREVIEW_ONLY_NOTICE,
46
+ CLAIM_URL_DESCRIPTION,
44
47
  FULL_CAPS,
48
+ PREVIEW_URL_DESCRIPTION,
45
49
  anonPreviewNotice,
46
- capsFromToolsets
47
- } from "./chunk-O4LOUBGW.js";
50
+ capsFromToolsets,
51
+ isPreviewViewerUrl
52
+ } from "./chunk-FGTU764J.js";
48
53
  import {
49
54
  ACTIVE_NODE_TYPES,
50
55
  CONTACT_FIELDS,
@@ -66,7 +71,7 @@ import {
66
71
  resolveFormType,
67
72
  structuredResult,
68
73
  textResult
69
- } from "./chunk-GZZGKW5A.js";
74
+ } from "./chunk-KZNBYHHR.js";
70
75
  import {
71
76
  getMcpAuth,
72
77
  runWithMcpAuth,
@@ -16314,7 +16319,7 @@ function registerCreateFormTool(server) {
16314
16319
  "clipform_create_form",
16315
16320
  {
16316
16321
  title: "Create Clipform",
16317
- description: `Create a new Clipform (interactive video-style form). Returns a viewer URL and form ID. When connected via an authenticated MCP client (e.g. claude.ai), the form lands directly in the user's workspace. Anonymous sessions build into a temporary account of their own: forms stay drafts with a private preview link. Some results also include a one-time claim URL that signs the user into that account; relay it when present.
16322
+ description: `Create a new Clipform (interactive video-style form). Returns a viewer URL and form ID (an anonymous build's link is a private preview where answers are not saved). When connected via an authenticated MCP client (e.g. claude.ai), the form lands directly in the user's workspace. Anonymous sessions build into a temporary account of their own: forms stay drafts with a private preview link. Some results also include a one-time claim URL that signs the user into that account; relay it when present.
16318
16323
 
16319
16324
  ${ONBOARDING_OFFER_TOOL_CUE}
16320
16325
 
@@ -16344,12 +16349,20 @@ Example: A form that asks a question, collects contact info, then finishes:
16344
16349
  }),
16345
16350
  outputSchema: external_exports.object({
16346
16351
  form_id: external_exports.string().describe("Form UUID - pass to follow-up tools"),
16347
- // #3330: an anonymous session's viewer_url is a scoped preview link
16348
- // (only the caller can open it) until the account is claimed and the
16349
- // form is published - only an authenticated/claimed workspace's
16350
- // create returns a genuinely live URL here.
16351
- viewer_url: external_exports.string().describe("Form URL for respondents - a live public URL once authenticated/published, or a scoped preview-only link for an anonymous session"),
16352
- dashboard_url: external_exports.string().optional().describe("Builder URL (sign in to edit)"),
16352
+ // #4845 expand/contract. The deployed ChatGPT schema is
16353
+ // additionalProperties:false, so emitting a field it does not declare
16354
+ // is invalid until the Rescan approves a schema that declares it.
16355
+ // Order: (a) deploy the schema declaring preview_url / claim_url /
16356
+ // is_preview as optional, emitting NOTHING new (this step);
16357
+ // (b) Rescan; (c) once approved, emit them; (d) a later Rescan-approved
16358
+ // CONTRACT step drops the anonymous viewer_url / dashboard_url
16359
+ // duplicates. See SUBMISSION.md. Until (c), structuredContent stays
16360
+ // key-for-key what origin/main emitted (approved-schema.test.ts).
16361
+ viewer_url: external_exports.string().describe("Form URL for respondents - the live public URL. For an anonymous build this is a private preview link: answers are not saved, do not share it with respondents."),
16362
+ dashboard_url: external_exports.string().optional().describe("Builder URL (sign in to edit). For an anonymous build this is a one-time claim link, not an editor."),
16363
+ is_preview: external_exports.boolean().optional().describe("True when this is an anonymous preview build (answers are not saved), false otherwise."),
16364
+ preview_url: external_exports.string().optional().describe(`Anonymous builds only. ${PREVIEW_URL_DESCRIPTION}`),
16365
+ claim_url: external_exports.string().optional().describe(`Anonymous builds only. ${CLAIM_URL_DESCRIPTION}`),
16353
16366
  nodes: external_exports.array(external_exports.object({ id: external_exports.string(), type: external_exports.string(), prompt: external_exports.string() })).describe("Created nodes in order - IDs for attach_node_media / update_node"),
16354
16367
  plan: external_exports.object({ name: external_exports.string(), auth_mode: external_exports.string() }).optional().describe("Plan and auth context for this session"),
16355
16368
  workspace_name: external_exports.string().optional().describe("Name of the workspace the form was created in")
@@ -16442,7 +16455,7 @@ Example: A form that asks a question, collects contact info, then finishes:
16442
16455
  });
16443
16456
  if (!createResult.ok) {
16444
16457
  const message = `Form NOT created (nothing was saved): ${createResult.error}`;
16445
- return isAnonymousAccountRequired(createResult) ? errorResult(message, authChallengeMeta("invalid_token", createResult.apiMessage ?? message)) : errorResult(message);
16458
+ return isAnonymousAccountRequired(createResult) ? errorResult(message, authChallengeMeta("insufficient_scope", createResult.apiMessage ?? message)) : errorResult(message);
16446
16459
  }
16447
16460
  const { data } = createResult;
16448
16461
  const formId = data.form_id;
@@ -16464,6 +16477,8 @@ Example: A form that asks a question, collects contact info, then finishes:
16464
16477
  const truncate2 = (s, n = 60) => s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
16465
16478
  const lines = [
16466
16479
  isPreview ? `Form created as a preview draft.` : `Form created and live.`,
16480
+ // #4845: lead with the in-chat path (wording defined once in lib/anon-notice.ts).
16481
+ ...isPreview ? [``, ANON_PREVIEW_ONLY_NOTICE] : [],
16467
16482
  ``,
16468
16483
  `Title: ${title}`,
16469
16484
  ...planContext?.is_authenticated && planContext.workspace_name ? [`Workspace: ${planContext.workspace_name}`] : [],
@@ -16472,18 +16487,18 @@ Example: A form that asks a question, collects contact info, then finishes:
16472
16487
  `Node IDs (use directly with attach_node_media / update_node - no get_form round-trip needed):`,
16473
16488
  ...createdNodes.map((n) => `- [${n.type}] "${truncate2(n.prompt)}": ${n.id}`),
16474
16489
  ``,
16475
- isPreview ? `PREVIEW URL (draft - only you can open this, not yet visible to respondents): ${data.viewer_url}` : `FORM URL (live - share this with respondents): ${data.viewer_url}`
16490
+ isPreview ? `PREVIEW URL (draft - answers are not saved, only you can open this, not for respondents): ${data.viewer_url}` : `FORM URL (live - share this with respondents): ${data.viewer_url}`
16476
16491
  ];
16477
16492
  if (formUrl) {
16478
16493
  lines.push(
16479
- isPreview ? `CLAIM URL (one-time - sign up to claim and publish): ${formUrl}` : `DASHBOARD URL (sign in to edit and manage): ${formUrl}`
16494
+ isPreview ? `CLAIM URL (fallback - or open this one-time link to sign up in the browser): ${formUrl}` : `DASHBOARD URL (sign in to edit and manage): ${formUrl}`
16480
16495
  );
16481
16496
  }
16482
16497
  const firstUrlLabel = isPreview ? "PREVIEW" : "FORM";
16483
16498
  const secondUrlLabel = isPreview ? "CLAIM" : "DASHBOARD";
16484
16499
  lines.push(
16485
16500
  ``,
16486
- isPreview ? `This form is a preview - claim and publish to start collecting responses.${formUrl ? " Open the CLAIM URL to sign up and claim the account this form already belongs to, then republish" : " Sign up to claim the account this form already belongs to and republish"} with clipform_update_form (is_live: true). The PREVIEW URL never becomes public on its own.` : `The form is live and accessible at the FORM URL above. Edits and media attached AFTER this point are NOT visible to respondents until you re-send is_live: true via clipform_update_form - republishing is the expected final step of a build, never skip it after attaching media/narration.`,
16501
+ isPreview ? `This form is a preview, not live. When the user says "publish it", call clipform_update_form (is_live: true); it prompts them to sign in. The PREVIEW URL never becomes public on its own.` : `The form is live and accessible at the FORM URL above. Edits and media attached AFTER this point are NOT visible to respondents until you re-send is_live: true via clipform_update_form - republishing is the expected final step of a build, never skip it after attaching media/narration.`,
16487
16502
  formUrl ? `The URLs above are exact values; constructed or guessed URLs return 404. Use the ${firstUrlLabel} URL and ${secondUrlLabel} URL as shown.` : `The ${firstUrlLabel} URL above is an exact value; constructed or guessed URLs return 404.`,
16488
16503
  `Pass form_id on follow-up tools (get_form, add_node, attach_node_media, etc.).`
16489
16504
  );
@@ -16954,7 +16969,11 @@ function formatFormState(data) {
16954
16969
  const urls = projectUrls(data);
16955
16970
  if (urls) {
16956
16971
  lines.push(``, `Links (exact values - constructed or guessed URLs return 404):`);
16957
- if (urls.viewer) lines.push(` Viewer (respondent view): ${urls.viewer}`);
16972
+ if (urls.viewer) {
16973
+ lines.push(
16974
+ isPreviewViewerUrl(urls.viewer) ? ` ${ANON_PREVIEW_LINK_LABEL}: ${urls.viewer}` : ` Viewer (respondent view): ${urls.viewer}`
16975
+ );
16976
+ }
16958
16977
  if (urls.builder) lines.push(` Builder: ${urls.builder}`);
16959
16978
  if (urls.summary) lines.push(` Summary: ${urls.summary}`);
16960
16979
  if (urls.responses) lines.push(` Responses: ${urls.responses}`);
@@ -16963,6 +16982,7 @@ function formatFormState(data) {
16963
16982
  if (urls.embed) lines.push(` Embed: ${urls.embed}`);
16964
16983
  if (urls.dynamic_link) lines.push(` Dynamic link: ${urls.dynamic_link}`);
16965
16984
  }
16985
+ if (isPreviewViewerUrl(urls?.viewer)) lines.push(``, ANON_PREVIEW_ONLY_NOTICE);
16966
16986
  lines.push(``, `Nodes (in order):`);
16967
16987
  for (let i = 0; i < nodes.length; i++) {
16968
16988
  const q = nodes[i];
@@ -17003,7 +17023,7 @@ function registerGetFormTool(server) {
17003
17023
  is_live: external_exports.boolean().describe("Whether the form is published"),
17004
17024
  has_unpublished_changes: external_exports.boolean().describe("Live form has edits not yet republished"),
17005
17025
  urls: external_exports.object({
17006
- viewer: external_exports.string().optional().describe("Live respondent link, or a scoped preview link for an anonymous caller's draft. Omitted for a non-anonymous caller's unpublished draft."),
17026
+ viewer: external_exports.string().optional().describe("Live respondent link, or - for an anonymous caller's draft - a private preview link where answers are not saved (do not share it with respondents). Omitted for a non-anonymous caller's unpublished draft."),
17007
17027
  builder: external_exports.string().optional().describe("Build tab - the node/canvas editor. Not present for an anonymous caller."),
17008
17028
  summary: external_exports.string().optional().describe("Summary tab - form overview and key stats. Not present for an anonymous caller."),
17009
17029
  responses: external_exports.string().optional().describe("Responses tab - individual submissions. Not present for an anonymous caller."),
@@ -17112,7 +17132,7 @@ function registerUpdateFormTool(server) {
17112
17132
  if (!result.ok) {
17113
17133
  return isAnonymousAccountRequired(result) ? errorResult(
17114
17134
  result.error,
17115
- authChallengeMeta("invalid_token", result.apiMessage ?? result.error)
17135
+ authChallengeMeta("insufficient_scope", result.apiMessage ?? result.error)
17116
17136
  ) : errorResult(result.error);
17117
17137
  }
17118
17138
  }
@@ -17124,7 +17144,7 @@ function registerUpdateFormTool(server) {
17124
17144
  if (!tagResult.ok) {
17125
17145
  return isAnonymousAccountRequired(tagResult) ? errorResult(
17126
17146
  tagResult.error,
17127
- authChallengeMeta("invalid_token", tagResult.apiMessage ?? tagResult.error)
17147
+ authChallengeMeta("insufficient_scope", tagResult.apiMessage ?? tagResult.error)
17128
17148
  ) : errorResult(tagResult.error);
17129
17149
  }
17130
17150
  }
@@ -20147,4 +20167,4 @@ export {
20147
20167
  * MIT Licensed
20148
20168
  *)
20149
20169
  */
20150
- //# sourceMappingURL=chunk-BKTCQ5HS.js.map
20170
+ //# sourceMappingURL=chunk-XOV5ILOJ.js.map