@clipform/mcp-server 2.6.0 → 2.10.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.
@@ -6,7 +6,7 @@ import {
6
6
  getWorkflowText,
7
7
  objectType,
8
8
  registerPrompts
9
- } from "./chunk-WMLM53A3.js";
9
+ } from "./chunk-4QF4KGPE.js";
10
10
  import {
11
11
  GUIDE_TYPES,
12
12
  QUIZ_VARIANTS,
@@ -14,10 +14,11 @@ import {
14
14
  getGuideUri,
15
15
  guideFallbackText,
16
16
  registerResources
17
- } from "./chunk-L3DIBX4G.js";
17
+ } from "./chunk-UBJ7CZL2.js";
18
18
  import {
19
19
  ACTIVE_NODE_TYPES,
20
20
  CONTACT_FIELDS,
21
+ FEATURE_FLAGS,
21
22
  FORM_TYPE_ALIASES,
22
23
  KEN_BURNS_EFFECTS,
23
24
  KEN_BURNS_FIT_MODES,
@@ -35,7 +36,7 @@ import {
35
36
  resolveFormType,
36
37
  structuredResult,
37
38
  textResult
38
- } from "./chunk-YAI5LQPF.js";
39
+ } from "./chunk-IL26A4IN.js";
39
40
  import {
40
41
  getMcpAuth,
41
42
  runWithMcpTool
@@ -17002,6 +17003,7 @@ var EXPOSED_COMPOSITIONS = exposedCompositionIds(
17002
17003
  );
17003
17004
 
17004
17005
  // src/lib/schemas.ts
17006
+ var CUSTOM_SCORES_GATED_KEYS = /* @__PURE__ */ new Set(["show_score", "score_ranges", "scoring_results"]);
17005
17007
  var ACTIVE_NODE_TYPES2 = ACTIVE_NODE_TYPES;
17006
17008
  var CONTACT_FIELD_IDS = CONTACT_FIELDS.map(
17007
17009
  (f) => f.id
@@ -17011,6 +17013,7 @@ function generateConfigSummary(configSchema, typeKey) {
17011
17013
  const parts = [];
17012
17014
  for (const [key, prop] of Object.entries(configSchema.properties)) {
17013
17015
  if (key === "content_media_type") continue;
17016
+ if (!FEATURE_FLAGS.ENABLE_CUSTOM_SCORES && CUSTOM_SCORES_GATED_KEYS.has(key)) continue;
17014
17017
  if (prop.enum) {
17015
17018
  const vals = prop.enum.map((v) => `"${v}"`).join("|");
17016
17019
  let s = `${key} (${vals}`;
@@ -17094,8 +17097,11 @@ var CaptionSchema = external_exports.object({
17094
17097
  });
17095
17098
  var OptionSchema = external_exports.object({
17096
17099
  content: external_exports.string().describe("Option text"),
17100
+ is_correct: external_exports.boolean().optional().describe("Mark this as the correct answer for a quiz (maps to score 1; other options score 0). Use this OR score, not both."),
17097
17101
  score: external_exports.number().optional().describe("Score value for this option (for scored quizzes). Use 1 for correct, 0 for wrong. Scored options automatically enable correct-answer marking (record_scores) on the node."),
17098
- scores: external_exports.record(external_exports.number()).optional().describe("Planned feature - category-based scores keyed by category name. Not yet editable in the dashboard.")
17102
+ // `scores` (category-based) is gated behind custom_scores (planned, #767) -
17103
+ // omitted from the schema while off so the MCP doesn't accept a not-live field.
17104
+ ...FEATURE_FLAGS.ENABLE_CUSTOM_SCORES ? { scores: external_exports.record(external_exports.number()).optional().describe("Category-based scores keyed by category name (personality quizzes).") } : {}
17099
17105
  });
17100
17106
  var OPTIONS_DESCRIPTION = (() => {
17101
17107
  const hints = ["Answer options."];
@@ -17125,6 +17131,14 @@ function applyNodeDefaults(node) {
17125
17131
  const label = node.config?.button_text || buttonDefault;
17126
17132
  node.options = [{ content: label }];
17127
17133
  }
17134
+ if (node.options) {
17135
+ for (const o of node.options) {
17136
+ if (o.score == null && typeof o.is_correct === "boolean") {
17137
+ o.score = o.is_correct ? 1 : 0;
17138
+ }
17139
+ delete o.is_correct;
17140
+ }
17141
+ }
17128
17142
  const supportsRecordScores = def?.config_schema?.properties?.choice?.properties?.record_scores !== void 0;
17129
17143
  const hasScoredOptions = node.options?.some((o) => o.score != null || o.scores != null);
17130
17144
  if (supportsRecordScores && hasScoredOptions) {
@@ -17276,7 +17290,7 @@ Example: A form that asks a question, collects contact info, then finishes:
17276
17290
  body: { tags }
17277
17291
  });
17278
17292
  }
17279
- const truncate = (s, n = 60) => s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
17293
+ const truncate2 = (s, n = 60) => s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
17280
17294
  const lines = [
17281
17295
  isPreview ? `Form created as a preview draft.` : `Form created and live.`,
17282
17296
  ``,
@@ -17284,7 +17298,7 @@ Example: A form that asks a question, collects contact info, then finishes:
17284
17298
  `Form ID: ${formId}`,
17285
17299
  ``,
17286
17300
  `Node IDs (use directly with attach_node_media / update_node - no get_form round-trip needed):`,
17287
- ...createdNodes.map((n) => `- [${n.type}] "${truncate(n.prompt)}": ${n.id}`),
17301
+ ...createdNodes.map((n) => `- [${n.type}] "${truncate2(n.prompt)}": ${n.id}`),
17288
17302
  ``,
17289
17303
  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}`
17290
17304
  ];
@@ -17489,7 +17503,9 @@ function registerListFormsTool(server) {
17489
17503
  share_id: external_exports.string(),
17490
17504
  is_live: external_exports.boolean(),
17491
17505
  has_unpublished_changes: external_exports.boolean(),
17492
- tags: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string(), color: external_exports.string().nullable() }))
17506
+ tags: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string(), color: external_exports.string().nullable() })),
17507
+ builder_url: external_exports.string().optional().describe("Dashboard Build tab for this form. Not present for an anonymous caller."),
17508
+ viewer_url: external_exports.string().optional().describe("Live respondent link. Only present for a published (is_live) form, and not for an anonymous caller.")
17493
17509
  })
17494
17510
  ).describe("Matching forms (empty if none)"),
17495
17511
  next_cursor: external_exports.string().nullable().describe("Pass as cursor to fetch the next page, or null when no more")
@@ -17522,7 +17538,9 @@ function registerListFormsTool(server) {
17522
17538
  share_id: f.share_id,
17523
17539
  is_live: f.is_live,
17524
17540
  has_unpublished_changes: f.has_unpublished_changes,
17525
- tags: f.tags.map((t) => ({ id: t.id, name: t.name, color: t.color }))
17541
+ tags: f.tags.map((t) => ({ id: t.id, name: t.name, color: t.color })),
17542
+ ...f.builder_url ? { builder_url: f.builder_url } : {},
17543
+ ...f.viewer_url ? { viewer_url: f.viewer_url } : {}
17526
17544
  })),
17527
17545
  next_cursor: data.next_cursor
17528
17546
  };
@@ -17534,7 +17552,8 @@ function registerListFormsTool(server) {
17534
17552
  for (const f of data.forms) {
17535
17553
  const status = f.is_live ? f.has_unpublished_changes ? "live, unpublished changes" : "live" : "draft";
17536
17554
  const tagStr = f.tags.length > 0 ? ` [${f.tags.map((t) => t.name).join(", ")}]` : "";
17537
- lines.push(`- ${f.title || "(untitled)"} (${status})${tagStr} \u2014 ${f.id}`);
17555
+ const linkStr = f.builder_url ? ` \u2014 ${f.builder_url}` : "";
17556
+ lines.push(`- ${f.title || "(untitled)"} (${status})${tagStr} \u2014 ${f.id}${linkStr}`);
17538
17557
  }
17539
17558
  if (data.next_cursor) {
17540
17559
  lines.push(
@@ -17570,6 +17589,26 @@ function projectConfig(nodeType, config2) {
17570
17589
  }
17571
17590
  return projected;
17572
17591
  }
17592
+ var FORM_URL_KEYS = [
17593
+ "viewer",
17594
+ "builder",
17595
+ "summary",
17596
+ "responses",
17597
+ "analytics",
17598
+ "share",
17599
+ "embed",
17600
+ "dynamic_link"
17601
+ ];
17602
+ function projectUrls(data) {
17603
+ const raw = data.urls;
17604
+ if (!raw) return void 0;
17605
+ const urls = {};
17606
+ for (const key of FORM_URL_KEYS) {
17607
+ const value = raw[key];
17608
+ if (typeof value === "string") urls[key] = value;
17609
+ }
17610
+ return Object.keys(urls).length > 0 ? urls : void 0;
17611
+ }
17573
17612
  function getFormNodes(data) {
17574
17613
  return data.node ?? data.nodes ?? [];
17575
17614
  }
@@ -17596,11 +17635,13 @@ function projectNext(node, logicByNode) {
17596
17635
  }
17597
17636
  function projectFormState(data) {
17598
17637
  const { nodes, logicByNode } = orderNodesWithLogic(data);
17638
+ const urls = projectUrls(data);
17599
17639
  return {
17600
17640
  form_id: String(data.form_id ?? ""),
17601
17641
  title: String(data.title ?? ""),
17602
17642
  is_live: Boolean(data.is_live),
17603
17643
  has_unpublished_changes: Boolean(data.has_unpublished_changes),
17644
+ ...urls ? { urls } : {},
17604
17645
  nodes: nodes.map((q) => {
17605
17646
  const options = q.node_options ?? q.options;
17606
17647
  const config2 = projectConfig(q.type, q.config);
@@ -17629,6 +17670,18 @@ function formatFormState(data) {
17629
17670
  `Unpublished changes: yes - respondents still see the last published version. Send is_live:true via clipform_update_form to publish.`
17630
17671
  );
17631
17672
  }
17673
+ const urls = projectUrls(data);
17674
+ if (urls) {
17675
+ lines.push(``, `Links (exact values - constructed or guessed URLs return 404):`);
17676
+ if (urls.viewer) lines.push(` Viewer (respondent view): ${urls.viewer}`);
17677
+ if (urls.builder) lines.push(` Builder: ${urls.builder}`);
17678
+ if (urls.summary) lines.push(` Summary: ${urls.summary}`);
17679
+ if (urls.responses) lines.push(` Responses: ${urls.responses}`);
17680
+ if (urls.analytics) lines.push(` Analytics: ${urls.analytics}`);
17681
+ if (urls.share) lines.push(` Share: ${urls.share}`);
17682
+ if (urls.embed) lines.push(` Embed: ${urls.embed}`);
17683
+ if (urls.dynamic_link) lines.push(` Dynamic link: ${urls.dynamic_link}`);
17684
+ }
17632
17685
  lines.push(``, `Nodes (in order):`);
17633
17686
  for (let i = 0; i < nodes.length; i++) {
17634
17687
  const q = nodes[i];
@@ -17668,6 +17721,16 @@ function registerGetFormTool(server) {
17668
17721
  title: external_exports.string().describe("Form title"),
17669
17722
  is_live: external_exports.boolean().describe("Whether the form is published"),
17670
17723
  has_unpublished_changes: external_exports.boolean().describe("Live form has edits not yet republished"),
17724
+ urls: external_exports.object({
17725
+ 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."),
17726
+ builder: external_exports.string().optional().describe("Build tab - the node/canvas editor. Not present for an anonymous caller."),
17727
+ summary: external_exports.string().optional().describe("Summary tab - form overview and key stats. Not present for an anonymous caller."),
17728
+ responses: external_exports.string().optional().describe("Responses tab - individual submissions. Not present for an anonymous caller."),
17729
+ analytics: external_exports.string().optional().describe("Analytics tab - views, starts, completion rate, drop-off per node. Not present for an anonymous caller."),
17730
+ share: external_exports.string().optional().describe("Share tab - link, QR, social. Not present for an anonymous caller."),
17731
+ embed: external_exports.string().optional().describe("Embed setup for websites. Not present for an anonymous caller."),
17732
+ dynamic_link: external_exports.string().optional().describe("Dynamic/personalized link setup. Not present for an anonymous caller.")
17733
+ }).optional().describe("Dashboard/viewer deeplinks for this form. An anonymous caller gets only 'viewer'."),
17671
17734
  nodes: external_exports.array(
17672
17735
  external_exports.object({
17673
17736
  id: external_exports.string(),
@@ -17999,7 +18062,8 @@ function registerGetResultsTool(server) {
17999
18062
  })
18000
18063
  ).optional().describe("Most recent open-text answers, capped at 20 - open questions only")
18001
18064
  })
18002
- ).describe("Nodes in form order. Contact/file/draw/camera/payment nodes carry only total_responses/total_views - no per-response detail.")
18065
+ ).describe("Nodes in form order. Contact/file/draw/camera/payment nodes carry only total_responses/total_views - no per-response detail."),
18066
+ results_url: external_exports.string().optional().describe("Dashboard Responses tab for this form. Not present for an anonymous caller.")
18003
18067
  },
18004
18068
  annotations: {
18005
18069
  readOnlyHint: true,
@@ -18017,13 +18081,13 @@ function registerGetResultsTool(server) {
18017
18081
  const nodes = (data.nodes || []).map(projectNode);
18018
18082
  const structured = {
18019
18083
  total_responses: data.total_responses,
18020
- nodes
18084
+ nodes,
18085
+ ...data.results_url ? { results_url: data.results_url } : {}
18021
18086
  };
18022
18087
  if (data.total_responses === 0) {
18023
- return structuredResult(
18024
- `This form has no responses yet (${nodes.length} node(s)).`,
18025
- structured
18026
- );
18088
+ const lines2 = [`This form has no responses yet (${nodes.length} node(s)).`];
18089
+ if (data.results_url) lines2.push(`View in the dashboard: ${data.results_url}`);
18090
+ return structuredResult(lines2.join("\n"), structured);
18027
18091
  }
18028
18092
  const lines = [`${data.total_responses} total response(s) across ${nodes.length} node(s).`];
18029
18093
  const highlights = nodes.filter((n) => "options" in n || "recent_answers" in n).slice(0, 3);
@@ -18035,11 +18099,157 @@ function registerGetResultsTool(server) {
18035
18099
  lines.push(`- ${n.prompt}: ${n.recent_answers.length} recent open-text answer(s)`);
18036
18100
  }
18037
18101
  }
18102
+ if (data.results_url) lines.push(`View in the dashboard: ${data.results_url}`);
18038
18103
  return structuredResult(lines.join("\n"), structured);
18039
18104
  }
18040
18105
  );
18041
18106
  }
18042
18107
 
18108
+ // src/tools/get-responses.ts
18109
+ var MAX_ANSWER_LENGTH = 300;
18110
+ var DEFAULT_LIMIT = 20;
18111
+ var MAX_LIMIT = 50;
18112
+ var REDACTED_TYPES = {
18113
+ details: "[contact details - view in dashboard]",
18114
+ contact: "[contact details - view in dashboard]",
18115
+ file_upload: "[file upload - view in dashboard]",
18116
+ payment: "[payment - view in dashboard]",
18117
+ open: ""
18118
+ // open is redacted ONLY when the answer is a recording (handled below)
18119
+ };
18120
+ function truncate(s, n = MAX_ANSWER_LENGTH) {
18121
+ return s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
18122
+ }
18123
+ function looksLikeMediaReference(display) {
18124
+ return /https?:\/\/|storage\/|\.(webm|mp4|mp3|wav|m4a|mov)\b/i.test(display);
18125
+ }
18126
+ function registerGetResponsesTool(server) {
18127
+ server.registerTool(
18128
+ "clipform_get_responses",
18129
+ {
18130
+ title: "List form responses",
18131
+ description: "List a Clipform form's individual responses (submissions) with time filtering - complements clipform_get_results (aggregates). Answers questions like 'what came in during the last 24 hours', 'what's the longest answer', 'what did people say'. Text answers only: contact details, file uploads, payments, and audio/video answers are redacted to placeholders - point the user at the dashboard for those. Responses are sorted newest first unless sort: 'oldest'.",
18132
+ inputSchema: {
18133
+ form_id: external_exports.string().describe("Form UUID (from clipform_list_forms / clipform_create_form)"),
18134
+ since: external_exports.string().datetime({ offset: true }).optional().describe("Only responses submitted at or after this ISO 8601 time (e.g. '2026-08-29T00:00:00Z')"),
18135
+ until: external_exports.string().datetime({ offset: true }).optional().describe("Only responses submitted at or before this ISO 8601 time"),
18136
+ query: external_exports.string().optional().describe("Only responses whose text answers contain this string (case-insensitive)"),
18137
+ sort: external_exports.enum(["newest", "oldest"]).optional().describe("Order by submission time. Default: newest"),
18138
+ limit: external_exports.number().int().min(1).max(MAX_LIMIT).optional().describe(`Max responses to return (default ${DEFAULT_LIMIT}, max ${MAX_LIMIT})`)
18139
+ },
18140
+ outputSchema: {
18141
+ total_matching: external_exports.number().describe("Responses matching the filters (before the limit cap)"),
18142
+ returned: external_exports.number().describe("Responses in this result"),
18143
+ locked_over_plan_limit: external_exports.number().describe("Matching responses hidden by the plan's response limit"),
18144
+ responses: external_exports.array(
18145
+ external_exports.object({
18146
+ submitted_at: external_exports.string().describe("ISO 8601 submission time"),
18147
+ status: external_exports.string(),
18148
+ answers: external_exports.array(
18149
+ external_exports.object({
18150
+ question: external_exports.string(),
18151
+ type: external_exports.string(),
18152
+ answer: external_exports.string().describe("Text answer, or a redaction placeholder")
18153
+ })
18154
+ )
18155
+ })
18156
+ ).describe("Matching responses, filtered/sorted/capped per the inputs")
18157
+ },
18158
+ annotations: {
18159
+ readOnlyHint: true,
18160
+ destructiveHint: false,
18161
+ idempotentHint: true,
18162
+ openWorldHint: false
18163
+ }
18164
+ },
18165
+ async ({ form_id, since, until, query, sort, limit }) => {
18166
+ const [subsResult, formResult] = await Promise.all([
18167
+ callApi(`/forms/${form_id}/submissions`),
18168
+ callApi(`/forms/${form_id}`)
18169
+ ]);
18170
+ if (!subsResult.ok) {
18171
+ return errorResult(`Could not fetch responses: ${subsResult.error}`);
18172
+ }
18173
+ const nodeById = /* @__PURE__ */ new Map();
18174
+ if (formResult.ok) {
18175
+ const nodes = formResult.data?.nodes ?? [];
18176
+ for (const n of nodes) nodeById.set(n.id, n);
18177
+ }
18178
+ const rows = subsResult.data ?? [];
18179
+ const sinceMs = since ? Date.parse(since) : null;
18180
+ const untilMs = until ? Date.parse(until) : null;
18181
+ const needle = query?.toLowerCase() ?? null;
18182
+ let lockedCount = 0;
18183
+ const projected = [];
18184
+ for (const row of rows) {
18185
+ if (row.locked) {
18186
+ lockedCount++;
18187
+ continue;
18188
+ }
18189
+ const submittedAt = row.completed_at ?? row.created_at;
18190
+ if (!submittedAt) continue;
18191
+ const ms = Date.parse(submittedAt);
18192
+ if (sinceMs !== null && ms < sinceMs) continue;
18193
+ if (untilMs !== null && ms > untilMs) continue;
18194
+ const answers = [];
18195
+ for (const [nodeId, resp] of Object.entries(row.node_responses ?? {})) {
18196
+ const node = nodeById.get(nodeId);
18197
+ const type = node?.type ?? (typeof resp?.type === "string" ? resp.type : "unknown");
18198
+ const question = truncate(node?.prompt || type, 120);
18199
+ const placeholder = REDACTED_TYPES[type];
18200
+ if (placeholder) {
18201
+ answers.push({ question, type, answer: placeholder });
18202
+ continue;
18203
+ }
18204
+ const display = typeof resp?.display === "string" ? resp.display : "";
18205
+ if (!display) continue;
18206
+ if (looksLikeMediaReference(display)) {
18207
+ answers.push({ question, type, answer: "[recording - view in dashboard]" });
18208
+ continue;
18209
+ }
18210
+ answers.push({ question, type, answer: truncate(display) });
18211
+ }
18212
+ if (needle !== null) {
18213
+ const haystack = answers.map((a) => a.answer.toLowerCase()).join("\n");
18214
+ if (!haystack.includes(needle)) continue;
18215
+ }
18216
+ projected.push({
18217
+ submitted_at: submittedAt,
18218
+ submitted_ms: ms,
18219
+ status: row.status ?? "unknown",
18220
+ answers
18221
+ });
18222
+ }
18223
+ projected.sort(
18224
+ (a, b) => sort === "oldest" ? a.submitted_ms - b.submitted_ms : b.submitted_ms - a.submitted_ms
18225
+ );
18226
+ const totalMatching = projected.length;
18227
+ const capped = projected.slice(0, limit ?? DEFAULT_LIMIT).map(({ submitted_ms: _ms, ...rest }) => rest);
18228
+ const filterBits = [
18229
+ since ? `since ${since}` : null,
18230
+ until ? `until ${until}` : null,
18231
+ query ? `matching "${query}"` : null
18232
+ ].filter(Boolean);
18233
+ const lines = [
18234
+ `${totalMatching} response(s)${filterBits.length ? ` ${filterBits.join(", ")}` : ""}${lockedCount > 0 ? ` (+${lockedCount} over the plan's response limit - not shown; upgrade or view counts in clipform_get_results)` : ""}. Showing ${capped.length}, ${sort === "oldest" ? "oldest" : "newest"} first.`,
18235
+ ``,
18236
+ ...capped.flatMap((r) => [
18237
+ `\u2014 ${r.submitted_at} [${r.status}]`,
18238
+ ...r.answers.map((a) => ` ${a.question}: ${a.answer}`)
18239
+ ]),
18240
+ ``,
18241
+ `Contact details, files, payments, and recordings are redacted here - the dashboard shows full response detail. For aggregate stats use clipform_get_results.`
18242
+ ];
18243
+ return structuredResult(lines.join("\n"), {
18244
+ total_matching: totalMatching,
18245
+ returned: capped.length,
18246
+ locked_over_plan_limit: lockedCount,
18247
+ responses: capped
18248
+ });
18249
+ }
18250
+ );
18251
+ }
18252
+
18043
18253
  // src/tools/whoami.ts
18044
18254
  function registerWhoamiTool(server) {
18045
18255
  server.registerTool(
@@ -18058,7 +18268,11 @@ function registerWhoamiTool(server) {
18058
18268
  node_limit: external_exports.number().nullable().describe("Max nodes per form, or null if unlimited"),
18059
18269
  form_limit: external_exports.number().nullable().describe("Max forms in the workspace, or null if unlimited")
18060
18270
  }).describe("Plan limits for the active workspace"),
18061
- workspaces: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string() })).optional().describe("All workspaces in the caller's company - only present for session (dashboard) auth")
18271
+ workspaces: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string() })).optional().describe("All workspaces in the caller's company - only present for session (dashboard) auth"),
18272
+ links: external_exports.object({
18273
+ dashboard: external_exports.string().describe("Dashboard home for the active workspace."),
18274
+ forms: external_exports.string().describe("Forms list for the active workspace.")
18275
+ }).optional().describe("Workspace-level dashboard links - only present for a non-anonymous caller.")
18062
18276
  },
18063
18277
  annotations: {
18064
18278
  readOnlyHint: true,
@@ -18092,9 +18306,16 @@ function registerWhoamiTool(server) {
18092
18306
  if (Array.isArray(me.workspaces)) {
18093
18307
  structured.workspaces = me.workspaces.map((w) => ({ id: w.id, name: w.name }));
18094
18308
  }
18309
+ if (me.links && typeof me.links.dashboard === "string" && typeof me.links.forms === "string") {
18310
+ structured.links = { dashboard: me.links.dashboard, forms: me.links.forms };
18311
+ }
18095
18312
  const workspaceLine = workspace ? `workspace '${workspace.name}'` : "no workspace (anonymous)";
18096
- const summary = `Auth: ${authMode}. ${workspaceLine}. Plan: ${plan.name}.`;
18097
- return structuredResult(summary, structured);
18313
+ const lines = [`Auth: ${authMode}. ${workspaceLine}. Plan: ${plan.name}.`];
18314
+ if (structured.links) {
18315
+ const links = structured.links;
18316
+ lines.push(`Dashboard: ${links.dashboard}`, `Forms list: ${links.forms}`);
18317
+ }
18318
+ return structuredResult(lines.join("\n"), structured);
18098
18319
  }
18099
18320
  );
18100
18321
  }
@@ -20309,7 +20530,9 @@ Optional args by type:
20309
20530
  - funnel: outcomes, criteria, needs_contact (true)
20310
20531
  - testimonial: use_case
20311
20532
  - application: role
20312
- - booking: event_name, event_type`,
20533
+ - booking: event_name, event_type
20534
+ - intake: use_case, response_format (all)
20535
+ - lead-capture: use_case`,
20313
20536
  inputSchema: {
20314
20537
  type: external_exports.string().describe("Form type to get the build workflow for (accepts aliases like 'feedback' \u2192 survey)"),
20315
20538
  variant: external_exports.enum(QUIZ_VARIANTS).optional().describe("Quiz sub-variant. Only used when type is 'quiz'. Omit for a standard scored quiz."),
@@ -20412,6 +20635,7 @@ function createServer(options) {
20412
20635
  registerUpdateFormTool(server);
20413
20636
  registerDeleteFormTool(server);
20414
20637
  registerGetResultsTool(server);
20638
+ registerGetResponsesTool(server);
20415
20639
  registerAddNodeTool(server);
20416
20640
  registerUpdateNodeTool(server);
20417
20641
  registerDeleteNodeTool(server);
@@ -20446,4 +20670,4 @@ export {
20446
20670
  RENDER_TIMING,
20447
20671
  createServer
20448
20672
  };
20449
- //# sourceMappingURL=chunk-44FHF2MF.js.map
20673
+ //# sourceMappingURL=chunk-NCWQ42GI.js.map