@wix/mcp 1.0.88 → 1.0.90

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.
@@ -18,7 +18,7 @@ function buildSystemReminderGuidelines(includeReadme, includeImageUploadGuidance
18
18
  const exception = includeReadme ? '**Exception:** ' : '';
19
19
  const lines = [];
20
20
  if (includeReadme) {
21
- lines.push('if the WixREADME tool is available to you, YOU MUST USE IT AT THE BEGINNING OF ANY CONVERSATION and then continue with calling the other tools and calling the Wix APIs until the task is completed.', '**Exception — creating a new Wix site/website:** For a normal creation request — and only then — skip WixREADME and the docs search, and route straight to the Wix site-creation tools available to you; their descriptions carry the routing rules. Everything else starts with WixREADME as usual.', "If the user explicitly asks for a headless site, Wix Classic Editor site, or manual account/API-based site creation, call CreateWixBusinessGuide right away — these don't need the site's purpose first.", "Never default to AI or search templates unless the user explicitly requests it. When the user named no creation method - let the user make that choice as the site-creation tools' descriptions direct. Never route normal site creation through ManageWixSite, CallWixSiteAPI, or ExecuteWixAPI.");
21
+ lines.push('if the WixREADME tool is available to you, YOU MUST USE IT AT THE BEGINNING OF ANY CONVERSATION and then continue with calling the other tools and calling the Wix APIs until the task is completed.', '**Exception — creating a new Wix site/website:** For a normal creation request — and only then — skip WixREADME and the docs search, and route straight to the Wix site-creation tools available to you; their descriptions carry the routing rules. Everything else starts with WixREADME as usual.', "If the user explicitly asks for a headless site, Wix Classic Editor site, or manual account/API-based site creation, call CreateWixBusinessGuide right away — these don't need the site's purpose first.", "If the user — or you, earlier in this conversation — already have a finished page or site in any form (HTML/CSS/JS files, a static build, a zip, another builder's export, a design you can export to HTML) and want it live on Wix, call DeployWebsite right away — not a site-creation tool, not the docs.", "Never default to AI or search templates unless the user explicitly requests it. When the user named no creation method - let the user make that choice as the site-creation tools' descriptions direct. Never route normal site creation through ManageWixSite, CallWixSiteAPI, or ExecuteWixAPI.");
22
22
  }
23
23
  lines.push(`${exception}If the user asks to list, show, or find their Wix sites, ${includeReadme ? 'skip WixREADME and ' : ''}call ListWixSites directly.`);
24
24
  if (includeImageUploadGuidance) {
@@ -80,7 +80,7 @@ export const SITE_CREATION_COPY = {
80
80
  const SITE_GUIDANCE = dedent `
81
81
  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.
82
82
 
83
- 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.
83
+ One case skips all of this: a finished page or site ALREADY EXISTS, in any form — hand-written HTML/CSS/JS, a static build, a zip, another AI builder's export, or a page you made earlier in this conversation. There is nothing to build, so don't offer AI or templates and don't call any site-creation tool: call DeployWebsite, which returns the recipe to publish the files yourself (the drop page is only its fallback).
84
84
 
85
85
  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.
86
86
  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.
@@ -167,32 +167,49 @@ const ACCOUNT_SITE_EXECUTE_SCOPE = dedent `
167
167
  request<T = unknown>(options: WixRequestOptions): Promise<WixResponse<T>>;
168
168
  // Build a multipart/form-data body from files (defaults to the \`files\`
169
169
  // global). Each file becomes a part named fieldName (default "files") with
170
- // its path as the filename. Pass body + contentType to wix.request.
170
+ // its path as the filename; \`body\` is bytes so binary parts stay intact.
171
+ // Pass a base64 entry for binary (an image/font): { path, base64 }, or a
172
+ // \`files\`-param entry tagged \`base64\`.
171
173
  multipart(
172
- files?: Array<{ path: string; content: string; contentType?: string }>,
174
+ files?: Array<{ path: string; content?: string; base64?: string; bytes?: Uint8Array; contentType?: string }>,
173
175
  options?: { fieldName?: string }
174
- ): { body: string; contentType: string };
176
+ ): { body: Uint8Array; contentType: string };
177
+ // Unzip a downloaded site archive (base64 string or bytes) into entries with
178
+ // RAW bytes (binary-safe); entry.text() decodes a text file. Entries can be
179
+ // passed straight back to wix.multipart() for a re-upload.
180
+ unzip(input: string | Uint8Array): Promise<Array<{ path: string; bytes: Uint8Array; text(): string }>>;
175
181
  };
176
182
  declare const siteId: string | undefined;
177
- // Files from the ExecuteWixAPI \`files\` param (raw content, no escaping).
178
- declare const files: Array<{ path: string; content: string }>;
183
+ // Files from the ExecuteWixAPI \`files\` param. Text entries have \`content\`;
184
+ // an entry tagged \`=== FILE: <path> base64 ===\` has encoding:'base64' and
185
+ // base64 \`content\` (a binary file — image, font).
186
+ declare const files: Array<{ path: string; content: string; encoding?: "base64" }>;
179
187
  \`\`\`
180
188
 
181
- FILE UPLOADS: pass file bodies via the \`files\` param (not inside \`code\` — no escaping), then build the multipart body with \`wix.multipart()\`:
189
+ FILE UPLOADS: put every file in the \`files\` param (never inline in \`code\`). Text goes raw; a small binary (icon, logo, font) rides as a \`base64\`-tagged entry — large images/video are referenced by URL (\`<img src="https://...">\`), not bundled. \`wix.multipart()\` turns the whole set into one upload body (bytes, so binary is safe):
182
190
  \`\`\`javascript
183
- // files param: "=== FILE: index.html ===\\n<!doctype html>...\\n=== FILE: app.js ===\\nconsole.log(1)"
191
+ // files param:
192
+ // === FILE: index.html ===
193
+ // <!doctype html><img src="logo.png">...
194
+ // === FILE: logo.png base64 ===
195
+ // iVBORw0KGgoAAA...
184
196
  async () => {
185
- const mp = wix.multipart(); // parts named "files", filename = each path
186
- const res = await wix.request({
187
- scope: "account",
188
- method: "POST",
189
- url: "https://www.wixapis.com/.../upload",
190
- headers: { "Content-Type": mp.contentType },
191
- body: mp.body
192
- });
197
+ const mp = wix.multipart(); // all entries from the files param; base64 ones decoded to raw bytes
198
+ const res = await wix.request({ scope: "account", method: "POST",
199
+ url: "https://www.wixapis.com/.../drop",
200
+ headers: { "Content-Type": mp.contentType }, body: mp.body });
193
201
  return res.data;
194
202
  }
195
203
  \`\`\`
204
+ DOWNLOAD + EDIT + RE-UPLOAD: fetch the zip as base64, \`wix.unzip()\` it, edit text via \`entry.text()\`, keep binary as bytes, and hand the entries back to \`wix.multipart()\`:
205
+ \`\`\`javascript
206
+ const r = await wix.request({ scope: "account", method: "GET", responseType: "base64", url: zipUrl });
207
+ const entries = await wix.unzip(r.data); // [{ path, bytes, text() }]
208
+ const edited = entries.map(e => e.path === "index.html"
209
+ ? { path: e.path, content: e.text().replace("Old title", "New title") }
210
+ : e); // images etc. pass through untouched as bytes
211
+ const mp = wix.multipart(edited);
212
+ \`\`\`
196
213
  `;
197
214
  const EXECUTE_WIX_API_WQL = dedent `
198
215
  For query and search endpoints: each endpoint declares which fields it can filter and sort by, and with which operators. That list is closed for fields and their operators and covers that endpoint only, and a field's presence in the response is no evidence it can be filtered on. It lives in the method's declared capabilities, and some resources also publish it as a "Supported Filters and Sorting" page. Logical operators are WQL syntax rather than field capabilities and never appear in it. Anything else errors or silently returns the wrong rows; fetch a bounded page and filter it in your own code instead.
@@ -321,6 +338,7 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
321
338
  ReadFullDocsArticle: dedent `
322
339
  Fetches the full Wix docs article or method article with code examples for using the method.
323
340
  Docs articles looks like this: https://dev.wix.com/docs/... and they can either be general docs articles or method articles.
341
+ Also reads Wix skills pages — https://www.wix.com/skills/<skill>, https://dev.wix.com/docs/skills/<skill>, and the files they link (e.g. .../<skill>/references/<file>.md) — exactly as served.
324
342
  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.
325
343
  For REST docs, use the URL as-is. For SDK docs, the URL SHOULD include ?apiView=SDK.
326
344
  ${reminder}
@@ -328,7 +346,7 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
328
346
  ReadFullDocsMethodSchema: dedent `
329
347
  Fetches the full method schema for a given method.
330
348
  This will give you the entire request/response schema with all the fields and their descriptions.
331
- For REST API methods, prefer SearchWixAPISpec when it is available: it can fetch and inspect the exact method schema by docs URL, return the request/response shape, and inspect selected nested component schemas without dumping unrelated fields. Use ReadFullDocsMethodSchema for REST only when SearchWixAPISpec is unavailable or did not provide the needed detail.
349
+ For REST and SDK API-reference methods, prefer SearchWixAPISpec when it is available: it can fetch and inspect the exact method schema by docs URL, return the request/response shape, the JavaScript SDK call (\`method.sdk\`: package, namespace, method, positional parameters, examples), and inspect selected nested component schemas without dumping unrelated fields. Use ReadFullDocsMethodSchema only when SearchWixAPISpec is unavailable or did not provide the needed detail.
332
350
  For REST docs, use the URL as-is. For SDK docs, the URL SHOULD include ?apiView=SDK.
333
351
  ${reminder}
334
352
  `,
@@ -454,7 +472,7 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
454
472
  `,
455
473
  'import-claude-design-from-url': dedent `
456
474
  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.
457
- 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.
475
+ NOT for a page or site that exists in any other form (the user's own HTML/CSS/JS, a static build, a zip, another builder's export, a page you made in this conversation) — for those call DeployWebsite instead; it returns the recipe to publish the files yourself.
458
476
  `,
459
477
  RunWixCliCommand: dedent `
460
478
  Executes Wix CLI commands for Wix app development and management.
@@ -483,6 +501,7 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
483
501
  `,
484
502
  SearchWixSDKDocumentation: dedent `
485
503
  Searches the Wix JavaScript SDK documentation.
504
+ Next step for an API-reference method it returns: pass its docs URL (with or without \`?apiView=SDK\`) to \`SearchWixAPISpec\`'s \`getResourceSchemaByUrl\` when available, and read \`method.sdk\` for the exact import, call signature, typed parameters, return type, and examples.
486
505
  `,
487
506
  SearchBuildAppsDocumentation: dedent `
488
507
  Searches the Wix Build Apps documentation.
@@ -495,18 +514,18 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
495
514
  SearchWixHeadlessDocumentation: dedent `
496
515
  Searches the Wix Headless documentation.
497
516
  `,
498
- // Note: The raw schema also contains sdkData (JS SDK package name, namespace, and
499
- // code examples) but it's intentionally omitted from the tool description. Currently
500
- // this MCP is focused on REST API calls via ExecuteWixAPI. We may want to expose
501
- // sdkData in the future — needs further thought on use cases and presentation.
517
+ // Note: each method also carries its JavaScript SDK facet (`method.sdk`: package,
518
+ // namespace, method name, positional signature, parameter schema, JS examples),
519
+ // merged at spec-sync time from the docs' SDK view. REST fields are unchanged;
520
+ // `sdk` is simply absent on methods the SDK docs don't list.
502
521
  SearchWixAPISpec: dedent `
503
- Inspect the Wix REST API spec by writing JavaScript code that runs in a sandboxed read-only environment.
504
- **Precondition:** you already have a method \`docsUrl\` — from \`SearchWixRESTDocumentation\`, \`BrowseWixRESTDocsMenu\`, \`ReadFullDocsArticle\`, ${includeReadme ? 'a `WixREADME` recipe, ' : ''}or a prior tool result / the user's message in this conversation. If you don't have one yet, call \`SearchWixRESTDocumentation\` FIRST to discover it; do not use this tool to search for or choose an endpoint.
522
+ Inspect the Wix REST API spec, and the JavaScript SDK call for each method, by writing JavaScript code that runs in a sandboxed read-only environment.
523
+ **Precondition:** you already have a method \`docsUrl\` — from \`SearchWixRESTDocumentation\`, \`SearchWixSDKDocumentation\` (its \`?apiView=SDK\` URLs work here as-is), \`BrowseWixRESTDocsMenu\`, \`ReadFullDocsArticle\`, ${includeReadme ? 'a `WixREADME` recipe, ' : ''}or a prior tool result / the user's message in this conversation. If you don't have one yet, call \`SearchWixRESTDocumentation\` (or \`SearchWixSDKDocumentation\` when writing SDK code) FIRST to discover it; do not use this tool to search for or choose an endpoint.
505
524
  **Never pass a \`docsUrl\` you produced from memory.** If the exact URL you are about to pass to \`getResourceSchemaByUrl\` did not appear verbatim in an earlier tool result (or the user's message), it is a guess — call \`SearchWixRESTDocumentation\` first and use a URL from its ranked results. A guessed URL that merely looks plausible routinely resolves to the WRONG sibling method (e.g. \`.../products-v3/create-product\` when the task needs \`.../products-v3/create-product-with-inventory\`), sending the whole turn down a dead end. When in doubt, discover — don't guess.
506
- **This tool's job is to INSPECT the exact schema of a method you have ALREADY located — its request/response shape, nested types, and enums. It is NOT a discovery tool for choosing which method to use.**
525
+ **This tool's job is to INSPECT the exact schema of a method you have ALREADY located — its request/response shape, nested types, and enums, and its SDK call (\`method.sdk\`). It is NOT a discovery tool for choosing which method to use.**
507
526
  With a method \`docsUrl\` in hand, call \`getResourceSchemaByUrl(methodDocsUrl)\` to get the resource schema, then read the target method from \`schema.methods\` and resolve any nested \`{ $circular: name }\` types via \`schema.components.schemas[name]\`.
508
527
  **If you don't already have a \`docsUrl\` in context, your FIRST action MUST be \`SearchWixRESTDocumentation\` (ranked semantic search) — not this tool.** Then pass the resulting \`docsUrl\` to \`getResourceSchemaByUrl\` here. Do NOT discover endpoints by hand-filtering or scanning \`lightIndex\` (e.g. \`lightIndex.filter(...)\` / \`lightIndex.flatMap(...)\` on a substring): it has no relevance ranking, matches only at resource-name granularity, and misses composite or method-level operations whose parent resource is named after a different noun than your intent. \`lightIndex\` is for resolving a \`docsUrl\` you already have, not for finding one.
509
- Prefer \`SearchWixAPISpec\` over \`ReadFullDocsMethodSchema\` for REST method schemas when it is available, especially once you already have a docs URL from semantic search, menu browsing, or conversation context.
528
+ Prefer \`SearchWixAPISpec\` over \`ReadFullDocsMethodSchema\` for REST and SDK method schemas when it is available, especially once you already have a docs URL from semantic search, menu browsing, or conversation context.
510
529
 
511
530
  Prefer URL-first results:
512
531
  - If you have a docs URL or partial docs URL, search \`resource.docsUrl\` and \`method.docsUrl\` first.
@@ -514,7 +533,7 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
514
533
  - For API execution, return and use \`method.publicUrl\` when available. It is the preferred executable \`https://www.wixapis.com/...\` URL.
515
534
  - Return \`docsUrl\` for relevant resources/methods when the next step needs an article or API call source URL; do not hand off to \`ReadFullDocsMethodSchema\` just to inspect a REST method schema.
516
535
  - Use \`resourceId\` only as the internal handle for low-level loaders; prefer URL helpers when you have a docs URL.
517
- - \`getResourceSchemaByUrl\` resolves only **API resource/method** docs (e.g. \`.../bookings/services/services-v2/create-service\`). It does NOT work on **skill** pages (\`.../skills/...\`) or **article** pages — those have no schema. For an article, use \`getArticleContentByUrl(docsUrl)\`. Never pass a \`.../skills/...\` URL (skill recipes often cross-link sibling skill pages) — use \`SearchWixRESTDocumentation\` to find the real API method instead. If a lookup misses, don't retry the same URL; rediscover it with \`SearchWixRESTDocumentation\`.
536
+ - \`getResourceSchemaByUrl\` resolves only **API resource/method** docs (e.g. \`.../bookings/services/services-v2/create-service\`). It does NOT work on **skill** pages (\`.../skills/...\`) or **article** pages — those have no schema. For an article, use \`getArticleContentByUrl(docsUrl)\`. Never pass a \`.../skills/...\` URL (skill recipes often cross-link sibling skill pages) — read the skill page with \`ReadFullDocsArticle\`, and use \`SearchWixRESTDocumentation\` to find the real API method. If a lookup misses, don't retry the same URL; rediscover it with \`SearchWixRESTDocumentation\`.
518
537
 
519
538
  Your code has access to these globals:
520
539
 
@@ -529,6 +548,8 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
529
548
  resourceId: string; // internal handle for getResourceSchema()
530
549
  docsUrl: string; // e.g. "https://dev.wix.com/docs/api-reference/.../<resource>"
531
550
  menuPath: string[]; // e.g. ["business-solutions", "<vertical>", "<resource>"]
551
+ sdkPackage?: string; // JS SDK npm package shared by the resource's methods, e.g. "@wix/<vertical>"
552
+ sdkNamespace?: string; // module exported by that package, e.g. "<resources>" — absent when the methods differ (then each method carries its own)
532
553
  methods: Array<{
533
554
  operationId: string; // e.g. "wix.<vertical>.v3.<Api>.<Operation>"
534
555
  summary: string; // human-readable method name
@@ -538,6 +559,9 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
538
559
  publicUrl?: string; // preferred executable URL for ExecuteWixAPI, when available after spec sync
539
560
  publicBaseUrl?: string;
540
561
  description: string; // truncated to 200 chars
562
+ sdk?: string; // the JS SDK call as the SDK docs show it, e.g. "query<Resources>(query, options)"; absent when the SDK docs don't list the method
563
+ sdkPackage?: string; // only when the resource-level value is absent
564
+ sdkNamespace?: string;
541
565
  }>;
542
566
  }
543
567
  \`\`\`
@@ -566,8 +590,17 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
566
590
  legacyExamples: Array<{ // Curl examples
567
591
  content: { title: string; request: string; response: string };
568
592
  }>;
593
+ sdk?: { // JavaScript SDK facet; absent when the SDK docs don't list the method
594
+ package: string; // npm package, e.g. "@wix/<vertical>" → import { <namespace> } from "<package>"
595
+ namespace: string; // module exported by the package, e.g. "<resources>"
596
+ method: string; // SDK method name; NOT always the operation name (e.g. Data Items' InsertDataItem is "insert")
597
+ signature: string[]; // positional parameter names in call order, e.g. ["query", "options"]
598
+ requestBody: object | null; // same envelope as the REST requestBody; its properties ARE the parameters in \`signature\`, each typed
599
+ responses?: object; // only when the SDK returns something other than the REST response (e.g. the created entity instead of a Create<X>Response wrapper)
600
+ examples: Array<{ title: string; description?: string; code: string }>; // JavaScript examples from the SDK docs
601
+ };
569
602
  }>;
570
- components: { schemas: object };
603
+ components: { schemas: object }; // REST types plus the SDK's own types; \`$circular\` names from either body resolve here, exactly as written (SDK-shaped copies of shared types are keyed "sdk:<TypeName>")
571
604
  }
572
605
  \`\`\`
573
606
 
@@ -629,6 +662,16 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
629
662
  - When inspecting a specific method schema (i.e. you have found a single method and are returning its details), always include \`responses: method.responses\` alongside \`requestBody\`. Knowing the response shape up front prevents speculative re-runs of mutations just to see what the API returned.
630
663
  - Query/search methods: the declared map is \`method.queryMethodData.queryFieldsCapabilitiesMap\`, or \`method.searchMethodData.searchFieldsCapabilitiesMap\` on search methods. Each entry declares one field's allowed \`operators\` and \`sort\` directions — a closed list, valid for that method alone, so another endpoint's map or a field's presence in the response proves nothing here. Each listed operator is valid on its own — a field takes one — so combine conditions with the logical operators instead: \`$and\` and \`$or\` take an array of expressions, \`$not\` takes one, and they can nest. They are WQL syntax rather than a field capability, so they never appear in the map, and its silence about them is no restriction. An undeclared field or unlisted operator are rejected; some endpoints instead ignore the filter or sort and return the wrong rows. \`sort: []\` means not sortable; no map means nothing is declared, so try what you need and filter in code if it isn't supported.
631
664
 
665
+ JavaScript SDK (writing code with \`@wix/sdk\` and the \`@wix/*\` packages, in Wix Headless, Velo, CLI apps, or any Node/browser project):
666
+ - Read \`method.sdk\`, not the REST fields. The REST \`requestBody\` is the wire shape; the SDK wraps it differently — fields become positional arguments, and secondary fields move into an \`options\` object — so never derive an SDK call from the REST body. \`sdk.requestBody.properties\` are the parameters, in the order given by \`sdk.signature\`; \`sdk.requestBody.required\` says which are mandatory.
667
+ - The call is \`<namespace>.<method>(<signature>)\` after \`import { <namespace> } from "<package>"\` (e.g. \`import { services } from "@wix/bookings"\`), or \`wixClient.<namespace>.<method>(...)\` on a client created with \`createClient({ modules: { <namespace> } })\`. \`sdk.method\` is the only reliable method name — it is not always the camelCased operation name.
668
+ - Return value: \`await\` resolves to \`sdk.responses\` when present, otherwise to the same shape as \`method.responses\`. Resolve \`$circular\` names from either body against \`schema.components.schemas\` exactly as written: an SDK body may point at an SDK-shaped copy of a type, keyed \`"sdk:<TypeName>"\` (e.g. \`sdk:wix.bookings.services.v2.Service\`), which sits in the same dictionary.
669
+ - Field names differ from REST on entity system fields: the SDK reads and writes \`_id\`, \`_createdDate\`, \`_updatedDate\` where REST has \`id\`, \`createdDate\`, \`updatedDate\`. The SDK-shaped types already use the SDK names — take field names from them, never from the REST body or a REST-keyed type.
670
+ - Timestamps: where a REST field is a \`date-time\` string, the SDK takes and returns a JavaScript \`Date\` object (the SDK types spell this \`type: "Date"\`).
671
+ - \`sdk.examples[].code\` are the SDK docs' JavaScript examples; prefer adapting one over composing a call from the schema. Query methods use the same WQL \`filter\`/\`sort\` rules and the same \`queryMethodData\` map as REST.
672
+ - No \`method.sdk\` means the SDK docs don't list that method: say so and offer the REST call instead of guessing an SDK signature. Where the method is listed, \`lightIndex\` carries the same call compactly as \`method.sdk\` (a string), with the package and namespace on the resource.
673
+ - For ExecuteWixAPI, keep using \`method.publicUrl\` and the REST \`requestBody\`; the SDK facet is for code the user will run, not for calls made through this MCP.
674
+
632
675
  Examples:
633
676
 
634
677
  Inspect one method schema by exact docs URL:
@@ -673,6 +716,39 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
673
716
  }
674
717
  \`\`\`
675
718
 
719
+ Get the JavaScript SDK call for a method — import, signature, typed parameters, return type, and a docs example (the docs URL may come from \`SearchWixSDKDocumentation\`, with or without \`?apiView=SDK\`):
720
+ \`\`\`javascript
721
+ async function() {
722
+ const methodUrl = "https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/query-products?apiView=SDK";
723
+
724
+ const schema = await getResourceSchemaByUrl(methodUrl);
725
+ const method = schema.methods.find(method => method.docsUrl === methodUrl.replace(/\\?.*$/, ""));
726
+ if (!method) return { message: "No exact method URL match", methods: schema.methods.map(m => ({ title: m.summary, docsUrl: m.docsUrl, sdk: m.sdk && m.sdk.method })) };
727
+ if (!method.sdk) return { message: "The SDK docs do not list this method; use the REST call.", publicUrl: method.publicUrl, requestBody: method.requestBody };
728
+
729
+ const { sdk } = method;
730
+ const types = schema.components.schemas;
731
+ // Each positional parameter, typed. The parameters body is pre-expanded for a method URL;
732
+ // any remaining { $circular: name } resolves against types[name].
733
+ const paramsSchema = sdk.requestBody?.content?.["application/json"]?.schema ?? {};
734
+ const parameters = sdk.signature.map(name => ({
735
+ name,
736
+ required: (paramsSchema.required ?? []).includes(name),
737
+ schema: paramsSchema.properties?.[name]
738
+ }));
739
+
740
+ return {
741
+ import: \`import { \${sdk.namespace} } from "\${sdk.package}";\`,
742
+ call: \`\${sdk.namespace}.\${sdk.method}(\${sdk.signature.join(", ")})\`,
743
+ parameters,
744
+ returns: sdk.responses ?? method.responses,
745
+ // Query methods: the same closed list of filterable/sortable fields applies to the SDK call.
746
+ queryFieldsCapabilities: method.queryMethodData?.queryFieldsCapabilitiesMap,
747
+ example: sdk.examples[0]
748
+ };
749
+ }
750
+ \`\`\`
751
+
676
752
  Inspect one resource by resource docs URL:
677
753
  \`\`\`javascript
678
754
  async function() {
@@ -877,11 +953,18 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
877
953
  Compose: put independent reads in one document as several root fields, and use related-entity fields (e.g. \`inventoryItems\`, \`collections\`, \`fulfillments\`) instead of extra round-trips. Select only the fields you need; every object field needs a selection set. Variables: use them for \`JSON\` scalars (filter, sort) and plain scalars (String, Int, ID), declared in the document (e.g. \`query Q($f: JSON) { ... filter: $f ... }\`). Write mutation inputs and any object that contains an enum INLINE as GraphQL literals with enum values unquoted (e.g. \`input: { product: { name: "Mug", productType: PHYSICAL } }\`) — this endpoint fails with 503 when an enum value arrives inside an object-typed variable. For lists page with \`cursorPaging\` or \`paging\` exactly as the input type declares (never the other one), and request \`pageInfo { hasNext total }\`; \`total\` is only populated with offset \`paging\`, so for a cursor-paged list count by walking pages or use a count operation.
878
954
  Results: each root field fails independently — \`errors[].path\` names the failing field and \`errors[].extensions.message\` the cause (e.g. PERMISSION_DENIED); \`data\` still holds the fields that succeeded. Mutations return the created/updated entity — read the IDs from it and do not re-query to verify. Large results are truncated: narrow the selection or page size instead of re-running.
879
955
  Set \`hasMutations: true\` for any document containing a mutation root field.
956
+ `,
957
+ DeployWebsite: dedent `
958
+ The user — or you, earlier in this conversation — ALREADY HAVE a finished page or site, in any form, and it should be live on Wix: HTML/CSS/JS files, a static build (dist/, out/), a zip, another AI builder's export, or a page/design you created in this conversation (export it to HTML first — a finished design IS a finished page). Call this when the user says deploy, host, publish, put online, make it live, get a URL, or upload my site / index.html.
959
+ Also for updating a site deployed this way (replace its files; same site, same URL) and for implementing a backend on one (stores, bookings, CMS, members) as a Wix Headless project — same site, same URL. Works with or without a Wix login (anonymous deploy with a save link), through the Wix MCP alone or from a shell.
960
+ Missing real functionality — a fake checkout, a form that goes nowhere, bookings — is NOT a reason to rebuild the site with a site-creation tool: deploy the page as is, then add the backend afterwards (the recipe covers it).
961
+ Returns the full recipe; follow it and run the flow yourself. The drop page is only the fallback when you can't.
962
+ NOT for: a screenshot, mockup or reference image of a site the user WANTS (nothing exists to deploy yet — that is site creation: WixSiteBuilder / templates); a Claude Design share URL (import-claude-design-from-url); migrating a live site by URL or CSV exports; adding HTML or code into an existing Wix site; uploading images or documents to a site's media (UploadImageToWixSite).
880
963
  `,
881
964
  CreateWixBusinessGuide: dedent `
882
965
  Provides comprehensive documentation for creating a new Wix business (site, app, etc.).
883
966
  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\`.
884
- 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.
967
+ Exception, even when the user says "headless": if a finished page or site already exists in any form (their own HTML/CSS/JS, a static build, a zip, an AI-builder export, a page you made in this conversation) and they want it hosted, don't call this tool — call DeployWebsite, which returns the recipe to publish the files yourself. This guide's CLI flow is for BUILDING a headless frontend, not for hosting ready-made files.
885
968
 
886
969
  ## What This Tool Contains
887
970
 
@@ -107,7 +107,8 @@ export const paramDescriptions = {
107
107
  },
108
108
  ReadFullDocsArticle: {
109
109
  articleUrl: 'The URL of the docs article or method article to fetch. Should be something like https://dev.wix.com/docs/.../...' +
110
- ' For REST docs, use the URL as-is. For SDK docs, the URL SHOULD include the query param ?apiView=SDK (e.g. https://dev.wix.com/docs/...?apiView=SDK).'
110
+ ' For REST docs, use the URL as-is. For SDK docs, the URL SHOULD include the query param ?apiView=SDK (e.g. https://dev.wix.com/docs/...?apiView=SDK).' +
111
+ ' Wix skills pages (https://www.wix.com/skills/..., https://dev.wix.com/docs/skills/...) are read as-is, including their referenced files.'
111
112
  },
112
113
  ReadFullDocsMethodSchema: {
113
114
  articleUrl: 'The URL of the documentation to fetch. Should be something like https://dev.wix.com/docs/.../...' +
@@ -0,0 +1,13 @@
1
+ import { WixMcpServer } from '../wix-mcp-server.js';
2
+ export declare const DEPLOY_WEBSITE_TOOL_NAME = "DeployWebsite";
3
+ export declare const DEPLOY_WEBSITE_RECIPE_URL = "https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md";
4
+ export declare const DROP_PAGE_URL = "https://www.wix.com/headless/drop?utm_campaign=mcp";
5
+ export declare function deployWebsiteResponse(recipe: string): string;
6
+ export declare const DEPLOY_WEBSITE_POINTER_RESPONSE: string;
7
+ export interface DeployWebsiteFetchOptions {
8
+ skillsPortalUrl?: string;
9
+ repo?: string;
10
+ commitHash?: string;
11
+ }
12
+ export declare function addDeployWebsiteTool(server: WixMcpServer, disableTools?: string[], toolDescriptions?: Record<string, string>, fetchOptions?: DeployWebsiteFetchOptions): void;
13
+ //# sourceMappingURL=deploy-website.d.ts.map
@@ -0,0 +1,45 @@
1
+ import { defaultToolDescriptions } from '../config/default-config.js';
2
+ import { fetchArticleContent } from './fetch-article.js';
3
+ export const DEPLOY_WEBSITE_TOOL_NAME = 'DeployWebsite';
4
+ export const DEPLOY_WEBSITE_RECIPE_URL = 'https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md';
5
+ export const DROP_PAGE_URL = 'https://www.wix.com/headless/drop?utm_campaign=mcp';
6
+ const FALLBACK_LINE = `Only if you cannot run it (you can't reach the file bytes, or a step fails and you can't fix it): send the user to ${DROP_PAGE_URL} — give that URL exactly as written, keeping the utm_campaign=mcp parameter.`;
7
+ // The tool exists to take the agent's attention at routing time; the work itself
8
+ // is the recipe, so the response IS the recipe — fetched live and returned whole,
9
+ // so the agent never has to decide whether to go read it.
10
+ export function deployWebsiteResponse(recipe) {
11
+ return [
12
+ `The following is the content of ${DEPLOY_WEBSITE_RECIPE_URL}. Follow it end to end and do the publishing yourself — do not hand the user instructions.`,
13
+ FALLBACK_LINE,
14
+ '---',
15
+ recipe.trim()
16
+ ].join('\n\n');
17
+ }
18
+ // When the recipe can't be fetched, the pointer still routes correctly.
19
+ export const DEPLOY_WEBSITE_POINTER_RESPONSE = [
20
+ `Read and follow this recipe end to end: ${DEPLOY_WEBSITE_RECIPE_URL}`,
21
+ 'It covers creating the site and uploading the files (with or without a Wix login), updating a site deployed this way, and implementing a backend on it (stores, bookings, CMS, members) as a Wix Headless project. Do the publishing yourself — do not hand the user instructions.',
22
+ FALLBACK_LINE
23
+ ].join('\n\n');
24
+ export function addDeployWebsiteTool(server, disableTools = [], toolDescriptions, fetchOptions = {}) {
25
+ if (disableTools.includes(DEPLOY_WEBSITE_TOOL_NAME)) {
26
+ return;
27
+ }
28
+ server.tool(DEPLOY_WEBSITE_TOOL_NAME, toolDescriptions?.[DEPLOY_WEBSITE_TOOL_NAME] ??
29
+ defaultToolDescriptions[DEPLOY_WEBSITE_TOOL_NAME], {}, { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async (_args, { httpClient }) => {
30
+ let text = DEPLOY_WEBSITE_POINTER_RESPONSE;
31
+ try {
32
+ // Same path ReadFullDocsArticle takes for this page: the rawdocs article
33
+ // API, addressed without the .md suffix, header scaffolding stripped.
34
+ const recipe = await fetchArticleContent(httpClient, DEPLOY_WEBSITE_RECIPE_URL.replace(/\.md$/, ''), 'article', true, fetchOptions.skillsPortalUrl, fetchOptions.repo, fetchOptions.commitHash);
35
+ if (recipe && recipe.trim()) {
36
+ text = deployWebsiteResponse(recipe);
37
+ }
38
+ }
39
+ catch (error) {
40
+ console.warn(`[DeployWebsite] recipe fetch failed, returning the pointer:`, error instanceof Error ? error.message : error);
41
+ }
42
+ return { content: [{ type: 'text', text }] };
43
+ });
44
+ }
45
+ //# sourceMappingURL=deploy-website.js.map
@@ -19,6 +19,8 @@ export interface ReadmeDocConfig {
19
19
  export interface DocsToolsOptions {
20
20
  getToKnowWixEnabled?: boolean;
21
21
  createWixBusinessGuideEnabled?: boolean;
22
+ /** DeployWebsite (publish ready-made site files) — an account-owner flow; off for visitor servers. */
23
+ deployWebsiteEnabled?: boolean;
22
24
  disableTools?: string[];
23
25
  /** Custom descriptions for tools, keyed by tool name */
24
26
  toolDescriptions?: Record<string, string>;
@@ -2,12 +2,13 @@ import { z } from 'zod';
2
2
  import { runSemanticSearchAndFormat } from './semanticSearch.js';
3
3
  import { logger } from '../infra/logger.js';
4
4
  import { captureException } from '@sentry/node';
5
- import { fetchArticleContent } from './fetch-article.js';
5
+ import { fetchArticleContent, isSkillsRegistryUrl } from './fetch-article.js';
6
6
  import { normalizeMenuContent, summarizeLargeMenu, fetchMenuContent, MENU_CONTENT_THRESHOLD } from './normalize-menu-content.js';
7
7
  import { fetchPlatformizedMenu, MenuNotFoundError } from './browse-menu.js';
8
8
  import { isQueryOrSearchEndpoint, QUERY_CAPABILITY_MARKER } from './searchFormatting.js';
9
9
  import { addGetToKnowTools, addGetSiteContextTool } from './get-to-know.js';
10
10
  import { addCreateWixBusinessGuideTools } from './create-site-guide.js';
11
+ import { addDeployWebsiteTool } from './deploy-website.js';
11
12
  import { defaultToolDescriptions } from '../config/default-config.js';
12
13
  import { desc } from '../config/param-descriptions.js';
13
14
  import dedent from 'dedent';
@@ -67,7 +68,7 @@ export const addDocsTools = (server, allowedTools = [
67
68
  'CLI',
68
69
  'OVERVIEW'
69
70
  ], options = {}) => {
70
- const { getToKnowWixEnabled = false, createWixBusinessGuideEnabled = true, disableTools, toolDescriptions, readmeDocs, authStrategy, skillsPortalUrl, skillsRepo, repo, skillsPr, commitHash, skillsTags, dynamicContextEnabled, cliEnabled = false, usePlatformizedSearch = true, usePlatformizedBrowse = true } = options;
71
+ const { getToKnowWixEnabled = false, createWixBusinessGuideEnabled = true, deployWebsiteEnabled = true, disableTools, toolDescriptions, readmeDocs, authStrategy, skillsPortalUrl, skillsRepo, repo, skillsPr, commitHash, skillsTags, dynamicContextEnabled, cliEnabled = false, usePlatformizedSearch = true, usePlatformizedBrowse = true } = options;
71
72
  const effectiveRepo = repo ?? skillsRepo;
72
73
  const effectiveCommitHash = commitHash ?? skillsPr;
73
74
  // Helper to get description (custom or default)
@@ -123,6 +124,15 @@ export const addDocsTools = (server, allowedTools = [
123
124
  !disableTools?.includes('CreateWixBusinessGuide')) {
124
125
  addCreateWixBusinessGuideTools(server, disableTools || [], toolDescriptions, cliEnabled);
125
126
  }
127
+ // On by default: it is a routing slot, not a docs source — the agent with
128
+ // ready-made site files must find it next to the site-creation tools.
129
+ if (deployWebsiteEnabled) {
130
+ addDeployWebsiteTool(server, disableTools || [], toolDescriptions, {
131
+ skillsPortalUrl,
132
+ repo: effectiveRepo,
133
+ commitHash: effectiveCommitHash
134
+ });
135
+ }
126
136
  // WDS Documentation
127
137
  if (allowedTools.includes('WDS') &&
128
138
  !disableTools?.includes('SearchWixWDSDocumentation')) {
@@ -622,17 +632,20 @@ export const addDocsTools = (server, allowedTools = [
622
632
  articleUrl: z.string().url().describe(d.articleUrl)
623
633
  };
624
634
  })(), { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async ({ articleUrl }, { httpClient }) => {
625
- // URL validation: only allow known docs prefixes
635
+ // URL validation: known docs prefixes, or a Wix skills registry page
636
+ // (https://www.wix.com/skills/..., https://dev.wix.com/docs/skills/...).
626
637
  const validPrefixes = ['https://dev.wix.com/docs/'];
627
638
  if (skillsPortalUrl)
628
639
  validPrefixes.push(skillsPortalUrl);
629
- if (!validPrefixes.some((p) => articleUrl.startsWith(p))) {
640
+ const isSkillsPage = isSkillsRegistryUrl(articleUrl);
641
+ if (!isSkillsPage &&
642
+ !validPrefixes.some((p) => articleUrl.startsWith(p))) {
630
643
  return {
631
644
  isError: true,
632
645
  content: [
633
646
  {
634
647
  type: 'text',
635
- text: `Invalid article URL: must start with https://dev.wix.com/docs/. Got: "${articleUrl}"`
648
+ text: `Invalid article URL: must start with https://dev.wix.com/docs/, or be a Wix skills page (https://www.wix.com/skills/...). Got: "${articleUrl}"`
636
649
  }
637
650
  ]
638
651
  };
@@ -640,8 +653,9 @@ export const addDocsTools = (server, allowedTools = [
640
653
  // Every docs page has a .md twin whose menu pages link children WITH
641
654
  // the .md suffix, so agents that navigated the .md tree pass .md URLs
642
655
  // here — which 404 in get-article-content. Read the canonical page.
643
- // (dev.wix.com only: on the skills portal .md is the real path.)
644
- if (articleUrl.startsWith('https://dev.wix.com/docs/')) {
656
+ // (dev.wix.com docs only: on the skills registry .md is the real path.)
657
+ if (!isSkillsPage &&
658
+ articleUrl.startsWith('https://dev.wix.com/docs/')) {
645
659
  articleUrl = articleUrl.replace(/\.md$/, '');
646
660
  }
647
661
  try {
@@ -35,5 +35,6 @@ export declare function fetchRecipesIndex(): Promise<string>;
35
35
  * ```
36
36
  */
37
37
  export declare function fetchArticle(articleUrl: string): Promise<string>;
38
+ export declare function isSkillsRegistryUrl(url: string): boolean;
38
39
  export declare function fetchArticleContent(httpClient: HttpClient, articleUrl: string, mode: 'article' | 'methodSchema' | 'raw', stripHeader?: boolean, skillsPortalUrl?: string, repo?: string, commitHash?: string, fetchMcpWhiteList?: boolean): Promise<string>;
39
40
  //# sourceMappingURL=fetch-article.d.ts.map
@@ -50,6 +50,38 @@ export async function fetchArticle(articleUrl) {
50
50
  // Internal implementation
51
51
  // ---------------------------------------------------------------------------
52
52
  const DOCS_URL_PREFIX = 'https://dev.wix.com/docs/';
53
+ /**
54
+ * The Wix skills registry serves agent skills (SKILL.md and bundled files)
55
+ * as raw markdown straight from the `wix/skills` repo, on every host the
56
+ * gateway maps to it. Pages there are not docs articles: the rawdocs
57
+ * get-article-content API does not know them, and `.md` is a real path, so
58
+ * they are fetched as-is.
59
+ */
60
+ const SKILLS_REGISTRY_URL = new RegExp('^https://(?:dev\\.wix\\.com(?:/docs)?|www\\.wix\\.com(?:/velo/reference)?)/skills(?:[/.?#]|$)');
61
+ export function isSkillsRegistryUrl(url) {
62
+ return SKILLS_REGISTRY_URL.test(url.trim());
63
+ }
64
+ async function fetchSkillsRegistryPage(httpClient, url) {
65
+ let data;
66
+ try {
67
+ ({ data } = await httpClient.get(url, {
68
+ headers: { accept: 'text/markdown, text/plain;q=0.9, */*;q=0.1' }
69
+ }));
70
+ }
71
+ catch (error) {
72
+ const { message, status } = getHttpErrorDetails(error);
73
+ const statusSuffix = status ? ` (status ${status})` : '';
74
+ throw new Error(`${message}${statusSuffix}`);
75
+ }
76
+ if (typeof data !== 'string') {
77
+ // The manifest endpoint is JSON; anything else is text.
78
+ return JSON.stringify(data, null, 2);
79
+ }
80
+ if (!data.trim()) {
81
+ throw new Error('Article content is empty');
82
+ }
83
+ return data;
84
+ }
53
85
  function getResponseErrorMessage(data) {
54
86
  if (!data) {
55
87
  return undefined;
@@ -152,10 +184,16 @@ export async function fetchArticleContent(httpClient, articleUrl, mode, stripHea
152
184
  }
153
185
  catch (error) {
154
186
  console.warn(`[fetch] Override failed for ${effectiveUrl}, falling back to ${articleUrl}:`, error instanceof Error ? error.message : error);
155
- // Fall through to normal fetch
156
- content = await fetchArticleContentFromDigor(httpClient, articleUrl, mode, stripHeader);
187
+ // Fall through to the normal fetch for this URL: the registry for a
188
+ // skills page (rawdocs cannot serve those), rawdocs otherwise.
189
+ content = isSkillsRegistryUrl(articleUrl)
190
+ ? await fetchSkillsRegistryPage(httpClient, articleUrl)
191
+ : await fetchArticleContentFromDigor(httpClient, articleUrl, mode, stripHeader);
157
192
  }
158
193
  }
194
+ else if (isSkillsRegistryUrl(articleUrl)) {
195
+ content = await fetchSkillsRegistryPage(httpClient, articleUrl);
196
+ }
159
197
  else if (mode === 'raw') {
160
198
  let parsedUrl;
161
199
  try {
@@ -86,11 +86,11 @@ export function addImportClaudeDesignTool(server, authStrategy, options) {
86
86
  designUrl = new URL(args.url);
87
87
  }
88
88
  catch {
89
- return errorResult("Invalid design URL — 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 — 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.");
89
+ return errorResult("Invalid design URL — this tool only accepts the claudeusercontent.com URL produced by Claude Design. For a page or site that exists in any other form (the user's files, a build, a zip, another builder's export, a page you made), do not retry this tool — call DeployWebsite, which returns the recipe to publish the files yourself. If that tool isn't available: read https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md and publish them yourself, or send the user to https://www.wix.com/headless/drop?utm_campaign=mcp.");
90
90
  }
91
91
  if (designUrl.protocol !== 'https:' ||
92
92
  !designUrl.hostname.endsWith(CLAUDE_USER_CONTENT_HOST_SUFFIX)) {
93
- return errorResult("Invalid design URL — 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 — 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.");
93
+ return errorResult("Invalid design URL — this tool only accepts the claudeusercontent.com URL produced by Claude Design. For a page or site that exists in any other form (the user's files, a build, a zip, another builder's export, a page you made), do not retry this tool — call DeployWebsite, which returns the recipe to publish the files yourself. If that tool isn't available: read https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md and publish them yourself, or send the user to https://www.wix.com/headless/drop?utm_campaign=mcp.");
94
94
  }
95
95
  // Resolve the caller's account identity BEFORE the design fetch — the
96
96
  // design URL is single-use, so an unauthenticated caller must fail
@@ -127,6 +127,8 @@ export interface ReadmeDocConfig {
127
127
  export interface DocsToolsOptions {
128
128
  getToKnowWixEnabled?: boolean;
129
129
  createWixBusinessGuideEnabled?: boolean;
130
+ /** DeployWebsite (publish ready-made site files) — an account-owner flow; off for visitor servers. */
131
+ deployWebsiteEnabled?: boolean;
130
132
  disableTools?: string[];
131
133
  /** Custom descriptions for tools, keyed by tool name */
132
134
  toolDescriptions?: Record<string, string>;
@@ -127,6 +127,8 @@ export interface ReadmeDocConfig {
127
127
  export interface DocsToolsOptions {
128
128
  getToKnowWixEnabled?: boolean;
129
129
  createWixBusinessGuideEnabled?: boolean;
130
+ /** DeployWebsite (publish ready-made site files) — an account-owner flow; off for visitor servers. */
131
+ deployWebsiteEnabled?: boolean;
130
132
  disableTools?: string[];
131
133
  /** Custom descriptions for tools, keyed by tool name */
132
134
  toolDescriptions?: Record<string, string>;