@clipform/mcp-server 2.3.0 → 2.5.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
@@ -65,9 +65,11 @@ Your MCP client lists these automatically on connect (via `tools/list`). Full re
65
65
  |------|-------------|
66
66
  | `clipform_create_form` | Create a new Clipform (interactive video-style form). |
67
67
  | `clipform_list_forms` | List forms in your workspace with optional filtering. |
68
- | `clipform_get_form` | Retrieve a form's details including all nodes in sequential order. |
68
+ | `clipform_whoami` | Show the current identity: auth mode (api key, session, or anonymous), active workspace, and plan limits. |
69
+ | `clipform_get_form` | Retrieve a form's details including all nodes in sequential order and their routing. |
69
70
  | `clipform_update_form` | Update a form's title, publish status, settings, or tags. |
70
71
  | `clipform_delete_form` | Move a form and all its nodes to the trash. |
72
+ | `clipform_get_results` | View response counts, choice/action answer breakdowns, and recent open-text answers for a form. |
71
73
  | `clipform_add_node` | Add a new node to an existing form. |
72
74
  | `clipform_update_node` | Update one or more existing nodes' text, type, config, or options. |
73
75
  | `clipform_delete_node` | Delete a node from a form. |
@@ -76,10 +78,9 @@ Your MCP client lists these automatically on connect (via `tools/list`). Full re
76
78
  | `clipform_attach_node_media` | Attach an existing workspace media asset (from clipform_upload_media_asset) to one or more nodes (max 10). |
77
79
  | `clipform_get_node_media` | Get the media attached to a node, including processing status. |
78
80
  | `clipform_delete_node_media` | Remove media from a node. |
79
- | `clipform_set_logic` | Set routing logic on one or more nodes. |
80
- | `clipform_log_generation` | Save an audit trail for a generated form. |
81
+ | `clipform_set_logic` | Set the next node for one or more nodes, wiring a linear route (A to B to C). |
82
+ | `clipform_log_generation` | Internal audit step run by a form-generation workflow. |
81
83
  | `clipform_search_news` | Fallback news lookup for clients without native web search. |
82
- | `clipform_youtube_transcript` | Extract the transcript, title, and channel info from a YouTube video. |
83
84
  | `clipform_generate_tts` | Generate narration audio from text with word-level captions. |
84
85
  | `clipform_generate_video` | Generate a video from images, video clips, or both, synced to an audio track. |
85
86
  | `clipform_search_media` | Search images or stock video clips. |
@@ -14,9 +14,33 @@ interface McpAuthContext {
14
14
  scopes: string[];
15
15
  tool_name?: string;
16
16
  api_key?: string;
17
+ is_anonymous?: boolean;
18
+ mcp_mint_proof?: string;
17
19
  }
20
+ /**
21
+ * Marker header the in-process MCP tool layer sets on its outgoing API
22
+ * requests (api-client.ts) when `is_anonymous` is true, so an anonymous
23
+ * app-chat session can't launder itself into an unrestricted API-key caller
24
+ * by asking the bot to publish/mint credentials on its behalf. apps/api's
25
+ * authMiddleware / workspaceAccessMiddleware / requireWorkspaceWriteAuth
26
+ * read this on the API-key path and set isAnonymous accordingly - trusting
27
+ * it is safe because it can only RESTRICT a caller: a real integration that
28
+ * sets this header on its own API-key requests only denies itself, it can
29
+ * never use it to gain anonymous-JWT privileges it doesn't already have.
30
+ */
31
+ declare const MCP_ANONYMOUS_HEADER = "x-clipform-anonymous";
32
+ /**
33
+ * Carries `mcp_mint_proof` on the same outgoing bot-key request. Unlike
34
+ * MCP_ANONYMOUS_HEADER (a boolean that can only ever RESTRICT), this header's
35
+ * VALUE matters - it names an identity a claim token gets bound to - so its
36
+ * value is HMAC-signed (mcp-mint-proof.ts) rather than trusted raw: a direct
37
+ * external caller reaching `POST /v1/forms` with any valid workspace API key
38
+ * could otherwise hand-set an unsigned "mint uid" header to an arbitrary
39
+ * victim's uid themselves (#3330 review).
40
+ */
41
+ declare const MCP_MINT_PROOF_HEADER = "x-clipform-mcp-mint-proof";
18
42
  declare function runWithMcpAuth<T>(ctx: McpAuthContext, fn: () => Promise<T> | T): Promise<T> | T;
19
43
  declare function getMcpAuth(): McpAuthContext | undefined;
20
44
  declare function runWithMcpTool<T>(toolName: string, fn: () => Promise<T> | T): Promise<T> | T;
21
45
 
22
- export { type McpAuthContext, getMcpAuth, runWithMcpAuth, runWithMcpTool };
46
+ export { MCP_ANONYMOUS_HEADER, MCP_MINT_PROOF_HEADER, type McpAuthContext, getMcpAuth, runWithMcpAuth, runWithMcpTool };
@@ -1,10 +1,14 @@
1
1
  import {
2
+ MCP_ANONYMOUS_HEADER,
3
+ MCP_MINT_PROOF_HEADER,
2
4
  getMcpAuth,
3
5
  runWithMcpAuth,
4
6
  runWithMcpTool
5
- } from "./chunk-V4L5RQWK.js";
7
+ } from "./chunk-ENHVQDSI.js";
6
8
  import "./chunk-G3PMV62Z.js";
7
9
  export {
10
+ MCP_ANONYMOUS_HEADER,
11
+ MCP_MINT_PROOF_HEADER,
8
12
  getMcpAuth,
9
13
  runWithMcpAuth,
10
14
  runWithMcpTool
@@ -1,6 +1,8 @@
1
1
  import {
2
+ MCP_ANONYMOUS_HEADER,
3
+ MCP_MINT_PROOF_HEADER,
2
4
  getMcpAuth
3
- } from "./chunk-V4L5RQWK.js";
5
+ } from "./chunk-ENHVQDSI.js";
4
6
  import {
5
7
  isAuthFault,
6
8
  isServerFault,
@@ -95,6 +97,17 @@ var FEATURES = {
95
97
  status: "live",
96
98
  category: "builder"
97
99
  },
100
+ anonymous_builder: {
101
+ name: "Anonymous builder",
102
+ description: "Try the builder without an account via /try - an anonymous Supabase session, converted to a real account at publish time",
103
+ // Desktop v1 (#2979) shipped flag-dark: the entry route, publish wall, and
104
+ // conversion modal all land behind this flag. Enabled at launch rollout -
105
+ // the prod Supabase Anonymous Sign-Ins toggle must stay on for /try guest
106
+ // sessions to mint (dev first, then prod after #3016).
107
+ enabled: true,
108
+ status: "beta",
109
+ category: "builder"
110
+ },
98
111
  // -- Node types --
99
112
  node_start: {
100
113
  name: "Start node",
@@ -339,8 +352,30 @@ var FEATURES = {
339
352
  dynamic_link: {
340
353
  name: "Dynamic links",
341
354
  description: "A named link that points at a form, so the target can be swapped from the dashboard without changing the embed code or printed link",
355
+ enabled: true,
356
+ status: "live",
357
+ category: "sharing",
358
+ wentLiveAt: "2026-08-27"
359
+ },
360
+ custom_domains: {
361
+ name: "Custom domains",
362
+ description: "Serve your forms on your own subdomain (forms.yourbrand.com) with vanity paths, instead of a clipform.io link",
342
363
  enabled: false,
343
- status: "beta",
364
+ status: "planned",
365
+ category: "sharing"
366
+ },
367
+ branded_qr: {
368
+ name: "Branded QR codes",
369
+ description: "Render a form's share QR in its theme color with the logo centered, instead of plain black",
370
+ enabled: false,
371
+ status: "planned",
372
+ category: "sharing"
373
+ },
374
+ email_share: {
375
+ name: "Add to an email",
376
+ description: "Copy-paste image + link snippet for pasting a Clipform into an email campaign",
377
+ enabled: false,
378
+ status: "planned",
344
379
  category: "sharing"
345
380
  },
346
381
  remove_branding: {
@@ -544,6 +579,9 @@ var DRAW_GAME_DEFAULT_TIERS = [
544
579
  { min_score: 75, label: "Great match" },
545
580
  { min_score: 92, label: "Perfect" }
546
581
  ];
582
+ var DEFAULT_MAX_RECORDING_SECONDS = 120;
583
+ var MAX_RECORDING_SECONDS_MIN = 10;
584
+ var MAX_RECORDING_SECONDS_MAX = 300;
547
585
  var NODE_TYPES = {
548
586
  start: {
549
587
  label: "Start",
@@ -719,6 +757,14 @@ var NODE_TYPES = {
719
757
  type: "string",
720
758
  label: "Content media type",
721
759
  description: "How the node media was provided"
760
+ },
761
+ max_recording_seconds: {
762
+ type: "number",
763
+ default: DEFAULT_MAX_RECORDING_SECONDS,
764
+ minimum: MAX_RECORDING_SECONDS_MIN,
765
+ maximum: MAX_RECORDING_SECONDS_MAX,
766
+ label: "Max recording length (seconds)",
767
+ description: "Cap on the audio/video recorder for this node, in seconds"
722
768
  }
723
769
  }
724
770
  },
@@ -1029,6 +1075,22 @@ var NODE_TYPES = {
1029
1075
  value: { type: "string" }
1030
1076
  }
1031
1077
  }
1078
+ },
1079
+ // Consent/opt-in agreements the respondent was shown. Present only when
1080
+ // the node has consent items. `accepted` is their choice; `label` is the
1081
+ // exact wording they agreed to, snapshotted per response (#2828).
1082
+ consent: {
1083
+ type: "array",
1084
+ items: {
1085
+ type: "object",
1086
+ required: ["name", "accepted", "type", "label"],
1087
+ properties: {
1088
+ name: { type: "string" },
1089
+ accepted: { type: "boolean" },
1090
+ type: { type: "string", enum: CONSENT_TYPES },
1091
+ label: { type: "string" }
1092
+ }
1093
+ }
1032
1094
  }
1033
1095
  }
1034
1096
  }
@@ -1810,7 +1872,10 @@ var NODE_TYPES = {
1810
1872
  supports_media: false,
1811
1873
  is_system: false,
1812
1874
  show_in_results: false,
1813
- default_config: null,
1875
+ default_config: {
1876
+ title: "Thank you!",
1877
+ message: "Your response has been submitted."
1878
+ },
1814
1879
  config_schema: {
1815
1880
  type: "object",
1816
1881
  properties: {
@@ -2115,7 +2180,10 @@ var HELP_TOPICS = {
2115
2180
  liveAndDraft: { path: "/help/managing-forms", anchor: "live-and-draft" },
2116
2181
  publishingChanges: { path: "/help/managing-forms", anchor: "publishing-changes" },
2117
2182
  embedding: { path: "/help/embed", anchor: null },
2118
- publicResults: { path: "/help/public-results", anchor: null }
2183
+ emailShare: { path: "/help/sharing", anchor: "share-by-email" },
2184
+ publicResults: { path: "/help/public-results", anchor: null },
2185
+ trash: { path: "/help/managing-forms", anchor: "trash" },
2186
+ agreementVersions: { path: "/help/responses", anchor: "agreement-versions" }
2119
2187
  };
2120
2188
  function buildHelpLinks(docsBaseUrl) {
2121
2189
  const links = {};
@@ -2125,6 +2193,58 @@ function buildHelpLinks(docsBaseUrl) {
2125
2193
  return links;
2126
2194
  }
2127
2195
 
2196
+ // ../config/logic-types.js
2197
+ function isBranchingActive(node) {
2198
+ if (!node) return true;
2199
+ return node.type === "choice" || node.type === "binary" ? node.config?.choice?.enable_branching === true : true;
2200
+ }
2201
+ function isOptionEdge(action) {
2202
+ return !!action?.condition?.vars?.some((v) => v.type === "option");
2203
+ }
2204
+ function isInactiveOptionEdge(node, action) {
2205
+ return isOptionEdge(action) && !isBranchingActive(node);
2206
+ }
2207
+
2208
+ // ../config/logic-chain.js
2209
+ function outgoingTargets(actions, node) {
2210
+ const out = [];
2211
+ for (const a of actions ?? []) {
2212
+ if (node && isInactiveOptionEdge(node, a)) continue;
2213
+ const v = a?.details?.to?.value;
2214
+ if (v && !out.includes(v)) out.push(v);
2215
+ }
2216
+ return out;
2217
+ }
2218
+ function defaultTarget(actions) {
2219
+ if (!actions || actions.length === 0) return null;
2220
+ const nodeLevel = actions.find((a) => {
2221
+ const vars = a.condition?.vars;
2222
+ return !!vars && vars.length > 0 && vars.every((v) => v.type === "node");
2223
+ });
2224
+ return (nodeLevel ?? actions[0])?.details?.to ?? null;
2225
+ }
2226
+ function bfsOrder(nodes, logicByNode) {
2227
+ const byId = new Map(nodes.map((n) => [n.id, n]));
2228
+ const ordered = [];
2229
+ const seen = /* @__PURE__ */ new Set();
2230
+ const start = nodes.find((n) => n.type === "start");
2231
+ if (start) {
2232
+ const queue = [start.id];
2233
+ while (queue.length) {
2234
+ const id = queue.shift();
2235
+ if (seen.has(id)) continue;
2236
+ seen.add(id);
2237
+ const node = byId.get(id);
2238
+ if (node) ordered.push(node);
2239
+ for (const target of outgoingTargets(logicByNode.get(id), node)) {
2240
+ if (!seen.has(target) && byId.has(target)) queue.push(target);
2241
+ }
2242
+ }
2243
+ }
2244
+ for (const node of nodes) if (!seen.has(node.id)) ordered.push(node);
2245
+ return ordered;
2246
+ }
2247
+
2128
2248
  // ../config/index.js
2129
2249
  var isServer = typeof window === "undefined";
2130
2250
  var isBrowser = typeof window !== "undefined";
@@ -2206,6 +2326,168 @@ var MEDIA_DIRECT_MAX_BYTES = 20 * 1024 * 1024;
2206
2326
  var SUPABASE_STORAGE_MAX_BYTES = 50 * 1024 * 1024;
2207
2327
  var VIDEO_COMPRESS_FLOOR_BYTES = 1 * 1024 * 1024;
2208
2328
  var HELP_LINKS = buildHelpLinks(BUSINESS.urls.docs);
2329
+ var PLAN_LIMITS = {
2330
+ free_v1: {
2331
+ tier: 0,
2332
+ name: "Free",
2333
+ price: "$0",
2334
+ period: "forever",
2335
+ price_cents: 0,
2336
+ stripe_price_id: { test: null, live: null },
2337
+ // Shopify App Pricing plan NAME (must match the Partner Dashboard plan
2338
+ // exactly - the app_subscriptions/update payload carries this name). Free
2339
+ // generates no subscription, so this is only used by the drift check.
2340
+ shopify_plan_handle: "Free",
2341
+ node_limit: null,
2342
+ form_limit: 5,
2343
+ workspace_limit: 1,
2344
+ show_branding: true,
2345
+ custom_theme: false,
2346
+ dynamic_links: false,
2347
+ custom_domain: false,
2348
+ response_limit: 100,
2349
+ features: [
2350
+ "Up to 5 forms",
2351
+ "Unlimited questions per form",
2352
+ // "completed" is not a tightening: response_limit has always been 100,
2353
+ // but the counter now excludes partials (#2618), so this wording is both
2354
+ // accurate and strictly more generous than what it replaced.
2355
+ "100 completed responses per month",
2356
+ "Full AI tooling (TTS, media, video)",
2357
+ "Clipform branding"
2358
+ ]
2359
+ },
2360
+ pro_v1: {
2361
+ tier: 1,
2362
+ name: "Pro",
2363
+ price: "$25",
2364
+ period: "/month",
2365
+ price_cents: 2500,
2366
+ stripe_price_id: { test: "price_1TZnmhCWdEPD0KoNWWJTc3B6", live: "price_1TZnrZEJOD9ik3LhhFwGZ6ut" },
2367
+ // Must match the Partner Dashboard plan name exactly (drives webhook plan
2368
+ // mapping + the price drift check). Keep in step with price_cents above.
2369
+ shopify_plan_handle: "Pro",
2370
+ node_limit: null,
2371
+ form_limit: null,
2372
+ workspace_limit: null,
2373
+ show_branding: false,
2374
+ custom_theme: true,
2375
+ dynamic_links: true,
2376
+ custom_domain: true,
2377
+ response_limit: null,
2378
+ features: [
2379
+ "Unlimited forms",
2380
+ "Unlimited questions",
2381
+ "Unlimited responses",
2382
+ "Custom themes & colors",
2383
+ "Remove Clipform branding",
2384
+ "Dynamic links - repoint a printed URL to a new form"
2385
+ ]
2386
+ },
2387
+ // Banded Pro (#1937). pro_v2_1k IS the current tier-1 version (#2671) - new
2388
+ // Pro signups land here; pro_v1 is retained only to grandfather the accounts
2389
+ // that predate the flip.
2390
+ //
2391
+ // Billing is one subscription with TWO Stripe items: this licensed base
2392
+ // billed in advance, plus a volume-tiered metered top-up (STRIPE_BAND_PRICE_ID
2393
+ // below) billed in arrears - so the band a workspace lands in is decided by
2394
+ // usage, never by swapping plan version. Completed responses report to the
2395
+ // Stripe usage meter (#2651, apps/api/src/lib/usage-metering.ts) via
2396
+ // planMetersUsage below, and checkout appends the metered item whenever a band
2397
+ // price is configured for the mode (#2652). The band price now exists in BOTH
2398
+ // test and live (STRIPE_BAND_PRICE_ID), so a new Pro checkout is two-item and
2399
+ // usage above the base band IS charged once this deploys - the HUMAN LEGAL
2400
+ // REVIEW on LEGAL.billing below must clear before that go-live.
2401
+ //
2402
+ // `response_limit` counts COMPLETED response sets only; partials never
2403
+ // consume allowance.
2404
+ //
2405
+ // shopify_plan_handle is null until the matching Partner Dashboard plans
2406
+ // exist - so planVersionForShopifyPlan still maps Shopify's "Pro" to pro_v1
2407
+ // ($25 unlimited) while web signups get this band. Creating those plans is
2408
+ // what closes that cross-channel gap.
2409
+ pro_v2_1k: {
2410
+ tier: 1,
2411
+ name: "Pro",
2412
+ price: "$24",
2413
+ period: "/month",
2414
+ price_cents: 2400,
2415
+ stripe_price_id: {
2416
+ test: "price_1U0lA6EJOD9ik3Lhube5eBcK",
2417
+ live: "price_1U115SEJOD9ik3LhOU9OkqJk"
2418
+ },
2419
+ shopify_plan_handle: null,
2420
+ node_limit: null,
2421
+ form_limit: null,
2422
+ workspace_limit: null,
2423
+ show_branding: false,
2424
+ custom_theme: true,
2425
+ dynamic_links: true,
2426
+ custom_domain: true,
2427
+ response_limit: 1e3,
2428
+ features: [
2429
+ "Unlimited forms",
2430
+ "Unlimited questions",
2431
+ "1,000 completed responses per month",
2432
+ "Custom themes & colors",
2433
+ "Remove Clipform branding",
2434
+ "Dynamic links - repoint a printed URL to a new form"
2435
+ ]
2436
+ },
2437
+ pro_v2_5k: {
2438
+ tier: 1,
2439
+ name: "Pro",
2440
+ price: "$49",
2441
+ period: "/month",
2442
+ price_cents: 4900,
2443
+ stripe_price_id: { test: null, live: null },
2444
+ shopify_plan_handle: null,
2445
+ node_limit: null,
2446
+ form_limit: null,
2447
+ workspace_limit: null,
2448
+ show_branding: false,
2449
+ custom_theme: true,
2450
+ dynamic_links: true,
2451
+ custom_domain: true,
2452
+ response_limit: 5e3,
2453
+ features: [
2454
+ "Unlimited forms",
2455
+ "Unlimited questions",
2456
+ "5,000 completed responses per month",
2457
+ "Custom themes & colors",
2458
+ "Remove Clipform branding",
2459
+ "Dynamic links - repoint a printed URL to a new form"
2460
+ ]
2461
+ },
2462
+ pro_v2_20k: {
2463
+ tier: 1,
2464
+ name: "Pro",
2465
+ price: "$99",
2466
+ period: "/month",
2467
+ price_cents: 9900,
2468
+ stripe_price_id: { test: null, live: null },
2469
+ shopify_plan_handle: null,
2470
+ node_limit: null,
2471
+ form_limit: null,
2472
+ workspace_limit: null,
2473
+ show_branding: false,
2474
+ custom_theme: true,
2475
+ dynamic_links: true,
2476
+ custom_domain: true,
2477
+ response_limit: 2e4,
2478
+ features: [
2479
+ "Unlimited forms",
2480
+ "Unlimited questions",
2481
+ "20,000 completed responses per month",
2482
+ "Custom themes & colors",
2483
+ "Remove Clipform branding",
2484
+ "Dynamic links - repoint a printed URL to a new form"
2485
+ ]
2486
+ }
2487
+ };
2488
+ var PRO_BANDS = ["pro_v2_1k", "pro_v2_5k", "pro_v2_20k"].map(
2489
+ (version) => ({ version, ...PLAN_LIMITS[version] })
2490
+ );
2209
2491
  var CONTACT_FIELDS = [
2210
2492
  { id: "first_name", label: "First Name", type: "text", placeholder: "Enter first name", order: 1 },
2211
2493
  { id: "last_name", label: "Last Name", type: "text", placeholder: "Enter last name", order: 2 },
@@ -2287,6 +2569,42 @@ function exposedCompositionIds(includeNonPublic) {
2287
2569
  }
2288
2570
  var SCENE_LAYER_COMPOSITION_IDS = COMPOSITION_REGISTRY.filter((c) => c.scene_layer).map((c) => c.id);
2289
2571
  var MCP_TOOL_TIERS = {};
2572
+ var MCP_TOOL_TOOLSETS = {
2573
+ // account: workspace/account-scoped read + admin tools.
2574
+ clipform_list_forms: "account",
2575
+ clipform_get_form: "account",
2576
+ clipform_delete_form: "account",
2577
+ clipform_whoami: "account",
2578
+ clipform_get_results: "account",
2579
+ // forms: form/node structure + research tools used while building.
2580
+ clipform_create_form: "forms",
2581
+ clipform_add_node: "forms",
2582
+ clipform_update_node: "forms",
2583
+ clipform_delete_node: "forms",
2584
+ clipform_update_form: "forms",
2585
+ clipform_set_logic: "forms",
2586
+ clipform_get_guide: "forms",
2587
+ clipform_get_workflow: "forms",
2588
+ clipform_search_news: "forms",
2589
+ clipform_fetch_boundary: "forms",
2590
+ clipform_log_generation: "forms",
2591
+ // media: creative/render/asset tools.
2592
+ clipform_generate_tts: "media",
2593
+ clipform_generate_video: "media",
2594
+ clipform_render_composition: "media",
2595
+ clipform_render_video_template: "media",
2596
+ clipform_check_render: "media",
2597
+ clipform_upload_media_asset: "media",
2598
+ clipform_complete_media_upload: "media",
2599
+ clipform_attach_node_media: "media",
2600
+ clipform_get_node_media: "media",
2601
+ clipform_delete_node_media: "media",
2602
+ clipform_search_media: "media",
2603
+ clipform_search_music: "media",
2604
+ clipform_list_compositions: "media",
2605
+ clipform_list_assets: "media",
2606
+ clipform_list_video_templates: "media"
2607
+ };
2290
2608
 
2291
2609
  // src/lib/api-client.ts
2292
2610
  function getApiBaseUrl() {
@@ -2314,7 +2632,14 @@ function getAuthHeaders() {
2314
2632
  });
2315
2633
  throw new Error(message);
2316
2634
  }
2317
- return { "Authorization": `Bearer ${apiKey}` };
2635
+ const headers = { "Authorization": `Bearer ${apiKey}` };
2636
+ if (authCtx?.is_anonymous) {
2637
+ headers[MCP_ANONYMOUS_HEADER] = "1";
2638
+ }
2639
+ if (authCtx?.mcp_mint_proof) {
2640
+ headers[MCP_MINT_PROOF_HEADER] = authCtx.mcp_mint_proof;
2641
+ }
2642
+ return headers;
2318
2643
  }
2319
2644
  async function callApi(path, options = {}) {
2320
2645
  const { body, params, timeoutMs = 3e4 } = options;
@@ -2352,9 +2677,7 @@ async function callApi(path, options = {}) {
2352
2677
  return {
2353
2678
  ok: false,
2354
2679
  status: 0,
2355
- error: `Failed to connect to API at ${url}.
2356
-
2357
- Error: ${message}`
2680
+ error: "Unable to reach the Clipform API right now. Please try again in a moment."
2358
2681
  };
2359
2682
  }
2360
2683
  if (response.status === 204) {
@@ -2434,13 +2757,11 @@ async function getSessionContextWithAuth() {
2434
2757
  lines.push(
2435
2758
  apiAuthMode === "api_key" ? "Auth: API key (workspace-scoped)" : "Auth: signed in (connected to a Clipform account)"
2436
2759
  );
2437
- lines.push(`Workspace: ${me.workspace?.name ?? "Unknown"} (${me.workspace?.id ?? "?"})`);
2438
- if (me.user_id) lines.push(`User: ${me.user_id}`);
2439
- if (me.company_id) lines.push(`Company: ${me.company_id}`);
2760
+ lines.push(`Workspace: ${me.workspace?.name ?? "Unknown"}`);
2440
2761
  } else {
2441
2762
  lines.push("Auth: anonymous (not connected to a Clipform account)");
2442
2763
  if (me.workspace) {
2443
- lines.push(`Workspace: ${me.workspace.name} (${me.workspace.id})`);
2764
+ lines.push(`Workspace: ${me.workspace.name}`);
2444
2765
  }
2445
2766
  }
2446
2767
  lines.push(`Plan: ${plan.name} (tier ${plan.tier})`);
@@ -2459,7 +2780,7 @@ async function getSessionContextWithAuth() {
2459
2780
  }
2460
2781
  if (authMode === "anonymous") {
2461
2782
  lines.push(`
2462
- To unlock your full plan: sign in to your Clipform account.`);
2783
+ Anonymous session: forms are not tied to a Clipform account until you sign in.`);
2463
2784
  }
2464
2785
  return { text: lines.join("\n"), authMode };
2465
2786
  }
@@ -2479,7 +2800,8 @@ export {
2479
2800
  ALL_VARIANTS,
2480
2801
  resolveFormType,
2481
2802
  getDiscoveryParams,
2482
- BUSINESS,
2803
+ defaultTarget,
2804
+ bfsOrder,
2483
2805
  CONTACT_FIELDS,
2484
2806
  KEN_BURNS_EFFECTS,
2485
2807
  KEN_BURNS_PRESETS,
@@ -2487,6 +2809,7 @@ export {
2487
2809
  SLIDESHOW_TRANSITIONS,
2488
2810
  exposedCompositionIds,
2489
2811
  MCP_TOOL_TIERS,
2812
+ MCP_TOOL_TOOLSETS,
2490
2813
  callApi,
2491
2814
  errorResult,
2492
2815
  textResult,
@@ -2494,4 +2817,4 @@ export {
2494
2817
  getSessionContextWithAuth,
2495
2818
  getSessionContext
2496
2819
  };
2497
- //# sourceMappingURL=chunk-YZJZ5NPL.js.map
2820
+ //# sourceMappingURL=chunk-3BYQLPTT.js.map