@wix/mcp 1.0.85 → 1.0.87

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.
@@ -71552,6 +71552,7 @@ function rethrowIfUnauthorized(error2) {
71552
71552
 
71553
71553
  // src/config/api-request-rules.ts
71554
71554
  var ARRAY_QUERY_PARAMS_RULE = "Repeat array query params once per value: `?fieldsets=URL&fieldsets=RICH_CONTENT`. Do NOT comma-join values (`fieldsets=URL,RICH_CONTENT`), do NOT leave a trailing comma, and do NOT use a value the method schema does not list, even if a sibling method accepts it (draft posts have no `SEO` fieldset).";
71555
+ var WQL_FILTER_RULE = "Query and search filters are written in WQL: each field takes a single operator, so combine conditions with the logical operators \u2014 `$and` and `$or` take an array of expressions, `$not` takes one, and they can nest.";
71555
71556
 
71556
71557
  // src/config/default-config.ts
71557
71558
  var AGENT_GOAL = dedent_default`
@@ -71652,7 +71653,7 @@ var SITE_CREATION_COPY = {
71652
71653
  var SITE_GUIDANCE = dedent_default`
71653
71654
  A new-site request has two things to settle: what the site is for, and how to build it. The how is AI generation vs. starting from a template, so naming templates, a template id or Wix Studio (Wix Studio is also the template side) already settles it.
71654
71655
 
71655
- One case skips all of this: the user ALREADY HAS the finished site as files — hand-written HTML/CSS/JS, a zip, or the export of an AI builder. There is nothing to build, so don't offer AI or templates and don't call any site-creation tool. Send them to https://www.wix.com/headless/drop?utm_campaign=mcp — they drag the files in and get a live Wix-hosted site. Give that URL exactly as written, keeping the utm_campaign=mcp parameter.
71656
+ One case skips all of this: the user ALREADY HAS the finished site as files — hand-written HTML/CSS/JS, a zip, or the export of an AI builder. There is nothing to build, so don't offer AI or templates and don't call any site-creation tool. For a full agentic flow, read the recipe first and publish the files yourself: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md — it walks you through creating the site and uploading the files end to end. If you can't follow it, or it doesn't apply (you can't reach the file bytes, or something fails), fall back to sending the user to https://www.wix.com/headless/drop?utm_campaign=mcp — they drag the files in and get a live Wix-hosted site. Give that URL exactly as written, keeping the utm_campaign=mcp parameter.
71656
71657
 
71657
71658
  Settle what the site is for first: its purpose, topic, business type, audience, or main visitor action. Naming a method is not a description of the site, and neither is the business or industry on its own.
71658
71659
  If you can't tell yet, ask the preferred question below and nothing else — one question, no creation-method question in the same reply, and no internal tool names.
@@ -71711,12 +71712,25 @@ var ACCOUNT_SITE_EXECUTE_SCOPE = dedent_default`
71711
71712
  siteId?: string;
71712
71713
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
71713
71714
  url: string;
71715
+ // Plain object → JSON. A string is sent verbatim (set your own Content-Type,
71716
+ // e.g. multipart/form-data). A Uint8Array/ArrayBuffer is sent as raw bytes
71717
+ // for binary uploads (set your own Content-Type).
71714
71718
  body?: unknown;
71715
71719
  headers?: Record<string, string>;
71720
+ // "base64" returns the response body as a base64 string (data), with
71721
+ // encoding: "base64" on the result — use it for binary downloads (zip,
71722
+ // image, pdf); decoding those as text corrupts them. Binary content types
71723
+ // are returned as base64 automatically even without this. Decode, edit and
71724
+ // re-upload binary WITHIN this script and return only a small summary. If you
71725
+ // must return the bytes, return the wix.request response object itself (so
71726
+ // encoding: "base64" is preserved and it isn't truncated); a bare base64
71727
+ // string you return is treated as plain text and may be truncated.
71728
+ responseType?: "base64";
71716
71729
  }
71717
71730
  interface WixResponse<T = unknown> {
71718
71731
  status: number;
71719
71732
  data: T;
71733
+ encoding?: "base64";
71720
71734
  json(): Promise<T>;
71721
71735
  }
71722
71736
  declare const wix: {
@@ -71855,6 +71869,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
71855
71869
  ReadFullDocsArticle: dedent_default`
71856
71870
  Fetches the full Wix docs article or method article with code examples for using the method.
71857
71871
  Docs articles looks like this: https://dev.wix.com/docs/... and they can either be general docs articles or method articles.
71872
+ Take the URL from the user's message or an earlier tool result. To reach an article you have no URL for, search or browse the docs first.
71858
71873
  For REST docs, use the URL as-is. For SDK docs, the URL SHOULD include ?apiView=SDK.
71859
71874
  ${reminder}
71860
71875
  `,
@@ -71987,7 +72002,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
71987
72002
  `,
71988
72003
  "import-claude-design-from-url": dedent_default`
71989
72004
  Import a design created in Claude's design tool into Wix. Takes the signed claudeusercontent.com URL Claude Design produces for its self-contained HTML bundle (all images, fonts, and styles inlined); any other host is rejected. Creates a live Wix-hosted site and returns its URL.
71990
- NOT for site files the user provides themselves (their own HTML/CSS/JS, a zip, another builder's export) — for those, don't call this tool: send the user to https://www.wix.com/headless/drop?utm_campaign=mcp, where they upload the files and get a live hosted site. Give that URL exactly as written, keeping the utm_campaign=mcp parameter.
72005
+ NOT for site files the user provides themselves (their own HTML/CSS/JS, a zip, another builder's export) — for those, don't call this tool. For a full agentic flow, read the recipe and publish the files yourself: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md — it covers creating the site and uploading the files end to end. If you can't follow it, or it doesn't apply, fall back to sending the user to https://www.wix.com/headless/drop?utm_campaign=mcp, where they upload the files and get a live hosted site. Give that URL exactly as written, keeping the utm_campaign=mcp parameter.
71991
72006
  `,
71992
72007
  RunWixCliCommand: dedent_default`
71993
72008
  Executes Wix CLI commands for Wix app development and management.
@@ -72414,7 +72429,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
72414
72429
  CreateWixBusinessGuide: dedent_default`
72415
72430
  Provides comprehensive documentation for creating a new Wix business (site, app, etc.).
72416
72431
  Call this ONLY when the user explicitly asks for a headless site, a Wix Classic Editor site, or manual/advanced account/API-based site creation — these don't need the site's purpose first. For normal site-creation ${SITE_CREATION_COPY.normalCreationRoute.nonWidget}; for a specific template id, call \`CreateSiteFromTemplate\`.
72417
- Exception, even when the user says "headless": if they ALREADY HAVE the finished site as files (their own HTML/CSS/JS, a zip, an AI-builder export) and want those files hosted, don't call this tool — send them to https://www.wix.com/headless/drop?utm_campaign=mcp to upload the files and get a live hosted site (give that URL exactly, keeping the utm_campaign=mcp parameter). This guide's CLI flow is for BUILDING a headless frontend, not for hosting ready-made files.
72432
+ Exception, even when the user says "headless": if they ALREADY HAVE the finished site as files (their own HTML/CSS/JS, a zip, an AI-builder export) and want those files hosted, don't call this tool. For a full agentic flow, read the recipe and publish the files yourself: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md — it covers creating the site and uploading the files end to end. If you can't follow it, or it doesn't apply, fall back to sending the user to https://www.wix.com/headless/drop?utm_campaign=mcp to upload the files and get a live hosted site (give that URL exactly, keeping the utm_campaign=mcp parameter). This guide's CLI flow is for BUILDING a headless frontend, not for hosting ready-made files.
72418
72433
 
72419
72434
  ## What This Tool Contains
72420
72435
 
@@ -72663,6 +72678,22 @@ function renderApps(apps, knownApps) {
72663
72678
  }
72664
72679
  return lines.join("\n");
72665
72680
  }
72681
+ function sanitizeCustomAppName(raw) {
72682
+ return raw.replace(/[\x00-\x1f\x7f]/g, " ").replace(/[`*_[\]|<>]/g, "").replace(/\s+/g, " ").trim().slice(0, 60);
72683
+ }
72684
+ function renderCustomAppsWithSkillsTools(apps) {
72685
+ const relevant = apps.filter((a2) => a2.appId && (a2.hasSkills || a2.hasTools)).sort(
72686
+ (a2, b) => (a2.appName ?? a2.appId ?? "").localeCompare(b.appName ?? b.appId ?? "")
72687
+ );
72688
+ if (!relevant.length) return "";
72689
+ const lines = [];
72690
+ for (const app of relevant) {
72691
+ const sanitizedName = app.appName ? sanitizeCustomAppName(app.appName) : "";
72692
+ const name = sanitizedName || app.appId;
72693
+ lines.push(`- \`${name}\` (ID: \`${app.appId}\`)`);
72694
+ }
72695
+ return lines.join("\n");
72696
+ }
72666
72697
  function renderProperties(props) {
72667
72698
  const sections = [];
72668
72699
  const regionalFormat = [];
@@ -72725,6 +72756,15 @@ function renderSite(site, index, knownApps) {
72725
72756
  lines.push("");
72726
72757
  lines.push(appsContent);
72727
72758
  }
72759
+ const customAppsWithSkillsToolsContent = renderCustomAppsWithSkillsTools(
72760
+ site.customApps ?? []
72761
+ );
72762
+ if (customAppsWithSkillsToolsContent) {
72763
+ lines.push("");
72764
+ lines.push("### Custom Apps with skills and tools");
72765
+ lines.push("");
72766
+ lines.push(customAppsWithSkillsToolsContent);
72767
+ }
72728
72768
  lines.push("");
72729
72769
  lines.push("---");
72730
72770
  lines.push("");
@@ -72988,7 +73028,10 @@ var paramDescriptions = {
72988
73028
  ARRAY_QUERY_PARAMS_RULE
72989
73029
  ].join("\n"),
72990
73030
  method: "The HTTP method to use for the API call (e.g. GET, POST, PUT, DELETE)",
72991
- body: 'The request body as a JSON object or array with all the required fields and values, including nested objects. Pass the actual value (object or array), NOT a JSON string. YOU MUST NEVER MAKE UP A BODY - the body should be based on the conversation context, i.e from the user prompt OR got into the conversation context by the "ReadFullDocsArticle" tool OR by the "SearchWixAPISpec" tool OR by the "ReadFullDocsMethodSchema" tool - i.e based on the API docs, a relevant recipe you read (preferably), a code example you found in the docs, a schema you read etc.. YOU MUST NEVER ASSUME YOU KNOW WHAT THE BODY SCHEMA IS WITHOUT CONCRETE EXAMPLES OR SCHEMA DEFINITIONS FROM THE CONVERSATION CONTEXT. Prefer reading relevant recipes if you have them in context for understand the body schema for API calls.',
73031
+ body: [
73032
+ 'The request body as a JSON object or array with all the required fields and values, including nested objects. Pass the actual value (object or array), NOT a JSON string. YOU MUST NEVER MAKE UP A BODY - the body should be based on the conversation context, i.e from the user prompt OR got into the conversation context by the "ReadFullDocsArticle" tool OR by the "SearchWixAPISpec" tool OR by the "ReadFullDocsMethodSchema" tool - i.e based on the API docs, a relevant recipe you read (preferably), a code example you found in the docs, a schema you read etc.. YOU MUST NEVER ASSUME YOU KNOW WHAT THE BODY SCHEMA IS WITHOUT CONCRETE EXAMPLES OR SCHEMA DEFINITIONS FROM THE CONVERSATION CONTEXT. Prefer reading relevant recipes if you have them in context for understand the body schema for API calls.',
73033
+ WQL_FILTER_RULE
73034
+ ].join("\n"),
72992
73035
  reason: "One sentence explaining the original user request and why you are calling this API to complete it.",
72993
73036
  sourceDocUrl: [
72994
73037
  "The URL of the documentation or recipe where you found this API endpoint.",
@@ -73312,15 +73355,17 @@ Failed to fetch content: ${errorMsg}`
73312
73355
  const baseContentsPromise = Promise.all(
73313
73356
  baseDocsWithoutRecipes.map(fetchDoc)
73314
73357
  );
73315
- const skillsContentPromise = getSkillsByPortal(httpClient, {
73358
+ const skillsContentPromise = skillsTags?.length ? getSkillsByPortal(httpClient, {
73316
73359
  portalId: REST_PORTAL_ID,
73317
73360
  ...repo && { repo },
73318
73361
  ...commitHash && { commitHash },
73319
- ...skillsTags && skillsTags.length > 0 && { tags: skillsTags }
73362
+ tags: skillsTags
73320
73363
  }).catch((err) => {
73321
73364
  console.error("[WixREADME] Failed to fetch skills by portal:", err);
73322
73365
  return null;
73323
- });
73366
+ }) : fetchDoc({ action: "include", url: RECIPES_INDEX_URL }).then(
73367
+ (doc) => doc?.text ?? null
73368
+ );
73324
73369
  const filter = {};
73325
73370
  if (siteId) filter.siteId = siteId;
73326
73371
  else if (siteName) filter.siteName = siteName;
@@ -73527,7 +73572,9 @@ function getErrorMessage(error2) {
73527
73572
  }
73528
73573
  function shouldTryMenuFallbackForDocsRead(error2, articleUrl) {
73529
73574
  const message = getErrorMessage(error2);
73530
- if (!message.includes("404") || !articleUrl.includes("/api-reference/")) {
73575
+ const { pathname } = new URL(articleUrl);
73576
+ const isMenuFallbackUrl = articleUrl.includes("/api-reference/") || pathname.startsWith("/docs/velo");
73577
+ if (!message.includes("404") || !isMenuFallbackUrl) {
73531
73578
  return false;
73532
73579
  }
73533
73580
  return !["This resource is deprecated", "No schema found"].some(
@@ -74180,7 +74227,7 @@ var addDocsTools = (server2, allowedTools = [
74180
74227
  content: [
74181
74228
  {
74182
74229
  type: "text",
74183
- text: `Note: "${articleUrl}" is a documentation category, not an article. Showing its menu contents below. For category URLs, use BrowseWixRESTDocsMenu directly next time.
74230
+ text: `Note: "${articleUrl}" is not an article. Showing the closest documentation menu below; read the article you need from its links. For category URLs, use BrowseWixRESTDocsMenu directly next time.
74184
74231
 
74185
74232
  ${text}`
74186
74233
  }
@@ -74272,7 +74319,7 @@ ${text}`
74272
74319
  content: [
74273
74320
  {
74274
74321
  type: "text",
74275
- text: `Note: "${articleUrl}" is a documentation category, not an article. Showing its menu contents below. For category URLs, use BrowseWixRESTDocsMenu directly next time.
74322
+ text: `Note: "${articleUrl}" is not an article. Showing the closest documentation menu below; read the article you need from its links. For category URLs, use BrowseWixRESTDocsMenu directly next time.
74276
74323
 
74277
74324
  ${text}`
74278
74325
  }
@@ -85226,6 +85273,14 @@ var EMPTY_COMPLETION_RESULT = {
85226
85273
 
85227
85274
  // src/tool-utils.ts
85228
85275
  var REST_METHOD_CONTRACT_RETRY_HINT = "Before retrying, make sure you have everything needed for a correct call: the right endpoint URL, HTTP method, request body shape, and response shape. Use the SearchWixRESTDocumentation tool or BrowseWixRESTDocsMenu tool to find the correct method or relevant related articles, the ReadFullDocsArticle tool to read the method description and all code examples, and the SearchWixAPISpec tool for precise field names and schema details.";
85276
+ var SINGLE_OPERATOR_PER_FIELD_HINT = 'WQL takes one operator per field object. Split the conditions into separate expressions under the matching logical operator \u2014 `{"$and": [{"field": {"$gte": ...}}, {"field": {"$lte": ...}}]}` \u2014 and retry without re-searching the docs.';
85277
+ var FIELD_CAPABILITIES_HINT = "This endpoint declares its filterable and sortable fields with their operators. Read the map with SearchWixAPISpec (`queryMethodData.queryFieldsCapabilitiesMap`, or `searchMethodData.searchFieldsCapabilitiesMap` on search methods) and use only what it lists; otherwise fetch a bounded page and filter or sort the results yourself.";
85278
+ var getRetryHint = (errorDetails) => {
85279
+ if (!/Filter contains unsupported operator/i.test(errorDetails)) {
85280
+ return REST_METHOD_CONTRACT_RETRY_HINT;
85281
+ }
85282
+ return /"operator":"\{/.test(errorDetails) ? SINGLE_OPERATOR_PER_FIELD_HINT : FIELD_CAPABILITIES_HINT;
85283
+ };
85229
85284
  var DEFAULT_OPENAI_SECURITY_SCHEMES = [
85230
85285
  { type: "oauth2", scopes: ["offline_access"] }
85231
85286
  ];
@@ -85252,7 +85307,7 @@ var buildErrorMessage = ({
85252
85307
  ].filter((str) => !!str).join("\n");
85253
85308
  return `${baseMessage}
85254
85309
 
85255
- ${REST_METHOD_CONTRACT_RETRY_HINT}`;
85310
+ ${getRetryHint(String(errorDetails))}`;
85256
85311
  };
85257
85312
  var handleWixAPIResponse = async (response) => {
85258
85313
  const requestId = response.headers?.["x-wix-request-id"];
@@ -85350,7 +85405,25 @@ function extractText(result) {
85350
85405
  return result.content.filter((content) => content.type === "text" && content.text != null).map((content) => content.text).join("\n");
85351
85406
  }
85352
85407
  var TOOL_RESULT_PREVIEW_MAX_CHARS = 4e3;
85353
- function formatToolResultAdditionalFields(result, extras) {
85408
+ var CODEX_OUTPUT_BUDGET_BYTES = 4e4;
85409
+ function getCodexTruncation(resultText, client) {
85410
+ const isCodex = [client?.name, client?.userAgent].some(
85411
+ (identity3) => identity3?.toLowerCase().includes("codex")
85412
+ );
85413
+ if (!isCodex) {
85414
+ return void 0;
85415
+ }
85416
+ const resultBytes = new TextEncoder().encode(resultText).length;
85417
+ if (resultBytes <= CODEX_OUTPUT_BUDGET_BYTES) {
85418
+ return void 0;
85419
+ }
85420
+ return {
85421
+ resultBytes,
85422
+ budgetBytes: CODEX_OUTPUT_BUDGET_BYTES,
85423
+ truncatedBytes: resultBytes - CODEX_OUTPUT_BUDGET_BYTES
85424
+ };
85425
+ }
85426
+ function formatToolResultAdditionalFields(result, extras, client) {
85354
85427
  const resultText = extractText(result);
85355
85428
  const contentTypes = [
85356
85429
  ...new Set(
@@ -85367,6 +85440,7 @@ function formatToolResultAdditionalFields(result, extras) {
85367
85440
  resultTextLength: resultText.length,
85368
85441
  resultPreviewTruncated: resultText.length > TOOL_RESULT_PREVIEW_MAX_CHARS,
85369
85442
  resultContentTypes: contentTypes,
85443
+ codexTruncation: getCodexTruncation(resultText, client),
85370
85444
  ...extras
85371
85445
  });
85372
85446
  }
@@ -85545,10 +85619,17 @@ var WixMcpServer = class extends McpServer {
85545
85619
  if (!toolSucceeded) {
85546
85620
  errorBody = extractText(cbResult);
85547
85621
  } else {
85548
- additionalFields = formatToolResultAdditionalFields(cbResult, {
85549
- returnedRows: toolBiParams.returnedRows,
85550
- originalLength: toolBiParams.originalLength
85551
- });
85622
+ additionalFields = formatToolResultAdditionalFields(
85623
+ cbResult,
85624
+ {
85625
+ returnedRows: toolBiParams.returnedRows,
85626
+ originalLength: toolBiParams.originalLength
85627
+ },
85628
+ {
85629
+ name: this.getClientName?.() || this.server.getClientVersion()?.name,
85630
+ userAgent: this.getUserAgent?.()
85631
+ }
85632
+ );
85552
85633
  }
85553
85634
  if (toolSucceeded && !isDevEnvironment) {
85554
85635
  panorama.transaction(toolName).finish({ toolInvocationId });
@@ -35867,6 +35867,14 @@ var EMPTY_COMPLETION_RESULT = {
35867
35867
 
35868
35868
  // src/tool-utils.ts
35869
35869
  var REST_METHOD_CONTRACT_RETRY_HINT = "Before retrying, make sure you have everything needed for a correct call: the right endpoint URL, HTTP method, request body shape, and response shape. Use the SearchWixRESTDocumentation tool or BrowseWixRESTDocsMenu tool to find the correct method or relevant related articles, the ReadFullDocsArticle tool to read the method description and all code examples, and the SearchWixAPISpec tool for precise field names and schema details.";
35870
+ var SINGLE_OPERATOR_PER_FIELD_HINT = 'WQL takes one operator per field object. Split the conditions into separate expressions under the matching logical operator \u2014 `{"$and": [{"field": {"$gte": ...}}, {"field": {"$lte": ...}}]}` \u2014 and retry without re-searching the docs.';
35871
+ var FIELD_CAPABILITIES_HINT = "This endpoint declares its filterable and sortable fields with their operators. Read the map with SearchWixAPISpec (`queryMethodData.queryFieldsCapabilitiesMap`, or `searchMethodData.searchFieldsCapabilitiesMap` on search methods) and use only what it lists; otherwise fetch a bounded page and filter or sort the results yourself.";
35872
+ var getRetryHint = (errorDetails) => {
35873
+ if (!/Filter contains unsupported operator/i.test(errorDetails)) {
35874
+ return REST_METHOD_CONTRACT_RETRY_HINT;
35875
+ }
35876
+ return /"operator":"\{/.test(errorDetails) ? SINGLE_OPERATOR_PER_FIELD_HINT : FIELD_CAPABILITIES_HINT;
35877
+ };
35870
35878
  var DEFAULT_OPENAI_SECURITY_SCHEMES = [
35871
35879
  { type: "oauth2", scopes: ["offline_access"] }
35872
35880
  ];
@@ -35893,7 +35901,7 @@ var buildErrorMessage = ({
35893
35901
  ].filter((str) => !!str).join("\n");
35894
35902
  return `${baseMessage}
35895
35903
 
35896
- ${REST_METHOD_CONTRACT_RETRY_HINT}`;
35904
+ ${getRetryHint(String(errorDetails))}`;
35897
35905
  };
35898
35906
  var handleWixAPIResponse = async (response) => {
35899
35907
  const requestId = response.headers?.["x-wix-request-id"];
@@ -36024,7 +36032,25 @@ function extractText(result) {
36024
36032
  return result.content.filter((content) => content.type === "text" && content.text != null).map((content) => content.text).join("\n");
36025
36033
  }
36026
36034
  var TOOL_RESULT_PREVIEW_MAX_CHARS = 4e3;
36027
- function formatToolResultAdditionalFields(result, extras) {
36035
+ var CODEX_OUTPUT_BUDGET_BYTES = 4e4;
36036
+ function getCodexTruncation(resultText, client) {
36037
+ const isCodex = [client?.name, client?.userAgent].some(
36038
+ (identity) => identity?.toLowerCase().includes("codex")
36039
+ );
36040
+ if (!isCodex) {
36041
+ return void 0;
36042
+ }
36043
+ const resultBytes = new TextEncoder().encode(resultText).length;
36044
+ if (resultBytes <= CODEX_OUTPUT_BUDGET_BYTES) {
36045
+ return void 0;
36046
+ }
36047
+ return {
36048
+ resultBytes,
36049
+ budgetBytes: CODEX_OUTPUT_BUDGET_BYTES,
36050
+ truncatedBytes: resultBytes - CODEX_OUTPUT_BUDGET_BYTES
36051
+ };
36052
+ }
36053
+ function formatToolResultAdditionalFields(result, extras, client) {
36028
36054
  const resultText = extractText(result);
36029
36055
  const contentTypes = [
36030
36056
  ...new Set(
@@ -36041,6 +36067,7 @@ function formatToolResultAdditionalFields(result, extras) {
36041
36067
  resultTextLength: resultText.length,
36042
36068
  resultPreviewTruncated: resultText.length > TOOL_RESULT_PREVIEW_MAX_CHARS,
36043
36069
  resultContentTypes: contentTypes,
36070
+ codexTruncation: getCodexTruncation(resultText, client),
36044
36071
  ...extras
36045
36072
  });
36046
36073
  }
@@ -36222,10 +36249,17 @@ var WixMcpServer = class extends McpServer {
36222
36249
  if (!toolSucceeded) {
36223
36250
  errorBody = extractText(cbResult);
36224
36251
  } else {
36225
- additionalFields = formatToolResultAdditionalFields(cbResult, {
36226
- returnedRows: toolBiParams.returnedRows,
36227
- originalLength: toolBiParams.originalLength
36228
- });
36252
+ additionalFields = formatToolResultAdditionalFields(
36253
+ cbResult,
36254
+ {
36255
+ returnedRows: toolBiParams.returnedRows,
36256
+ originalLength: toolBiParams.originalLength
36257
+ },
36258
+ {
36259
+ name: this.getClientName?.() || this.server.getClientVersion()?.name,
36260
+ userAgent: this.getUserAgent?.()
36261
+ }
36262
+ );
36229
36263
  }
36230
36264
  if (toolSucceeded && !isDevEnvironment) {
36231
36265
  panorama.transaction(toolName).finish({ toolInvocationId });
@@ -37134,6 +37168,7 @@ function rethrowIfUnauthorized(error2) {
37134
37168
 
37135
37169
  // src/config/api-request-rules.ts
37136
37170
  var ARRAY_QUERY_PARAMS_RULE = "Repeat array query params once per value: `?fieldsets=URL&fieldsets=RICH_CONTENT`. Do NOT comma-join values (`fieldsets=URL,RICH_CONTENT`), do NOT leave a trailing comma, and do NOT use a value the method schema does not list, even if a sibling method accepts it (draft posts have no `SEO` fieldset).";
37171
+ var WQL_FILTER_RULE = "Query and search filters are written in WQL: each field takes a single operator, so combine conditions with the logical operators \u2014 `$and` and `$or` take an array of expressions, `$not` takes one, and they can nest.";
37137
37172
 
37138
37173
  // src/config/default-config.ts
37139
37174
  var AGENT_GOAL = dedent_default`
@@ -37234,7 +37269,7 @@ var SITE_CREATION_COPY = {
37234
37269
  var SITE_GUIDANCE = dedent_default`
37235
37270
  A new-site request has two things to settle: what the site is for, and how to build it. The how is AI generation vs. starting from a template, so naming templates, a template id or Wix Studio (Wix Studio is also the template side) already settles it.
37236
37271
 
37237
- One case skips all of this: the user ALREADY HAS the finished site as files — hand-written HTML/CSS/JS, a zip, or the export of an AI builder. There is nothing to build, so don't offer AI or templates and don't call any site-creation tool. Send them to https://www.wix.com/headless/drop?utm_campaign=mcp — they drag the files in and get a live Wix-hosted site. Give that URL exactly as written, keeping the utm_campaign=mcp parameter.
37272
+ One case skips all of this: the user ALREADY HAS the finished site as files — hand-written HTML/CSS/JS, a zip, or the export of an AI builder. There is nothing to build, so don't offer AI or templates and don't call any site-creation tool. For a full agentic flow, read the recipe first and publish the files yourself: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md — it walks you through creating the site and uploading the files end to end. If you can't follow it, or it doesn't apply (you can't reach the file bytes, or something fails), fall back to sending the user to https://www.wix.com/headless/drop?utm_campaign=mcp — they drag the files in and get a live Wix-hosted site. Give that URL exactly as written, keeping the utm_campaign=mcp parameter.
37238
37273
 
37239
37274
  Settle what the site is for first: its purpose, topic, business type, audience, or main visitor action. Naming a method is not a description of the site, and neither is the business or industry on its own.
37240
37275
  If you can't tell yet, ask the preferred question below and nothing else — one question, no creation-method question in the same reply, and no internal tool names.
@@ -37293,12 +37328,25 @@ var ACCOUNT_SITE_EXECUTE_SCOPE = dedent_default`
37293
37328
  siteId?: string;
37294
37329
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
37295
37330
  url: string;
37331
+ // Plain object → JSON. A string is sent verbatim (set your own Content-Type,
37332
+ // e.g. multipart/form-data). A Uint8Array/ArrayBuffer is sent as raw bytes
37333
+ // for binary uploads (set your own Content-Type).
37296
37334
  body?: unknown;
37297
37335
  headers?: Record<string, string>;
37336
+ // "base64" returns the response body as a base64 string (data), with
37337
+ // encoding: "base64" on the result — use it for binary downloads (zip,
37338
+ // image, pdf); decoding those as text corrupts them. Binary content types
37339
+ // are returned as base64 automatically even without this. Decode, edit and
37340
+ // re-upload binary WITHIN this script and return only a small summary. If you
37341
+ // must return the bytes, return the wix.request response object itself (so
37342
+ // encoding: "base64" is preserved and it isn't truncated); a bare base64
37343
+ // string you return is treated as plain text and may be truncated.
37344
+ responseType?: "base64";
37298
37345
  }
37299
37346
  interface WixResponse<T = unknown> {
37300
37347
  status: number;
37301
37348
  data: T;
37349
+ encoding?: "base64";
37302
37350
  json(): Promise<T>;
37303
37351
  }
37304
37352
  declare const wix: {
@@ -37437,6 +37485,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
37437
37485
  ReadFullDocsArticle: dedent_default`
37438
37486
  Fetches the full Wix docs article or method article with code examples for using the method.
37439
37487
  Docs articles looks like this: https://dev.wix.com/docs/... and they can either be general docs articles or method articles.
37488
+ Take the URL from the user's message or an earlier tool result. To reach an article you have no URL for, search or browse the docs first.
37440
37489
  For REST docs, use the URL as-is. For SDK docs, the URL SHOULD include ?apiView=SDK.
37441
37490
  ${reminder}
37442
37491
  `,
@@ -37569,7 +37618,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
37569
37618
  `,
37570
37619
  "import-claude-design-from-url": dedent_default`
37571
37620
  Import a design created in Claude's design tool into Wix. Takes the signed claudeusercontent.com URL Claude Design produces for its self-contained HTML bundle (all images, fonts, and styles inlined); any other host is rejected. Creates a live Wix-hosted site and returns its URL.
37572
- NOT for site files the user provides themselves (their own HTML/CSS/JS, a zip, another builder's export) — for those, don't call this tool: send the user to https://www.wix.com/headless/drop?utm_campaign=mcp, where they upload the files and get a live hosted site. Give that URL exactly as written, keeping the utm_campaign=mcp parameter.
37621
+ NOT for site files the user provides themselves (their own HTML/CSS/JS, a zip, another builder's export) — for those, don't call this tool. For a full agentic flow, read the recipe and publish the files yourself: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md — it covers creating the site and uploading the files end to end. If you can't follow it, or it doesn't apply, fall back to sending the user to https://www.wix.com/headless/drop?utm_campaign=mcp, where they upload the files and get a live hosted site. Give that URL exactly as written, keeping the utm_campaign=mcp parameter.
37573
37622
  `,
37574
37623
  RunWixCliCommand: dedent_default`
37575
37624
  Executes Wix CLI commands for Wix app development and management.
@@ -37996,7 +38045,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
37996
38045
  CreateWixBusinessGuide: dedent_default`
37997
38046
  Provides comprehensive documentation for creating a new Wix business (site, app, etc.).
37998
38047
  Call this ONLY when the user explicitly asks for a headless site, a Wix Classic Editor site, or manual/advanced account/API-based site creation — these don't need the site's purpose first. For normal site-creation ${SITE_CREATION_COPY.normalCreationRoute.nonWidget}; for a specific template id, call \`CreateSiteFromTemplate\`.
37999
- Exception, even when the user says "headless": if they ALREADY HAVE the finished site as files (their own HTML/CSS/JS, a zip, an AI-builder export) and want those files hosted, don't call this tool — send them to https://www.wix.com/headless/drop?utm_campaign=mcp to upload the files and get a live hosted site (give that URL exactly, keeping the utm_campaign=mcp parameter). This guide's CLI flow is for BUILDING a headless frontend, not for hosting ready-made files.
38048
+ Exception, even when the user says "headless": if they ALREADY HAVE the finished site as files (their own HTML/CSS/JS, a zip, an AI-builder export) and want those files hosted, don't call this tool. For a full agentic flow, read the recipe and publish the files yourself: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md — it covers creating the site and uploading the files end to end. If you can't follow it, or it doesn't apply, fall back to sending the user to https://www.wix.com/headless/drop?utm_campaign=mcp to upload the files and get a live hosted site (give that URL exactly, keeping the utm_campaign=mcp parameter). This guide's CLI flow is for BUILDING a headless frontend, not for hosting ready-made files.
38000
38049
 
38001
38050
  ## What This Tool Contains
38002
38051
 
@@ -38283,6 +38332,22 @@ function renderApps(apps, knownApps) {
38283
38332
  }
38284
38333
  return lines.join("\n");
38285
38334
  }
38335
+ function sanitizeCustomAppName(raw) {
38336
+ return raw.replace(/[\x00-\x1f\x7f]/g, " ").replace(/[`*_[\]|<>]/g, "").replace(/\s+/g, " ").trim().slice(0, 60);
38337
+ }
38338
+ function renderCustomAppsWithSkillsTools(apps) {
38339
+ const relevant = apps.filter((a2) => a2.appId && (a2.hasSkills || a2.hasTools)).sort(
38340
+ (a2, b2) => (a2.appName ?? a2.appId ?? "").localeCompare(b2.appName ?? b2.appId ?? "")
38341
+ );
38342
+ if (!relevant.length) return "";
38343
+ const lines = [];
38344
+ for (const app of relevant) {
38345
+ const sanitizedName = app.appName ? sanitizeCustomAppName(app.appName) : "";
38346
+ const name = sanitizedName || app.appId;
38347
+ lines.push(`- \`${name}\` (ID: \`${app.appId}\`)`);
38348
+ }
38349
+ return lines.join("\n");
38350
+ }
38286
38351
  function renderProperties(props) {
38287
38352
  const sections = [];
38288
38353
  const regionalFormat = [];
@@ -38345,6 +38410,15 @@ function renderSite(site, index, knownApps) {
38345
38410
  lines.push("");
38346
38411
  lines.push(appsContent);
38347
38412
  }
38413
+ const customAppsWithSkillsToolsContent = renderCustomAppsWithSkillsTools(
38414
+ site.customApps ?? []
38415
+ );
38416
+ if (customAppsWithSkillsToolsContent) {
38417
+ lines.push("");
38418
+ lines.push("### Custom Apps with skills and tools");
38419
+ lines.push("");
38420
+ lines.push(customAppsWithSkillsToolsContent);
38421
+ }
38348
38422
  lines.push("");
38349
38423
  lines.push("---");
38350
38424
  lines.push("");
@@ -38608,7 +38682,10 @@ var paramDescriptions = {
38608
38682
  ARRAY_QUERY_PARAMS_RULE
38609
38683
  ].join("\n"),
38610
38684
  method: "The HTTP method to use for the API call (e.g. GET, POST, PUT, DELETE)",
38611
- body: 'The request body as a JSON object or array with all the required fields and values, including nested objects. Pass the actual value (object or array), NOT a JSON string. YOU MUST NEVER MAKE UP A BODY - the body should be based on the conversation context, i.e from the user prompt OR got into the conversation context by the "ReadFullDocsArticle" tool OR by the "SearchWixAPISpec" tool OR by the "ReadFullDocsMethodSchema" tool - i.e based on the API docs, a relevant recipe you read (preferably), a code example you found in the docs, a schema you read etc.. YOU MUST NEVER ASSUME YOU KNOW WHAT THE BODY SCHEMA IS WITHOUT CONCRETE EXAMPLES OR SCHEMA DEFINITIONS FROM THE CONVERSATION CONTEXT. Prefer reading relevant recipes if you have them in context for understand the body schema for API calls.',
38685
+ body: [
38686
+ 'The request body as a JSON object or array with all the required fields and values, including nested objects. Pass the actual value (object or array), NOT a JSON string. YOU MUST NEVER MAKE UP A BODY - the body should be based on the conversation context, i.e from the user prompt OR got into the conversation context by the "ReadFullDocsArticle" tool OR by the "SearchWixAPISpec" tool OR by the "ReadFullDocsMethodSchema" tool - i.e based on the API docs, a relevant recipe you read (preferably), a code example you found in the docs, a schema you read etc.. YOU MUST NEVER ASSUME YOU KNOW WHAT THE BODY SCHEMA IS WITHOUT CONCRETE EXAMPLES OR SCHEMA DEFINITIONS FROM THE CONVERSATION CONTEXT. Prefer reading relevant recipes if you have them in context for understand the body schema for API calls.',
38687
+ WQL_FILTER_RULE
38688
+ ].join("\n"),
38612
38689
  reason: "One sentence explaining the original user request and why you are calling this API to complete it.",
38613
38690
  sourceDocUrl: [
38614
38691
  "The URL of the documentation or recipe where you found this API endpoint.",
@@ -38932,15 +39009,17 @@ Failed to fetch content: ${errorMsg}`
38932
39009
  const baseContentsPromise = Promise.all(
38933
39010
  baseDocsWithoutRecipes.map(fetchDoc)
38934
39011
  );
38935
- const skillsContentPromise = getSkillsByPortal(httpClient6, {
39012
+ const skillsContentPromise = skillsTags?.length ? getSkillsByPortal(httpClient6, {
38936
39013
  portalId: REST_PORTAL_ID,
38937
39014
  ...repo && { repo },
38938
39015
  ...commitHash && { commitHash },
38939
- ...skillsTags && skillsTags.length > 0 && { tags: skillsTags }
39016
+ tags: skillsTags
38940
39017
  }).catch((err) => {
38941
39018
  console.error("[WixREADME] Failed to fetch skills by portal:", err);
38942
39019
  return null;
38943
- });
39020
+ }) : fetchDoc({ action: "include", url: RECIPES_INDEX_URL }).then(
39021
+ (doc) => doc?.text ?? null
39022
+ );
38944
39023
  const filter = {};
38945
39024
  if (siteId) filter.siteId = siteId;
38946
39025
  else if (siteName) filter.siteName = siteName;
@@ -39147,7 +39226,9 @@ function getErrorMessage(error2) {
39147
39226
  }
39148
39227
  function shouldTryMenuFallbackForDocsRead(error2, articleUrl) {
39149
39228
  const message = getErrorMessage(error2);
39150
- if (!message.includes("404") || !articleUrl.includes("/api-reference/")) {
39229
+ const { pathname } = new URL(articleUrl);
39230
+ const isMenuFallbackUrl = articleUrl.includes("/api-reference/") || pathname.startsWith("/docs/velo");
39231
+ if (!message.includes("404") || !isMenuFallbackUrl) {
39151
39232
  return false;
39152
39233
  }
39153
39234
  return !["This resource is deprecated", "No schema found"].some(
@@ -39800,7 +39881,7 @@ var addDocsTools = (server, allowedTools = [
39800
39881
  content: [
39801
39882
  {
39802
39883
  type: "text",
39803
- text: `Note: "${articleUrl}" is a documentation category, not an article. Showing its menu contents below. For category URLs, use BrowseWixRESTDocsMenu directly next time.
39884
+ text: `Note: "${articleUrl}" is not an article. Showing the closest documentation menu below; read the article you need from its links. For category URLs, use BrowseWixRESTDocsMenu directly next time.
39804
39885
 
39805
39886
  ${text}`
39806
39887
  }
@@ -39892,7 +39973,7 @@ ${text}`
39892
39973
  content: [
39893
39974
  {
39894
39975
  type: "text",
39895
- text: `Note: "${articleUrl}" is a documentation category, not an article. Showing its menu contents below. For category URLs, use BrowseWixRESTDocsMenu directly next time.
39976
+ text: `Note: "${articleUrl}" is not an article. Showing the closest documentation menu below; read the article you need from its links. For category URLs, use BrowseWixRESTDocsMenu directly next time.
39896
39977
 
39897
39978
  ${text}`
39898
39979
  }
@@ -51956,12 +52037,12 @@ function addImportClaudeDesignTool(server, authStrategy, options) {
51956
52037
  designUrl = new URL(args.url);
51957
52038
  } catch {
51958
52039
  return errorResult(
51959
- "Invalid design URL \u2014 this tool only accepts the claudeusercontent.com URL produced by Claude Design. For site files the user provided themselves, do not retry: send the user to https://www.wix.com/headless/drop?utm_campaign=mcp to upload them and get a live hosted site."
52040
+ "Invalid design URL \u2014 this tool only accepts the claudeusercontent.com URL produced by Claude Design. For site files the user provided themselves, do not retry this tool. For a full agentic flow, read the recipe and publish the files yourself: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md \u2014 it covers creating the site and uploading the files end to end. If you can't follow it, or it doesn't apply, send the user to https://www.wix.com/headless/drop?utm_campaign=mcp to upload them and get a live hosted site."
51960
52041
  );
51961
52042
  }
51962
52043
  if (designUrl.protocol !== "https:" || !designUrl.hostname.endsWith(CLAUDE_USER_CONTENT_HOST_SUFFIX)) {
51963
52044
  return errorResult(
51964
- "Invalid design URL \u2014 this tool only accepts the claudeusercontent.com URL produced by Claude Design. For site files the user provided themselves, do not retry: send the user to https://www.wix.com/headless/drop?utm_campaign=mcp to upload them and get a live hosted site."
52045
+ "Invalid design URL \u2014 this tool only accepts the claudeusercontent.com URL produced by Claude Design. For site files the user provided themselves, do not retry this tool. For a full agentic flow, read the recipe and publish the files yourself: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md \u2014 it covers creating the site and uploading the files end to end. If you can't follow it, or it doesn't apply, send the user to https://www.wix.com/headless/drop?utm_campaign=mcp to upload them and get a live hosted site."
51965
52046
  );
51966
52047
  }
51967
52048
  const accountHeaders = await authStrategy.getAccountAuthHeaders();
@@ -53019,12 +53100,31 @@ function formatExecuteResult(data, maxResponseSize, reportBi) {
53019
53100
  reportBi?.({
53020
53101
  originalLength: typeof data.result === "string" ? data.result.length : JSON.stringify(data.result)?.length
53021
53102
  });
53103
+ const base64Text = base64PayloadText(data.result);
53104
+ if (base64Text !== null) {
53105
+ if (base64Text.length <= MAX_ALLOWED_RESPONSE_CHARS) {
53106
+ return { content: [{ type: "text", text: base64Text }] };
53107
+ }
53108
+ const approxKb = Math.ceil(base64Text.length / 1024).toLocaleString();
53109
+ const limitKb = Math.floor(
53110
+ MAX_ALLOWED_RESPONSE_CHARS / 1024
53111
+ ).toLocaleString();
53112
+ return executeErrorResult(
53113
+ `Binary (base64) result is ~${approxKb} KB, too large to return intact (limit ~${limitKb} KB); truncating it would corrupt the base64. Process the bytes inside your ExecuteWixAPI script \u2014 decode, edit, and re-upload in the same call \u2014 and return only a small summary.`
53114
+ );
53115
+ }
53022
53116
  return {
53023
53117
  content: [
53024
53118
  { type: "text", text: truncateResponse(data.result, maxResponseSize) }
53025
53119
  ]
53026
53120
  };
53027
53121
  }
53122
+ function base64PayloadText(result) {
53123
+ if (result !== null && typeof result === "object" && result.encoding === "base64" && typeof result.data === "string") {
53124
+ return JSON.stringify(result);
53125
+ }
53126
+ return null;
53127
+ }
53028
53128
  function addExecuteTool(server, options) {
53029
53129
  const {
53030
53130
  codeModeBaseUrl,
@@ -53396,10 +53496,14 @@ function createWixToolkit(options) {
53396
53496
  if (!toolSucceeded) {
53397
53497
  errorBody = extractText(cbResult);
53398
53498
  } else {
53399
- additionalFields = formatToolResultAdditionalFields(cbResult, {
53400
- returnedRows: toolBiParams.returnedRows,
53401
- originalLength: toolBiParams.originalLength
53402
- });
53499
+ additionalFields = formatToolResultAdditionalFields(
53500
+ cbResult,
53501
+ {
53502
+ returnedRows: toolBiParams.returnedRows,
53503
+ originalLength: toolBiParams.originalLength
53504
+ },
53505
+ { name: clientName }
53506
+ );
53403
53507
  }
53404
53508
  if (!isDevEnvironment) {
53405
53509
  if (toolSucceeded) {