@wix/mcp 1.0.86 → 1.0.88

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.
@@ -27117,6 +27117,14 @@ var EMPTY_COMPLETION_RESULT = {
27117
27117
 
27118
27118
  // src/tool-utils.ts
27119
27119
  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.";
27120
+ 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.';
27121
+ 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.";
27122
+ var getRetryHint = (errorDetails) => {
27123
+ if (!/Filter contains unsupported operator/i.test(errorDetails)) {
27124
+ return REST_METHOD_CONTRACT_RETRY_HINT;
27125
+ }
27126
+ return /"operator":"\{/.test(errorDetails) ? SINGLE_OPERATOR_PER_FIELD_HINT : FIELD_CAPABILITIES_HINT;
27127
+ };
27120
27128
  var DEFAULT_OPENAI_SECURITY_SCHEMES = [
27121
27129
  { type: "oauth2", scopes: ["offline_access"] }
27122
27130
  ];
@@ -27143,7 +27151,7 @@ var buildErrorMessage = ({
27143
27151
  ].filter((str) => !!str).join("\n");
27144
27152
  return `${baseMessage}
27145
27153
 
27146
- ${REST_METHOD_CONTRACT_RETRY_HINT}`;
27154
+ ${getRetryHint(String(errorDetails))}`;
27147
27155
  };
27148
27156
  var handleWixAPIResponse = async (response) => {
27149
27157
  const requestId = response.headers?.["x-wix-request-id"];
@@ -30873,6 +30881,7 @@ function rethrowIfUnauthorized(error2) {
30873
30881
 
30874
30882
  // src/config/api-request-rules.ts
30875
30883
  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).";
30884
+ 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.";
30876
30885
 
30877
30886
  // src/config/default-config.ts
30878
30887
  var AGENT_GOAL = dedent_default`
@@ -30973,7 +30982,7 @@ var SITE_CREATION_COPY = {
30973
30982
  var SITE_GUIDANCE = dedent_default`
30974
30983
  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.
30975
30984
 
30976
- 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.
30985
+ 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.
30977
30986
 
30978
30987
  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.
30979
30988
  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.
@@ -31032,18 +31041,56 @@ var ACCOUNT_SITE_EXECUTE_SCOPE = dedent_default`
31032
31041
  siteId?: string;
31033
31042
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
31034
31043
  url: string;
31044
+ // Plain object → JSON. A string is sent verbatim (set your own Content-Type,
31045
+ // e.g. multipart/form-data). A Uint8Array/ArrayBuffer is sent as raw bytes
31046
+ // for binary uploads (set your own Content-Type).
31035
31047
  body?: unknown;
31036
31048
  headers?: Record<string, string>;
31049
+ // "base64" returns the response body as a base64 string (data), with
31050
+ // encoding: "base64" on the result — use it for binary downloads (zip,
31051
+ // image, pdf); decoding those as text corrupts them. Binary content types
31052
+ // are returned as base64 automatically even without this. Decode, edit and
31053
+ // re-upload binary WITHIN this script and return only a small summary. If you
31054
+ // must return the bytes, return the wix.request response object itself (so
31055
+ // encoding: "base64" is preserved and it isn't truncated); a bare base64
31056
+ // string you return is treated as plain text and may be truncated.
31057
+ responseType?: "base64";
31037
31058
  }
31038
31059
  interface WixResponse<T = unknown> {
31039
31060
  status: number;
31040
31061
  data: T;
31062
+ encoding?: "base64";
31041
31063
  json(): Promise<T>;
31042
31064
  }
31043
31065
  declare const wix: {
31044
31066
  request<T = unknown>(options: WixRequestOptions): Promise<WixResponse<T>>;
31067
+ // Build a multipart/form-data body from files (defaults to the \`files\`
31068
+ // global). Each file becomes a part named fieldName (default "files") with
31069
+ // its path as the filename. Pass body + contentType to wix.request.
31070
+ multipart(
31071
+ files?: Array<{ path: string; content: string; contentType?: string }>,
31072
+ options?: { fieldName?: string }
31073
+ ): { body: string; contentType: string };
31045
31074
  };
31046
31075
  declare const siteId: string | undefined;
31076
+ // Files from the ExecuteWixAPI \`files\` param (raw content, no escaping).
31077
+ declare const files: Array<{ path: string; content: string }>;
31078
+ \`\`\`
31079
+
31080
+ FILE UPLOADS: pass file bodies via the \`files\` param (not inside \`code\` — no escaping), then build the multipart body with \`wix.multipart()\`:
31081
+ \`\`\`javascript
31082
+ // files param: "=== FILE: index.html ===\\n<!doctype html>...\\n=== FILE: app.js ===\\nconsole.log(1)"
31083
+ async () => {
31084
+ const mp = wix.multipart(); // parts named "files", filename = each path
31085
+ const res = await wix.request({
31086
+ scope: "account",
31087
+ method: "POST",
31088
+ url: "https://www.wixapis.com/.../upload",
31089
+ headers: { "Content-Type": mp.contentType },
31090
+ body: mp.body
31091
+ });
31092
+ return res.data;
31093
+ }
31047
31094
  \`\`\`
31048
31095
  `;
31049
31096
  var EXECUTE_WIX_API_WQL = dedent_default`
@@ -31176,6 +31223,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
31176
31223
  ReadFullDocsArticle: dedent_default`
31177
31224
  Fetches the full Wix docs article or method article with code examples for using the method.
31178
31225
  Docs articles looks like this: https://dev.wix.com/docs/... and they can either be general docs articles or method articles.
31226
+ 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.
31179
31227
  For REST docs, use the URL as-is. For SDK docs, the URL SHOULD include ?apiView=SDK.
31180
31228
  ${reminder}
31181
31229
  `,
@@ -31308,7 +31356,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
31308
31356
  `,
31309
31357
  "import-claude-design-from-url": dedent_default`
31310
31358
  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.
31311
- 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.
31359
+ 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.
31312
31360
  `,
31313
31361
  RunWixCliCommand: dedent_default`
31314
31362
  Executes Wix CLI commands for Wix app development and management.
@@ -31735,7 +31783,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
31735
31783
  CreateWixBusinessGuide: dedent_default`
31736
31784
  Provides comprehensive documentation for creating a new Wix business (site, app, etc.).
31737
31785
  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\`.
31738
- 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.
31786
+ 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.
31739
31787
 
31740
31788
  ## What This Tool Contains
31741
31789
 
@@ -32372,7 +32420,10 @@ var paramDescriptions = {
32372
32420
  ARRAY_QUERY_PARAMS_RULE
32373
32421
  ].join("\n"),
32374
32422
  method: "The HTTP method to use for the API call (e.g. GET, POST, PUT, DELETE)",
32375
- 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.',
32423
+ body: [
32424
+ '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.',
32425
+ WQL_FILTER_RULE
32426
+ ].join("\n"),
32376
32427
  reason: "One sentence explaining the original user request and why you are calling this API to complete it.",
32377
32428
  sourceDocUrl: [
32378
32429
  "The URL of the documentation or recipe where you found this API endpoint.",
@@ -32696,15 +32747,17 @@ Failed to fetch content: ${errorMsg}`
32696
32747
  const baseContentsPromise = Promise.all(
32697
32748
  baseDocsWithoutRecipes.map(fetchDoc)
32698
32749
  );
32699
- const skillsContentPromise = getSkillsByPortal(httpClient6, {
32750
+ const skillsContentPromise = skillsTags?.length ? getSkillsByPortal(httpClient6, {
32700
32751
  portalId: REST_PORTAL_ID,
32701
32752
  ...repo && { repo },
32702
32753
  ...commitHash && { commitHash },
32703
- ...skillsTags && skillsTags.length > 0 && { tags: skillsTags }
32754
+ tags: skillsTags
32704
32755
  }).catch((err) => {
32705
32756
  console.error("[WixREADME] Failed to fetch skills by portal:", err);
32706
32757
  return null;
32707
- });
32758
+ }) : fetchDoc({ action: "include", url: RECIPES_INDEX_URL }).then(
32759
+ (doc) => doc?.text ?? null
32760
+ );
32708
32761
  const filter = {};
32709
32762
  if (siteId) filter.siteId = siteId;
32710
32763
  else if (siteName) filter.siteName = siteName;
@@ -45776,12 +45829,12 @@ function addImportClaudeDesignTool(server, authStrategy, options) {
45776
45829
  designUrl = new URL(args.url);
45777
45830
  } catch {
45778
45831
  return errorResult(
45779
- "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."
45832
+ "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."
45780
45833
  );
45781
45834
  }
45782
45835
  if (designUrl.protocol !== "https:" || !designUrl.hostname.endsWith(CLAUDE_USER_CONTENT_HOST_SUFFIX)) {
45783
45836
  return errorResult(
45784
- "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."
45837
+ "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."
45785
45838
  );
45786
45839
  }
45787
45840
  const accountHeaders = await authStrategy.getAccountAuthHeaders();
@@ -46836,12 +46889,31 @@ function formatExecuteResult(data, maxResponseSize, reportBi) {
46836
46889
  reportBi?.({
46837
46890
  originalLength: typeof data.result === "string" ? data.result.length : JSON.stringify(data.result)?.length
46838
46891
  });
46892
+ const base64Text = base64PayloadText(data.result);
46893
+ if (base64Text !== null) {
46894
+ if (base64Text.length <= MAX_ALLOWED_RESPONSE_CHARS) {
46895
+ return { content: [{ type: "text", text: base64Text }] };
46896
+ }
46897
+ const approxKb = Math.ceil(base64Text.length / 1024).toLocaleString();
46898
+ const limitKb = Math.floor(
46899
+ MAX_ALLOWED_RESPONSE_CHARS / 1024
46900
+ ).toLocaleString();
46901
+ return executeErrorResult(
46902
+ `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.`
46903
+ );
46904
+ }
46839
46905
  return {
46840
46906
  content: [
46841
46907
  { type: "text", text: truncateResponse(data.result, maxResponseSize) }
46842
46908
  ]
46843
46909
  };
46844
46910
  }
46911
+ function base64PayloadText(result) {
46912
+ if (result !== null && typeof result === "object" && result.encoding === "base64" && typeof result.data === "string") {
46913
+ return JSON.stringify(result);
46914
+ }
46915
+ return null;
46916
+ }
46845
46917
  function addExecuteTool(server, options) {
46846
46918
  const {
46847
46919
  codeModeBaseUrl,
@@ -46873,6 +46945,14 @@ function addExecuteTool(server, options) {
46873
46945
  visitor ? "Whether this code creates, updates, deletes, publishes, imports, uploads, or otherwise mutates site data on the visitor\u2019s behalf. Set this to true for create/update/delete/bulk create/import/upload calls even if the reason is inspection, verification, or response-shape discovery. Read-only GET/query/list/search calls can use false." : "Whether this code creates, updates, deletes, publishes, installs, imports, uploads, or otherwise mutates Wix data or site/account state. Set this to true for create/update/delete/bulk create/import/upload calls even if the reason is inspection, verification, or response-shape discovery. Read-only GET/query/list/search calls can use false."
46874
46946
  ),
46875
46947
  sourceDocUrls: external_exports.array(external_exports.string()).describe(d2.sourceDocUrls),
46948
+ files: external_exports.string().optional().describe(
46949
+ [
46950
+ "Optional bundle of raw file contents for uploads, so file bytes never go through the `code` JavaScript (no escaping, no string-literal/`${}`/backtick pitfalls).",
46951
+ "Format: one or more sections, each a line `=== FILE: <path> ===` on its own, followed by that file's raw text content; `<path>` is the path relative to the site root (e.g. `index.html`, `assets/app.js`). Example: `=== FILE: index.html ===\\n<!doctype html>...\\n=== FILE: assets/app.js ===\\nconsole.log(1)`.",
46952
+ 'In `code`, the parsed files are the `files` global (array of `{ path, content }`), and `wix.multipart(files)` returns `{ body, contentType }` for a `multipart/form-data` upload \u2014 e.g. `const mp = wix.multipart(); await wix.request({ scope: "account", method: "POST", url, headers: { "Content-Type": mp.contentType }, body: mp.body });`.',
46953
+ "Text files only; for binary assets pass bytes as a base64 request body instead. A file whose content itself contains a line exactly like the delimiter is not supported."
46954
+ ].join(" ")
46955
+ ),
46876
46956
  // Visitor mode takes an agent-supplied token and pins the site; account/site
46877
46957
  // mode lets the agent pick the site.
46878
46958
  ...visitor ? {
@@ -46887,7 +46967,7 @@ function addExecuteTool(server, options) {
46887
46967
  },
46888
46968
  { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
46889
46969
  async (rawArgs, extra) => {
46890
- const { code, reason, hasMutations, sourceDocUrls, visitorToken } = rawArgs;
46970
+ const { code, reason, hasMutations, sourceDocUrls, visitorToken, files } = rawArgs;
46891
46971
  const siteId = visitor ? visitor.siteId : rawArgs.siteId;
46892
46972
  logger2.log(
46893
46973
  `[ExecuteWixAPI]${visitor ? " (visitor)" : ""} Executing code (${code.length} chars), siteId=${siteId ?? "none"}, hasMutations=${hasMutations}, sourceDocUrls=${JSON.stringify(sourceDocUrls)}, reason="${reason}"`
@@ -46932,7 +47012,8 @@ function addExecuteTool(server, options) {
46932
47012
  siteToken: visitor ? visitorToken : siteHeaders?.Authorization,
46933
47013
  accountToken: visitor ? visitorToken : accountHeaders?.Authorization,
46934
47014
  accountId: accountHeaders?.["wix-account-id"],
46935
- siteAuthContext
47015
+ siteAuthContext,
47016
+ ...files ? { files } : {}
46936
47017
  },
46937
47018
  visitor ? visitorToken : accountHeaders?.Authorization ?? siteHeaders?.Authorization,
46938
47019
  codeModeFetch
@@ -12077,6 +12077,7 @@ function rethrowIfUnauthorized(error) {
12077
12077
 
12078
12078
  // src/config/api-request-rules.ts
12079
12079
  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).";
12080
+ 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.";
12080
12081
 
12081
12082
  // src/config/default-config.ts
12082
12083
  var AGENT_GOAL = dedent_default`
@@ -12177,7 +12178,7 @@ var SITE_CREATION_COPY = {
12177
12178
  var SITE_GUIDANCE = dedent_default`
12178
12179
  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.
12179
12180
 
12180
- 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.
12181
+ 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.
12181
12182
 
12182
12183
  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.
12183
12184
  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.
@@ -12236,18 +12237,56 @@ var ACCOUNT_SITE_EXECUTE_SCOPE = dedent_default`
12236
12237
  siteId?: string;
12237
12238
  method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
12238
12239
  url: string;
12240
+ // Plain object → JSON. A string is sent verbatim (set your own Content-Type,
12241
+ // e.g. multipart/form-data). A Uint8Array/ArrayBuffer is sent as raw bytes
12242
+ // for binary uploads (set your own Content-Type).
12239
12243
  body?: unknown;
12240
12244
  headers?: Record<string, string>;
12245
+ // "base64" returns the response body as a base64 string (data), with
12246
+ // encoding: "base64" on the result — use it for binary downloads (zip,
12247
+ // image, pdf); decoding those as text corrupts them. Binary content types
12248
+ // are returned as base64 automatically even without this. Decode, edit and
12249
+ // re-upload binary WITHIN this script and return only a small summary. If you
12250
+ // must return the bytes, return the wix.request response object itself (so
12251
+ // encoding: "base64" is preserved and it isn't truncated); a bare base64
12252
+ // string you return is treated as plain text and may be truncated.
12253
+ responseType?: "base64";
12241
12254
  }
12242
12255
  interface WixResponse<T = unknown> {
12243
12256
  status: number;
12244
12257
  data: T;
12258
+ encoding?: "base64";
12245
12259
  json(): Promise<T>;
12246
12260
  }
12247
12261
  declare const wix: {
12248
12262
  request<T = unknown>(options: WixRequestOptions): Promise<WixResponse<T>>;
12263
+ // Build a multipart/form-data body from files (defaults to the \`files\`
12264
+ // global). Each file becomes a part named fieldName (default "files") with
12265
+ // its path as the filename. Pass body + contentType to wix.request.
12266
+ multipart(
12267
+ files?: Array<{ path: string; content: string; contentType?: string }>,
12268
+ options?: { fieldName?: string }
12269
+ ): { body: string; contentType: string };
12249
12270
  };
12250
12271
  declare const siteId: string | undefined;
12272
+ // Files from the ExecuteWixAPI \`files\` param (raw content, no escaping).
12273
+ declare const files: Array<{ path: string; content: string }>;
12274
+ \`\`\`
12275
+
12276
+ FILE UPLOADS: pass file bodies via the \`files\` param (not inside \`code\` — no escaping), then build the multipart body with \`wix.multipart()\`:
12277
+ \`\`\`javascript
12278
+ // files param: "=== FILE: index.html ===\\n<!doctype html>...\\n=== FILE: app.js ===\\nconsole.log(1)"
12279
+ async () => {
12280
+ const mp = wix.multipart(); // parts named "files", filename = each path
12281
+ const res = await wix.request({
12282
+ scope: "account",
12283
+ method: "POST",
12284
+ url: "https://www.wixapis.com/.../upload",
12285
+ headers: { "Content-Type": mp.contentType },
12286
+ body: mp.body
12287
+ });
12288
+ return res.data;
12289
+ }
12251
12290
  \`\`\`
12252
12291
  `;
12253
12292
  var EXECUTE_WIX_API_WQL = dedent_default`
@@ -12380,6 +12419,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
12380
12419
  ReadFullDocsArticle: dedent_default`
12381
12420
  Fetches the full Wix docs article or method article with code examples for using the method.
12382
12421
  Docs articles looks like this: https://dev.wix.com/docs/... and they can either be general docs articles or method articles.
12422
+ 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.
12383
12423
  For REST docs, use the URL as-is. For SDK docs, the URL SHOULD include ?apiView=SDK.
12384
12424
  ${reminder}
12385
12425
  `,
@@ -12512,7 +12552,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
12512
12552
  `,
12513
12553
  "import-claude-design-from-url": dedent_default`
12514
12554
  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.
12515
- 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.
12555
+ 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.
12516
12556
  `,
12517
12557
  RunWixCliCommand: dedent_default`
12518
12558
  Executes Wix CLI commands for Wix app development and management.
@@ -12939,7 +12979,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
12939
12979
  CreateWixBusinessGuide: dedent_default`
12940
12980
  Provides comprehensive documentation for creating a new Wix business (site, app, etc.).
12941
12981
  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\`.
12942
- 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.
12982
+ 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.
12943
12983
 
12944
12984
  ## What This Tool Contains
12945
12985
 
@@ -13576,7 +13616,10 @@ var paramDescriptions = {
13576
13616
  ARRAY_QUERY_PARAMS_RULE
13577
13617
  ].join("\n"),
13578
13618
  method: "The HTTP method to use for the API call (e.g. GET, POST, PUT, DELETE)",
13579
- 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.',
13619
+ body: [
13620
+ '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.',
13621
+ WQL_FILTER_RULE
13622
+ ].join("\n"),
13580
13623
  reason: "One sentence explaining the original user request and why you are calling this API to complete it.",
13581
13624
  sourceDocUrl: [
13582
13625
  "The URL of the documentation or recipe where you found this API endpoint.",
@@ -13900,15 +13943,17 @@ Failed to fetch content: ${errorMsg}`
13900
13943
  const baseContentsPromise = Promise.all(
13901
13944
  baseDocsWithoutRecipes.map(fetchDoc)
13902
13945
  );
13903
- const skillsContentPromise = getSkillsByPortal(httpClient, {
13946
+ const skillsContentPromise = skillsTags?.length ? getSkillsByPortal(httpClient, {
13904
13947
  portalId: REST_PORTAL_ID,
13905
13948
  ...repo && { repo },
13906
13949
  ...commitHash && { commitHash },
13907
- ...skillsTags && skillsTags.length > 0 && { tags: skillsTags }
13950
+ tags: skillsTags
13908
13951
  }).catch((err) => {
13909
13952
  console.error("[WixREADME] Failed to fetch skills by portal:", err);
13910
13953
  return null;
13911
- });
13954
+ }) : fetchDoc({ action: "include", url: RECIPES_INDEX_URL }).then(
13955
+ (doc) => doc?.text ?? null
13956
+ );
13912
13957
  const filter = {};
13913
13958
  if (siteId) filter.siteId = siteId;
13914
13959
  else if (siteName) filter.siteName = siteName;
@@ -14944,6 +14989,14 @@ ${text}`
14944
14989
 
14945
14990
  // src/tool-utils.ts
14946
14991
  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.";
14992
+ 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.';
14993
+ 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.";
14994
+ var getRetryHint = (errorDetails) => {
14995
+ if (!/Filter contains unsupported operator/i.test(errorDetails)) {
14996
+ return REST_METHOD_CONTRACT_RETRY_HINT;
14997
+ }
14998
+ return /"operator":"\{/.test(errorDetails) ? SINGLE_OPERATOR_PER_FIELD_HINT : FIELD_CAPABILITIES_HINT;
14999
+ };
14947
15000
  var safeParseJSON = (text) => {
14948
15001
  try {
14949
15002
  return JSON.parse(text);
@@ -14967,7 +15020,7 @@ var buildErrorMessage = ({
14967
15020
  ].filter((str) => !!str).join("\n");
14968
15021
  return `${baseMessage}
14969
15022
 
14970
- ${REST_METHOD_CONTRACT_RETRY_HINT}`;
15023
+ ${getRetryHint(String(errorDetails))}`;
14971
15024
  };
14972
15025
  var handleWixAPIResponse = async (response) => {
14973
15026
  const requestId = response.headers?.["x-wix-request-id"];
@@ -16634,12 +16687,31 @@ function formatExecuteResult(data, maxResponseSize, reportBi) {
16634
16687
  reportBi?.({
16635
16688
  originalLength: typeof data.result === "string" ? data.result.length : JSON.stringify(data.result)?.length
16636
16689
  });
16690
+ const base64Text = base64PayloadText(data.result);
16691
+ if (base64Text !== null) {
16692
+ if (base64Text.length <= MAX_ALLOWED_RESPONSE_CHARS) {
16693
+ return { content: [{ type: "text", text: base64Text }] };
16694
+ }
16695
+ const approxKb = Math.ceil(base64Text.length / 1024).toLocaleString();
16696
+ const limitKb = Math.floor(
16697
+ MAX_ALLOWED_RESPONSE_CHARS / 1024
16698
+ ).toLocaleString();
16699
+ return executeErrorResult(
16700
+ `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.`
16701
+ );
16702
+ }
16637
16703
  return {
16638
16704
  content: [
16639
16705
  { type: "text", text: truncateResponse(data.result, maxResponseSize) }
16640
16706
  ]
16641
16707
  };
16642
16708
  }
16709
+ function base64PayloadText(result) {
16710
+ if (result !== null && typeof result === "object" && result.encoding === "base64" && typeof result.data === "string") {
16711
+ return JSON.stringify(result);
16712
+ }
16713
+ return null;
16714
+ }
16643
16715
  function addExecuteTool(server, options) {
16644
16716
  const {
16645
16717
  codeModeBaseUrl,
@@ -16671,6 +16743,14 @@ function addExecuteTool(server, options) {
16671
16743
  visitor ? "Whether this code creates, updates, deletes, publishes, imports, uploads, or otherwise mutates site data on the visitor\u2019s behalf. Set this to true for create/update/delete/bulk create/import/upload calls even if the reason is inspection, verification, or response-shape discovery. Read-only GET/query/list/search calls can use false." : "Whether this code creates, updates, deletes, publishes, installs, imports, uploads, or otherwise mutates Wix data or site/account state. Set this to true for create/update/delete/bulk create/import/upload calls even if the reason is inspection, verification, or response-shape discovery. Read-only GET/query/list/search calls can use false."
16672
16744
  ),
16673
16745
  sourceDocUrls: external_exports.array(external_exports.string()).describe(d.sourceDocUrls),
16746
+ files: external_exports.string().optional().describe(
16747
+ [
16748
+ "Optional bundle of raw file contents for uploads, so file bytes never go through the `code` JavaScript (no escaping, no string-literal/`${}`/backtick pitfalls).",
16749
+ "Format: one or more sections, each a line `=== FILE: <path> ===` on its own, followed by that file's raw text content; `<path>` is the path relative to the site root (e.g. `index.html`, `assets/app.js`). Example: `=== FILE: index.html ===\\n<!doctype html>...\\n=== FILE: assets/app.js ===\\nconsole.log(1)`.",
16750
+ 'In `code`, the parsed files are the `files` global (array of `{ path, content }`), and `wix.multipart(files)` returns `{ body, contentType }` for a `multipart/form-data` upload \u2014 e.g. `const mp = wix.multipart(); await wix.request({ scope: "account", method: "POST", url, headers: { "Content-Type": mp.contentType }, body: mp.body });`.',
16751
+ "Text files only; for binary assets pass bytes as a base64 request body instead. A file whose content itself contains a line exactly like the delimiter is not supported."
16752
+ ].join(" ")
16753
+ ),
16674
16754
  // Visitor mode takes an agent-supplied token and pins the site; account/site
16675
16755
  // mode lets the agent pick the site.
16676
16756
  ...visitor ? {
@@ -16685,7 +16765,7 @@ function addExecuteTool(server, options) {
16685
16765
  },
16686
16766
  { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
16687
16767
  async (rawArgs, extra) => {
16688
- const { code, reason, hasMutations, sourceDocUrls, visitorToken } = rawArgs;
16768
+ const { code, reason, hasMutations, sourceDocUrls, visitorToken, files } = rawArgs;
16689
16769
  const siteId = visitor ? visitor.siteId : rawArgs.siteId;
16690
16770
  logger.log(
16691
16771
  `[ExecuteWixAPI]${visitor ? " (visitor)" : ""} Executing code (${code.length} chars), siteId=${siteId ?? "none"}, hasMutations=${hasMutations}, sourceDocUrls=${JSON.stringify(sourceDocUrls)}, reason="${reason}"`
@@ -16730,7 +16810,8 @@ function addExecuteTool(server, options) {
16730
16810
  siteToken: visitor ? visitorToken : siteHeaders?.Authorization,
16731
16811
  accountToken: visitor ? visitorToken : accountHeaders?.Authorization,
16732
16812
  accountId: accountHeaders?.["wix-account-id"],
16733
- siteAuthContext
16813
+ siteAuthContext,
16814
+ ...files ? { files } : {}
16734
16815
  },
16735
16816
  visitor ? visitorToken : accountHeaders?.Authorization ?? siteHeaders?.Authorization,
16736
16817
  codeModeFetch
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/mcp",
3
- "version": "1.0.86",
3
+ "version": "1.0.88",
4
4
  "description": "A Model Context Protocol server for Wix AI tools",
5
5
  "bin": "./build/bin-standalone.js",
6
6
  "dependencies": {
@@ -133,5 +133,5 @@
133
133
  ]
134
134
  }
135
135
  },
136
- "falconPackageHash": "bfa0ad2bd45ba9e54844e736741354d379ec2790f92872287c5fc1d2"
136
+ "falconPackageHash": "ca9fecd4d1efd1afea0d159f209aea15cf9475c2edec6f5a172bd92f"
137
137
  }