@wix/mcp 1.0.93 → 1.0.94

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.
Files changed (35) hide show
  1. package/build/bin-standalone.js +143 -193
  2. package/build/cjs/index.cjs +503 -196
  3. package/build/cjs/toolkit.cjs +471 -189
  4. package/build/dts/api-call/index.js +6 -3
  5. package/build/dts/api-call/site-files.d.ts +28 -0
  6. package/build/dts/api-call/site-files.js +194 -0
  7. package/build/dts/api-call/upload-headless-website-files.d.ts +35 -0
  8. package/build/dts/api-call/upload-headless-website-files.js +171 -0
  9. package/build/dts/bin.js +3 -6
  10. package/build/dts/code-mode/attachments.js +1 -1
  11. package/build/dts/code-mode/index.js +18 -4
  12. package/build/dts/config/default-config.d.ts +0 -4
  13. package/build/dts/config/default-config.js +36 -51
  14. package/build/dts/config/param-descriptions.js +2 -2
  15. package/build/dts/docs/docs.d.ts +4 -2
  16. package/build/dts/docs/docs.js +11 -7
  17. package/build/dts/docs/fetch-article.js +4 -1
  18. package/build/dts/docs/get-to-know.js +1 -1
  19. package/build/dts/docs/start-headless-build.d.ts +19 -0
  20. package/build/dts/docs/start-headless-build.js +74 -0
  21. package/build/dts/graphql/index.js +3 -1
  22. package/build/dts/import-claude-design-tool/index.js +2 -2
  23. package/build/dts/index.d.ts +1 -0
  24. package/build/dts/index.js +1 -0
  25. package/build/dts/site-widget-tools/search-site-templates-tool/index.js +17 -7
  26. package/build/dts/toolkit-entry.d.cts +4 -2
  27. package/build/dts/toolkit-entry.d.ts +4 -2
  28. package/build/dts/toolkit.js +9 -0
  29. package/build/dts/wix-mcp-server.d.ts +2 -0
  30. package/build/dts/wix-mcp-server.js +4 -0
  31. package/build/esm/index.js +501 -196
  32. package/build/esm/toolkit.js +471 -189
  33. package/package.json +2 -2
  34. package/build/dts/docs/create-site-guide.d.ts +0 -3
  35. package/build/dts/docs/create-site-guide.js +0 -109
@@ -11702,7 +11702,7 @@ async function fetchArticle(articleUrl) {
11702
11702
  }
11703
11703
  var DOCS_URL_PREFIX = "https://dev.wix.com/docs/";
11704
11704
  var SKILLS_REGISTRY_URL = new RegExp(
11705
- "^https://(?:dev\\.wix\\.com(?:/docs)?|www\\.wix\\.com(?:/velo/reference)?)/skills(?:[/.?#]|$)"
11705
+ "^https://(?:dev\\.wix\\.com(?:/docs)?|www\\.wix\\.com(?:/velo/reference)?)/skills(?:[/.?#]|$)|^https://www\\.wix\\.com/headless/"
11706
11706
  );
11707
11707
  function isSkillsRegistryUrl(url) {
11708
11708
  return SKILLS_REGISTRY_URL.test(url.trim());
@@ -12127,10 +12127,8 @@ function buildSystemReminderGuidelines(includeReadme, includeImageUploadGuidance
12127
12127
  if (includeReadme) {
12128
12128
  lines.push(
12129
12129
  "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.",
12130
- "**Exception \u2014 creating a new Wix site/website:** For a normal creation request \u2014 and only then \u2014 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.",
12131
- "If the user explicitly asks for a headless site, Wix Classic Editor site, or manual account/API-based site creation, call CreateWixBusinessGuide right away \u2014 these don't need the site's purpose first.",
12132
- "If the user \u2014 or you, earlier in this conversation \u2014 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 StartDeployWebsite right away \u2014 not a site-creation tool, not the docs.",
12133
- "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."
12130
+ "**Creating a new Wix site \u2014 and only then \u2014 skip WixREADME and the docs.** First settle one thing: will the user keep working on this site with you, in code, or design it themselves in Wix's editor? In code \u2014 you or the user have code, the two of you designed it here, they asked you to build it, they asked for a headless site, or it is an app: StartDeployWebsite if the code exists in any form, StartHeadlessBuild if not, called right away (it only returns the guide; the site's purpose is not needed first). In Wix's editor: the site-creation tools; their descriptions carry the rules. Everything else starts with WixREADME as usual.",
12131
+ "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 site creation through ManageWixSite, CallWixSiteAPI, or ExecuteWixAPI."
12134
12132
  );
12135
12133
  }
12136
12134
  lines.push(
@@ -12178,9 +12176,8 @@ function buildSystemReminder(includeReadme, includeImageUploadGuidance = true) {
12178
12176
  );
12179
12177
  const flows = buildSystemReminderFlows(includeReadme);
12180
12178
  return dedent_default`
12181
- <agent-mandatory-instructions>
12182
- YOU MUST READ AND FOLLOW THE AGENT-MANDATORY-INSTRUCTIONS BELOW
12183
- A FAILURE TO DO SO WILL RESULT IN ERRORS AND CRITICAL ISSUES.
12179
+ <wix-usage-guide>
12180
+ How to work with Wix through these tools. Following these steps avoids failed or incorrect API calls.
12184
12181
  <goal>
12185
12182
  ${AGENT_GOAL}
12186
12183
  </goal>
@@ -12190,7 +12187,7 @@ function buildSystemReminder(includeReadme, includeImageUploadGuidance = true) {
12190
12187
  <flow-description>
12191
12188
  ${flows}
12192
12189
  </flow-description>
12193
- </agent-mandatory-instructions>
12190
+ </wix-usage-guide>
12194
12191
  `;
12195
12192
  }
12196
12193
  var SYSTEM_REMINDER = buildSystemReminder(true);
@@ -12202,18 +12199,14 @@ var SITE_CREATION_COPY = {
12202
12199
  unstatedMethod: {
12203
12200
  nonWidget: "you MUST ask whether they want to build with AI or start from a template, and create nothing and show nothing until they answer",
12204
12201
  widget: "you MUST call `WixSiteCreation` and let them choose"
12205
- },
12206
- normalCreationRoute: {
12207
- nonWidget: "route those through `WixSiteBuilder` or `SearchSiteTemplates`",
12208
- widget: "route those through `WixSiteCreation`"
12209
12202
  }
12210
12203
  };
12211
12204
  var SITE_GUIDANCE = dedent_default`
12212
- First decide which of two different things is being asked for:
12213
- - The site is code the user or you control — hand-written HTML/CSS/JS, a static build, a zip, another AI builder's export, or a page or design you made earlier in this conversation — and it should go live on Wix. This is also the route whenever the user wants to keep working on the site here, with you: a Wix Headless site is a regular coded site, so you keep full control of the design and can change and redeploy it from this conversation at any time, and Wix business capabilities can be added to it as a backend when they are needed. Call StartDeployWebsite: it returns the recipe to host those exact files as a Wix Headless site (updates reuse the same site and URL; the drop page is only its fallback). Do not offer AI or templates for this and do not call any site-creation tool.
12214
- - Wix should create the site — generated with AI from a description, or started from a template — and the user will design and edit it in Wix rather than through you. That is what the site-creation tools do; they never take files you already have.
12205
+ First decide who keeps building the site:
12206
+ - **You, in code**: a Wix Headless site, with full control of the design, changed and redeployed from this conversation, Wix business features added as a backend. This is the case when you or the user have code (hand-written files, a static build, a zip, another builder's export, a page you made here): call StartDeployWebsite. It is also the case when nothing is coded yet but the user asked you to build it or asked for a headless site, the two of you worked out its design in this conversation, or it is an app (a game, a dashboard, a tool): call StartHeadlessBuild right away, before asking what the site is for. Do not offer AI or templates for either.
12207
+ - **The user, in Wix's editor**: Wix generates it with AI or starts it from a template, and the user designs it in Wix. That is what the site-creation tools do; they never take files you already have.
12215
12208
 
12216
- For a new site there are 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.
12209
+ For a new site in Wix's editor there are 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.
12217
12210
 
12218
12211
  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.
12219
12212
  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.
@@ -12233,6 +12226,7 @@ var toWidget = (text) => Object.values(SITE_CREATION_COPY).reduce(
12233
12226
  );
12234
12227
  var EXECUTE_WIX_API_INTRO = dedent_default`
12235
12228
  Execute JavaScript code against the Wix REST API when code is useful for chaining related calls, pagination, loops, branching, or in-memory transformation.
12229
+ Putting a site live or changing a headless site's files starts with StartDeployWebsite; building a new site in code with StartHeadlessBuild.
12236
12230
 
12237
12231
  CODE CONTRACT:
12238
12232
  - Pass an async function expression: \`async function() { ... }\` or \`async () => { ... }\`. Put every declaration, \`await\`, and \`return\` inside it, and do not invoke it yourself.
@@ -12317,7 +12311,7 @@ var ACCOUNT_SITE_EXECUTE_SCOPE = dedent_default`
12317
12311
  declare const files: Array<{ path: string; content: string; encoding?: "base64" }>;
12318
12312
  \`\`\`
12319
12313
 
12320
- FILE UPLOADS: files never go inline in \`code\`; two params feed the \`files\` global. \`attachments\` takes files attached in the chat or created in your file sandbox, of any type and size up to 10 MB per call (a zip of a whole site, HTML, images, PDFs): pass the files themselves, all in one call, and the platform resolves each into \`{ download_url, file_id, file_name?, mime_type? }\` — the bytes are fetched for you and arrive as base64 entries named by file name. The \`files\` param takes content you have as text here: 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):
12314
+ FILE UPLOADS: files never go inline in \`code\`; two params feed the \`files\` global. \`attachments\` works in ChatGPT only: pass files attached in the chat or created in your sandbox (any type, up to 10 MB per call: a zip of a whole site, HTML, images, PDFs) as you have them, by file id or /mnt/data path, all in one call. ChatGPT swaps each for \`{ download_url, file_id, file_name?, mime_type? }\` before the call reaches Wix, and the bytes arrive as base64 entries named by file name. Other clients send the value unchanged and the call fails before running; there, leave \`attachments\` out, or upload from your shell if it reaches the internet. The \`files\` param takes content you have as text here: 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):
12321
12315
  \`\`\`javascript
12322
12316
  // files param:
12323
12317
  // === FILE: index.html ===
@@ -12478,7 +12472,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
12478
12472
  ReadFullDocsArticle: dedent_default`
12479
12473
  Fetches the full Wix docs article or method article with code examples for using the method.
12480
12474
  Docs articles looks like this: https://dev.wix.com/docs/... and they can either be general docs articles or method articles.
12481
- 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.
12475
+ 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) — and Wix Headless agent pages under https://www.wix.com/headless/ (e.g. https://www.wix.com/headless/skill.md), exactly as served.
12482
12476
  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.
12483
12477
  For REST docs, use the URL as-is. For SDK docs, the URL SHOULD include ?apiView=SDK.
12484
12478
  ${reminder}
@@ -12526,7 +12520,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
12526
12520
  ${reminder}
12527
12521
  `,
12528
12522
  ManageWixSite: dedent_default`
12529
- Use account level API in order to create a site and update a site.
12523
+ Use account level API in order to update a site.
12530
12524
  Account-level only: this tool acts on the account itself, or on sites as objects within it, and sends no site context. Anything that reads or writes inside a site is site-level and does not belong here — use a site-level tool with that site's \`siteId\`.
12531
12525
  For POST/PATCH/PUT requests, pass the request body as a JSON object or array in the "body" parameter with all the required fields and values as described in the API schema, code examples, or docs you retrieved (e.g. body: {"name": "value", "nested": {"key": "value"}} or body: [{"key": "value"}]).
12532
12526
  The API endpoint url param MUST ALWAYS be taken from the conversation context.
@@ -12605,14 +12599,15 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
12605
12599
 
12606
12600
  Do not call this if the user explicitly asks to build with AI; call \`WixSiteBuilder\` instead.
12607
12601
  Do not call this if the user asks for templates or Wix Studio; call \`SearchSiteTemplates\` instead.
12602
+ Not for a site the user wants you to build in code, a design you worked out together, or an app: call \`StartHeadlessBuild\`.
12608
12603
 
12609
12604
  When calling this tool, infer \`customActions\` from the user's request so the reply can be tailored — 2-3 concrete, industry-specific setup actions for the site (see the input's guidance).
12610
12605
 
12611
12606
  After calling, you MUST speak the message provided in the tool result to the user as your reply — do not stay silent and do not replace it with a one-line pointer to the widget. Write out ALL of it, word for word: its opening line (the "Ready to set up your … site?" question INCLUDING "Choose your path in the widget above:"), BOTH of its option bullets, and its "Next up:" line, each on its own line and in that order. Do not summarize, shorten, drop any line, invent your own opener, rename the options, or add an intro or outro.
12612
12607
  `,
12613
12608
  "import-claude-design-from-url": dedent_default`
12614
- 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.
12615
- 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 StartDeployWebsite instead; it returns the recipe to publish the files yourself.
12609
+ Import a design created in Claude's design tool into Wix. Only for the signed \`claudeusercontent.com\` bundle URL Claude Design's "Send to Wix" produces (a self-contained HTML bundle with all images, fonts, and styles inlined). Creates a live Wix-hosted site and returns its URL.
12610
+ NOT for claude.ai share or artifact links, GitHub, Drive, chatgpt.com or any other URL, and not for files: those go to StartDeployWebsite, which returns the recipe to publish the files yourself.
12616
12611
  `,
12617
12612
  RunWixCliCommand: dedent_default`
12618
12613
  Executes Wix CLI commands for Wix app development and management.
@@ -13049,23 +13044,23 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
13049
13044
  Call this ONLY when the user's own words name AI (for example "create a site using AI" or "build it with AI") and the site intent is clear. A description of what the site should do — reservations, online ordering, a store, bookings, menus, a portfolio, and the like — is NOT a request for AI and NOT enough to call this tool; never infer AI from features or industry.
13050
13045
  Never default to this tool: if the user hasn't explicitly named AI, don't call this tool — ${SITE_CREATION_COPY.unstatedMethod.nonWidget}.
13051
13046
 
13052
- For templates or Wix Studio, call \`SearchSiteTemplates\`; for a specific template by \`metaSiteId\` or \`templateId\`, call \`CreateSiteFromTemplate\`; for a headless or classic Wix Editor site, or manual/account-API creation, call \`CreateWixBusinessGuide\`.
13047
+ For templates or Wix Studio, call \`SearchSiteTemplates\` (\`builder: classic\` for the Classic Editor); for a template id, \`CreateSiteFromTemplate\`. A site the user wants you to build in code, or an app: \`StartHeadlessBuild\`.
13053
13048
 
13054
13049
  Do NOT suggest HTML code, prompt templates, or alternative approaches instead of building — actually build the site through this tool. (HTML the user already has is the StartDeployWebsite case, not a reason to build.)
13055
13050
  `,
13056
13051
  SearchSiteTemplates: dedent_default`
13057
13052
  ${SITE_GUIDANCE}
13058
13053
 
13059
- Searches Wix templates and shows the template gallery, from the Harmony or the Studio template source.
13054
+ Searches Wix templates and shows the template gallery, from the Harmony, the Studio or the Classic Editor template source.
13060
13055
 
13061
- Call this ONLY when the user's own words ask for a template or for Wix Studio (for example "start from a template", "show me some templates", "build it in Wix Studio") and you can already tell what the site is for. If they mention Wix Studio, search Studio templates; otherwise search Harmony templates.
13056
+ Call this ONLY when the user's own words ask for a template, for Wix Studio or for the Classic Editor (for example "start from a template", "show me some templates", "build it in Wix Studio", "the old Wix editor") and you can already tell what the site is for. If they mention Wix Studio, search Studio templates; if they ask for the Classic Editor (the Wix Editor), search \`classic\`; otherwise search Harmony templates.
13062
13057
  ${SITE_CREATION_COPY.templatePick.nonWidget}
13063
13058
 
13064
13059
  A description of what the site should do — bookings, a store, a menu, a portfolio, and the like — is NOT a request for templates or for Wix Studio; never infer either from features or industry. Templates are not the default: if the site intent is clear but no method is named, do not guess — ${SITE_CREATION_COPY.unstatedMethod.nonWidget}.`,
13065
13060
  CreateSiteFromTemplate: dedent_default`
13066
13061
  Creates a Wix site from a specific template (by \`metaSiteId\` or \`templateId\`), publishes it when possible, and shows a live preview with links to the editor and the published site.
13067
13062
 
13068
- Call this when the user provides a specific template by \`metaSiteId\` or \`templateId\`. Use this only when the user gives a valid \`metaSiteId\` or \`templateId\` directly. To browse or choose a template first, call \`SearchSiteTemplates\`.
13063
+ Call this when the user provides a specific template by \`metaSiteId\` or \`templateId\`. Use this only when the user gives a valid \`metaSiteId\` or \`templateId\` directly. To browse or choose a template first, call \`SearchSiteTemplates\` (\`builder: classic\` for a Classic Editor site).
13069
13064
 
13070
13065
  `,
13071
13066
  SearchWixGraphQLOperations: dedent_default`
@@ -13093,34 +13088,24 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
13093
13088
  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.
13094
13089
  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.
13095
13090
  Set \`hasMutations: true\` for any document containing a mutation root field.
13091
+ `,
13092
+ UploadHeadlessWebsiteFiles: dedent_default`
13093
+ The upload step of the StartDeployWebsite and StartHeadlessBuild guides, and nothing else: puts a finished static website's files — index.html plus its CSS/JS/images, a static build, or a zip of the site — onto an existing Wix Headless site by its metaSiteId. Call it ONLY after one of them has returned its guide in this conversation and the site exists: for a new site, first run the recipe's provision call and pass the metaSiteId it returns; for a site deployed earlier, pass its metaSiteId. It does not create sites. Every upload replaces the whole file set and the URL stays. Returns the live URL and the dashboard link, and points back to the recipe for what comes next.
13094
+ Pass the files themselves: in ChatGPT as \`attachments\` (any type, a zip is unpacked), elsewhere as the \`files\` text bundle (\`=== FILE: <path> ===\` sections, \`base64\` tag for small binaries). The site must contain index.html at its root.
13095
+ NOT a site-creation tool: a request to create, build, design or generate a site — with or without a description — never comes here (those are the site-creation tools, after their own questions). Also NOT for media uploads to an existing site (UploadImageToWixSite) or any other API call (ExecuteWixAPI).
13096
13096
  `,
13097
13097
  StartDeployWebsite: dedent_default`
13098
- 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 — or an already-deployed Wix Headless site whose files should be replaced. The site can be 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, update my site, or upload my site / index.html.
13098
+ 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 — or an already-deployed Wix Headless site whose files should be replaced. The site can be 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, put online, make it live, get a URL, upload my site / index.html, or wants to update or redeploy the files of a site you or the user deployed as code.
13099
13099
  Updates keep the same site and URL. Also for implementing a backend on such a site 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.
13100
13100
  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).
13101
- Returns the full recipe; follow it and run the flow yourself. The drop page is only the fallback when you can't.
13102
- 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).
13101
+ Returns the full recipe; follow it and run the flow yourself. When the UploadHeadlessWebsiteFiles tool is available, use it for the recipe's upload step — after the site exists (the recipe's provision call, or a metaSiteId from an earlier deploy) — instead of building the multipart body in code; never before this tool. The drop page is only the fallback when you can't.
13102
+ NOT for: a screenshot or reference of a site the user wants Wix to generate (the site-creation tools); a site described in words with no code yet (StartHeadlessBuild); a Wix editor site's content or settings (WixSiteBuilder's edit mode, or WixREADME); a Claude Design bundle 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).
13103
13103
  `,
13104
- CreateWixBusinessGuide: dedent_default`
13105
- Provides comprehensive documentation for creating a new Wix business (site, app, etc.).
13106
- 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\`.
13107
- 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 StartDeployWebsite, 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.
13108
-
13109
- ## What This Tool Contains
13110
-
13111
- This tool provides complete documentation including:
13112
- - Template selection (empty sites or designed templates)
13113
- - Wix Editor vs Wix Studio options
13114
- - Regular vs Headless site creation (CLI-based; returns setup instructions)
13115
- - API endpoint details and parameters
13116
- - Post-creation steps (OAuth for headless, publishing for regular sites)
13117
- - App installation instructions
13118
-
13119
- ## After Reading This Documentation
13120
-
13121
- **After reading this documentation:**
13122
- 1. Use ManageWixSite for account-level site creation API calls
13123
- 2. Use CallWixSiteAPI / ExecuteWixAPI for site-level follow-up API calls (like install apps if needed)`
13104
+ StartHeadlessBuild: dedent_default`
13105
+ The user wants a site or web app that you build and keep changing in code, here with them, and no code exists yet. Wix runs its business side (store, bookings, CMS, forms, members, payments) as a Wix Headless site. Call this when the user asks you to build it yourself rather than Wix's AI or a template; when the two of you worked out a design in this conversation and nothing is coded yet; when it is an app (a game, a dashboard, a tool); when the user asks for a headless site; when a prompt links \`wix.com/headless/skill.md\` or a project's \`download.zip\`; or to move a Wix site to code.
13106
+ Call it right away: it only returns the guide, and the site's purpose is not needed first (pass "not stated yet" for what you don't know). Returns the full guide; follow it and do the work yourself, choosing its route by what you can reach (a shell that reaches Wix, or this MCP alone).
13107
+ NOT for: files, a build, a zip or a page that already exist (StartDeployWebsite); a site the user will design in Wix's editor (the site-creation tools); a Claude Design URL (import-claude-design-from-url).
13108
+ `
13124
13109
  };
13125
13110
  }
13126
13111
  var defaultToolDescriptions = buildToolDescriptions(true);
@@ -13132,9 +13117,9 @@ var recipesIndexDoc = {
13132
13117
  action: "include"
13133
13118
  };
13134
13119
  var README_INTRO = dedent_default`
13135
- <agent-mandatory-instructions>
13120
+ <wix-usage-guide>
13136
13121
 
13137
- YOU MUST READ AND FOLLOW THE AGENT-MANDATORY-INSTRUCTIONS BELOW:
13122
+ How to work with Wix through these tools:
13138
13123
 
13139
13124
  <source-of-truth>
13140
13125
  Do not answer from memory about anything Wix-specific — this covers every part of your answer, not only API calls: any URL, link, ID, setting location, or product fact you give the user must come from the docs tools, the recipes below, a previous tool result in this conversation, or the user's own message (identifiers and URLs the user supplies are fine to use as given). If a recipe in the index below matches what the user asked for, read it before answering. For APIs, use the search, browse, read-docs, and schema tools as the source of truth for the exact URL, HTTP method, request body shape, response shape, field names, required fields, and enum values before calling an API.
@@ -13186,7 +13171,7 @@ var README_BODY = dedent_default`
13186
13171
  <other-uses>
13187
13172
  Beyond managing user sites, the Wix MCP tools can be used for any other purpose they support — for example, reading Wix SDK docs to write code against the SDK, using the Wix CLI to create apps, answering documentation questions, and so on.
13188
13173
  </other-uses>
13189
- </agent-mandatory-instructions>
13174
+ </wix-usage-guide>
13190
13175
  `;
13191
13176
  var defaultReadmeDocs = [
13192
13177
  {
@@ -13242,8 +13227,7 @@ var widgetSiteCreationConfig = {
13242
13227
  "WixSiteBuilder",
13243
13228
  "WixSiteCreation",
13244
13229
  "SearchSiteTemplates",
13245
- "CreateSiteFromTemplate",
13246
- "CreateWixBusinessGuide"
13230
+ "CreateSiteFromTemplate"
13247
13231
  ].map((tool) => [tool, toWidget(defaultToolDescriptions[tool])])
13248
13232
  )
13249
13233
  };
@@ -13832,7 +13816,7 @@ var paramDescriptions = {
13832
13816
  reason: "One sentence describing the original user request and the task you are trying to accomplish with this search."
13833
13817
  },
13834
13818
  ReadFullDocsArticle: {
13835
- articleUrl: "The URL of the docs article or method article to fetch. Should be something like https://dev.wix.com/docs/.../... 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). Wix skills pages (https://www.wix.com/skills/..., https://dev.wix.com/docs/skills/...) are read as-is, including their referenced files."
13819
+ articleUrl: "The URL of the docs article or method article to fetch. Should be something like https://dev.wix.com/docs/.../... 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). Wix skills pages (https://www.wix.com/skills/..., https://dev.wix.com/docs/skills/...) and Wix Headless agent pages (https://www.wix.com/headless/..., e.g. https://www.wix.com/headless/skill.md) are read as-is, including their referenced files."
13836
13820
  },
13837
13821
  ReadFullDocsMethodSchema: {
13838
13822
  articleUrl: "The URL of the documentation to fetch. Should be something like https://dev.wix.com/docs/.../... 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).",
@@ -13872,7 +13856,7 @@ var paramDescriptions = {
13872
13856
  operationName: "Operation to run when the document defines more than one named operation.",
13873
13857
  siteId: "The Wix site to run against. Required for site-level operations (anything reading or writing inside a site \u2014 products, orders, contacts, bookings, CMS, blog, members ...). Resolve it with GetSiteContext or ListWixSites; never pass a guessed value.",
13874
13858
  scope: '"site" (default when siteId is given) runs with a site-scoped credential; "account" runs account-level operations (sites, account settings) and ignores siteId.',
13875
- hasMutations: "Whether this document creates, updates, deletes or otherwise mutates Wix data. True for any mutation root field (in any operation of the document) even when the intent is inspection; false only for pure queries. This flag alone decides whether a transient server error may be retried, so never mark a document that writes as false.",
13859
+ hasMutations: "True if the document contains a mutation root field in any of its operations; false if it contains only queries. A transient server error is retried only when this is false.",
13876
13860
  reason: "One sentence explaining the original user request and why you are running this GraphQL operation to complete it."
13877
13861
  },
13878
13862
  RunWixCliCommand: {
@@ -14136,121 +14120,6 @@ ${skillsContent}`
14136
14120
  );
14137
14121
  }
14138
14122
 
14139
- // src/docs/create-site-guide.ts
14140
- function isHeadlessRequest(prompt) {
14141
- return !!prompt && /headless/i.test(prompt);
14142
- }
14143
- var HEADLESS_NON_CLI_INSTRUCTIONS = `# Creating a Wix Headless Site
14144
-
14145
- Wix Headless sites require the **Wix CLI** and cannot be created from the Wix dashboard or via REST API alone. Let the user know they need to set up a local development environment with the Wix CLI before proceeding.
14146
-
14147
- ## Prerequisites
14148
- - Node.js 18+
14149
- - A terminal / command-line environment
14150
-
14151
- ## Steps to Get Started
14152
-
14153
- 1. **Install and authenticate the Wix CLI:**
14154
- \`\`\`
14155
- npx @wix/cli login
14156
- \`\`\`
14157
- 2. **Follow the quick-start guide:** https://dev.wix.com/docs/go-headless
14158
- 3. **For a complete orchestrated setup** (commerce, CMS, bookings, identity), use the Wix Headless skill: https://www.wix-headless.dev/skill.md`;
14159
- var HEADLESS_CLI_INSTRUCTIONS = `# Creating a Wix Headless Site (CLI Flow)
14160
-
14161
- Follow these steps using the \`RunWixCliCommand\` tool to scaffold and configure a Wix Headless site.
14162
-
14163
- ## Before You Start
14164
- Ask the user:
14165
- 1. What is the brand/site name?
14166
- 2. Which local directory should the project be created in?
14167
-
14168
- ## Steps
14169
-
14170
- ### Step 1 \u2014 Authenticate (if not already done)
14171
- Run: \`npx @wix/cli login\`
14172
- Skip if the user is already authenticated.
14173
-
14174
- ### Step 2 \u2014 Scaffold the project
14175
- Use a Wix headless starter template. Ask the user which starter they prefer, or use the default Next.js starter. Follow the scaffolding instructions at: https://dev.wix.com/docs/go-headless
14176
-
14177
- ### Step 3 \u2014 Install dependencies
14178
- Run: \`npm install\` in the project directory.
14179
-
14180
- ### Step 4 \u2014 Pull environment variables
14181
- Run: \`npx @wix/cli env pull\`
14182
- This writes the \`.env.local\` file with the site's environment variables.
14183
-
14184
- ### Step 5 \u2014 Build and verify
14185
- Run: \`npx @wix/cli build\`
14186
-
14187
- ### Step 6 \u2014 Release (optional)
14188
- Run: \`npx @wix/cli release\` to deploy.
14189
-
14190
- ## Full Orchestration
14191
- For a complete multi-wave headless setup (commerce, CMS, bookings, design system, content seeding), refer to the Wix Headless skill: https://www.wix-headless.dev/skill.md
14192
-
14193
- Use \`RunWixCliCommand\` for each CLI step above.`;
14194
- var CREATE_SITE_DOCS = [
14195
- {
14196
- url: "https://dev.wix.com/docs/kb-only/MCP_REST_RECIPES_KB_ID/TRAIN_create_a_new_site_from_template",
14197
- title: "How to Create a New Wix Site",
14198
- nextSteps: "\n\n**Next Steps:** Use the ManageWixSite tool to make the API call for creating the Wix site."
14199
- },
14200
- {
14201
- url: "https://dev.wix.com/docs/kb-only/MCP_REST_RECIPES_KB_ID/TRAIN_wix.devcenter.apps.installer.v1.AppsInstallerService.InstallApp",
14202
- title: "How to Install App on a Wix Site",
14203
- nextSteps: "\n\n**Next Steps:** Use the CallWixSiteAPI tool to make the API call for installing apps on the Wix site."
14204
- }
14205
- ];
14206
- async function addCreateWixBusinessGuideTools(server, disableTools = [], toolDescriptions, cliEnabled = false) {
14207
- if (disableTools.includes("CreateWixBusinessGuide")) {
14208
- return;
14209
- }
14210
- server.tool(
14211
- "CreateWixBusinessGuide",
14212
- toolDescriptions?.["CreateWixBusinessGuide"] ?? defaultToolDescriptions.CreateWixBusinessGuide,
14213
- {
14214
- userPrompt: external_exports.string().optional().describe("The user prompt that triggered this tool")
14215
- },
14216
- { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
14217
- async (args, { httpClient }) => {
14218
- if (isHeadlessRequest(args.userPrompt)) {
14219
- const isCli = typeof cliEnabled === "function" ? cliEnabled() : cliEnabled;
14220
- const text = isCli ? HEADLESS_CLI_INSTRUCTIONS : HEADLESS_NON_CLI_INSTRUCTIONS;
14221
- return { content: [{ type: "text", text }] };
14222
- }
14223
- try {
14224
- const contents = await Promise.all(
14225
- CREATE_SITE_DOCS.map(async ({ url, title, nextSteps }) => {
14226
- const articleContent = await fetchArticleContent(
14227
- httpClient,
14228
- url,
14229
- "article",
14230
- true
14231
- );
14232
- const fullContent = `# ${title}
14233
-
14234
- ${articleContent}${nextSteps}`;
14235
- return { type: "text", text: fullContent };
14236
- })
14237
- );
14238
- return { content: contents };
14239
- } catch (error) {
14240
- return {
14241
- isError: true,
14242
- content: [
14243
- {
14244
- type: "text",
14245
- text: `Error fetching create site documentation: ${error.message}`
14246
- }
14247
- ]
14248
- };
14249
- }
14250
- }
14251
- );
14252
- }
14253
-
14254
14123
  // src/docs/start-deploy-website.ts
14255
14124
  var START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS = {
14256
14125
  userRequest: "The user's request that led here, quoted if short, otherwise summarized in a sentence.",
@@ -14322,6 +14191,87 @@ function addStartDeployWebsiteTool(server, disableTools = [], toolDescriptions,
14322
14191
  );
14323
14192
  }
14324
14193
 
14194
+ // src/docs/start-headless-build.ts
14195
+ var START_HEADLESS_BUILD_TOOL_NAME = "StartHeadlessBuild";
14196
+ var HEADLESS_GUIDE_URL = "https://www.wix.com/skills/headless-cold-start/headless-kit.md";
14197
+ var START_HEADLESS_BUILD_STARTING_POINTS = [
14198
+ "nothing yet",
14199
+ "a design worked out in this conversation",
14200
+ "an existing Wix Headless site",
14201
+ "a project download link",
14202
+ "a Wix site to move to code"
14203
+ ];
14204
+ var START_HEADLESS_BUILD_PARAM_DESCRIPTIONS = {
14205
+ userRequest: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.userRequest,
14206
+ siteDescription: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.siteDescription,
14207
+ startingPoint: "What the build starts from: nothing yet; a design worked out in this conversation; an existing Wix Headless site; a project download link (a download.zip from Wix); or a Wix site to move to code.",
14208
+ siteFunctionality: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.siteFunctionality,
14209
+ agentIdentity: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.agentIdentity,
14210
+ wixAccess: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.wixAccess
14211
+ };
14212
+ var PREFIX2 = "The following is Wix's headless guide. Follow it end to end and do the work yourself. If you find the user already has the site's code, call StartDeployWebsite instead.";
14213
+ var MOVE_TO_CODE_LINE = `Moving a Wix site to code starts from the site's dashboard (Headless settings, "Go Headless"), which hands the owner a prompt with the project's download link. Ask the user for that prompt, then follow the guide with it.`;
14214
+ function startHeadlessBuildResponse(guide, startingPoint) {
14215
+ const lines = [];
14216
+ if (startingPoint === "a Wix site to move to code") {
14217
+ lines.push(MOVE_TO_CODE_LINE);
14218
+ }
14219
+ if (guide && guide.trim()) {
14220
+ lines.push(PREFIX2, "---", guide.trim());
14221
+ } else {
14222
+ lines.push(
14223
+ `Wix's headless guide could not be loaded here. It is at ${HEADLESS_GUIDE_URL}: read it whole with ReadFullDocsArticle if you have that tool, otherwise fetch it with curl or your web tool, then follow it end to end and do the work yourself. If you find the user already has the site's code, call StartDeployWebsite instead.`
14224
+ );
14225
+ }
14226
+ return lines.join("\n\n");
14227
+ }
14228
+ function addStartHeadlessBuildTool(server, disableTools = [], toolDescriptions, fetchOptions = {}) {
14229
+ if (disableTools.includes(START_HEADLESS_BUILD_TOOL_NAME)) {
14230
+ return;
14231
+ }
14232
+ const d = START_HEADLESS_BUILD_PARAM_DESCRIPTIONS;
14233
+ server.tool(
14234
+ START_HEADLESS_BUILD_TOOL_NAME,
14235
+ toolDescriptions?.[START_HEADLESS_BUILD_TOOL_NAME] ?? defaultToolDescriptions[START_HEADLESS_BUILD_TOOL_NAME],
14236
+ {
14237
+ userRequest: external_exports.string().min(1).describe(d.userRequest),
14238
+ siteDescription: external_exports.string().min(1).describe(d.siteDescription),
14239
+ startingPoint: external_exports.enum(START_HEADLESS_BUILD_STARTING_POINTS).describe(d.startingPoint),
14240
+ siteFunctionality: external_exports.string().min(1).describe(d.siteFunctionality),
14241
+ agentIdentity: external_exports.string().min(1).describe(d.agentIdentity),
14242
+ wixAccess: external_exports.string().min(1).describe(d.wixAccess)
14243
+ },
14244
+ { readOnlyHint: true, destructiveHint: false, openWorldHint: false },
14245
+ async (args, { httpClient }) => {
14246
+ let guide;
14247
+ try {
14248
+ guide = await fetchArticleContent(
14249
+ httpClient,
14250
+ HEADLESS_GUIDE_URL,
14251
+ "article",
14252
+ true,
14253
+ fetchOptions.skillsPortalUrl,
14254
+ fetchOptions.repo,
14255
+ fetchOptions.commitHash
14256
+ );
14257
+ } catch (error) {
14258
+ console.warn(
14259
+ "[StartHeadlessBuild] guide fetch failed, returning the pointer:",
14260
+ error instanceof Error ? error.message : error
14261
+ );
14262
+ }
14263
+ return {
14264
+ content: [
14265
+ {
14266
+ type: "text",
14267
+ text: startHeadlessBuildResponse(guide, args.startingPoint)
14268
+ }
14269
+ ]
14270
+ };
14271
+ }
14272
+ );
14273
+ }
14274
+
14325
14275
  // src/docs/docs.ts
14326
14276
  var REST_SEARCH_API_GROUNDING_REMINDER = dedent_default`
14327
14277
  # Before calling a Wix API
@@ -14369,8 +14319,8 @@ var addDocsTools = (server, allowedTools = [
14369
14319
  ], options = {}) => {
14370
14320
  const {
14371
14321
  getToKnowWixEnabled = false,
14372
- createWixBusinessGuideEnabled = true,
14373
14322
  startDeployWebsiteEnabled = true,
14323
+ startHeadlessBuildEnabled = true,
14374
14324
  disableTools,
14375
14325
  toolDescriptions,
14376
14326
  readmeDocs,
@@ -14382,7 +14332,6 @@ var addDocsTools = (server, allowedTools = [
14382
14332
  commitHash,
14383
14333
  skillsTags,
14384
14334
  dynamicContextEnabled,
14385
- cliEnabled = false,
14386
14335
  usePlatformizedSearch = true,
14387
14336
  usePlatformizedBrowse = true
14388
14337
  } = options;
@@ -14428,14 +14377,6 @@ var addDocsTools = (server, allowedTools = [
14428
14377
  skillsTags
14429
14378
  });
14430
14379
  }
14431
- if (createWixBusinessGuideEnabled && !disableTools?.includes("CreateWixBusinessGuide")) {
14432
- addCreateWixBusinessGuideTools(
14433
- server,
14434
- disableTools || [],
14435
- toolDescriptions,
14436
- cliEnabled
14437
- );
14438
- }
14439
14380
  if (startDeployWebsiteEnabled) {
14440
14381
  addStartDeployWebsiteTool(server, disableTools || [], toolDescriptions, {
14441
14382
  skillsPortalUrl,
@@ -14443,6 +14384,13 @@ var addDocsTools = (server, allowedTools = [
14443
14384
  commitHash: effectiveCommitHash
14444
14385
  });
14445
14386
  }
14387
+ if (startHeadlessBuildEnabled) {
14388
+ addStartHeadlessBuildTool(server, disableTools || [], toolDescriptions, {
14389
+ skillsPortalUrl,
14390
+ repo: effectiveRepo,
14391
+ commitHash: effectiveCommitHash
14392
+ });
14393
+ }
14446
14394
  if (allowedTools.includes("WDS") && !disableTools?.includes("SearchWixWDSDocumentation")) {
14447
14395
  server.tool(
14448
14396
  "SearchWixWDSDocumentation",
@@ -14972,7 +14920,7 @@ var addDocsTools = (server, allowedTools = [
14972
14920
  content: [
14973
14921
  {
14974
14922
  type: "text",
14975
- 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}"`
14923
+ text: `Invalid article URL: must start with https://dev.wix.com/docs/, or be a Wix skills page (https://www.wix.com/skills/..., https://www.wix.com/headless/...). Got: "${articleUrl}"`
14976
14924
  }
14977
14925
  ]
14978
14926
  };
@@ -15694,10 +15642,12 @@ function addApiCallTool(server, strategy, allowedTools = ["CallWixSiteAPI", "Lis
15694
15642
  sourceDocUrl: external_exports.string().describe(d.sourceDocUrl)
15695
15643
  };
15696
15644
  })(),
15645
+ // openWorldHint true: a free-form URL into the whole Wix REST surface is an
15646
+ // open-ended target, even though auth is bounded to the user's account.
15697
15647
  getHints("CallWixSiteAPI", {
15698
15648
  readOnlyHint: false,
15699
15649
  destructiveHint: true,
15700
- openWorldHint: false
15650
+ openWorldHint: true
15701
15651
  }),
15702
15652
  async ({ url, body, method, siteId, reason, sourceDocUrl }, { httpClient, setToolBiParams }) => {
15703
15653
  logger.log(
@@ -15874,7 +15824,7 @@ function addApiCallTool(server, strategy, allowedTools = ["CallWixSiteAPI", "Lis
15874
15824
  getHints("ManageWixSite", {
15875
15825
  readOnlyHint: false,
15876
15826
  destructiveHint: true,
15877
- openWorldHint: false
15827
+ openWorldHint: true
15878
15828
  }),
15879
15829
  async ({ url, body, method, reason }, { httpClient, setToolBiParams }) => {
15880
15830
  logger.log(
@@ -16630,7 +16580,8 @@ function addExecuteGraphqlTool(server, description, authStrategy, options) {
16630
16580
  hasMutations: external_exports.boolean().describe(d.hasMutations),
16631
16581
  reason: external_exports.string().describe(d.reason)
16632
16582
  },
16633
- { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
16583
+ // openWorldHint true: any operation in the Wix GraphQL schema, chosen at call time.
16584
+ { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
16634
16585
  async ({ query, variables, operationName, siteId, scope, hasMutations, reason }, extra) => {
16635
16586
  const effectiveScope = scope ?? (siteId ? "site" : "account");
16636
16587
  logger.log(
@@ -16862,7 +16813,7 @@ async function resolveAttachments(attachments, fetchImpl = fetch) {
16862
16813
  throw new AttachmentError(
16863
16814
  `attachments[${i}] was not resolved to a download URL (got ${JSON.stringify(
16864
16815
  typeof raw === "string" ? raw : (a && a.download_url) ?? raw
16865
- )}). The platform did not hydrate this attachment; retry once, and if it still arrives like this put the file in the \`files\` bundle instead (\`=== FILE: <path> base64 ===\` for binary).`
16816
+ )}). \`attachments\` works only in ChatGPT, which swaps each file for a download URL before the call reaches Wix. Here it arrived as written, and Wix cannot read a path on your machine, so retrying the same value fails the same way. Upload another way. With a shell that reaches the internet, send the file from there (for a site, the StartDeployWebsite recipe's curl drop). Otherwise leave \`attachments\` out and put the files in \`files\`: text as is, and a binary as a \`=== FILE: <path> base64 ===\` entry only when it is a few KB (an icon, a logo), since a larger base64 payload does not survive your own output. A larger image goes in by public URL or through the user (the Media Manager).`
16866
16817
  );
16867
16818
  }
16868
16819
  const problem = checkAttachmentUrl(url);
@@ -16920,6 +16871,327 @@ function validateExecuteCode(code) {
16920
16871
  return { isValid: true };
16921
16872
  }
16922
16873
 
16874
+ // src/api-call/site-files.ts
16875
+ var MAX_FILES_BUNDLE_CHARS = 4e6;
16876
+ var MAX_UNZIP_BYTES = 64 * 1024 * 1024;
16877
+ var MAX_SITE_FILES = 2e3;
16878
+ var textEncoder = new TextEncoder();
16879
+ function base64ToBytes(b64) {
16880
+ const clean = b64.replace(/^data:[^,]*,/, "").replace(/\s+/g, "");
16881
+ return Uint8Array.from(atob(clean), (c) => c.charCodeAt(0));
16882
+ }
16883
+ function parseFileBundle(bundle) {
16884
+ if (!bundle || !bundle.trim()) return [];
16885
+ if (bundle.length > MAX_FILES_BUNDLE_CHARS) {
16886
+ throw new Error(
16887
+ `files bundle is too large (${bundle.length} chars; max ${MAX_FILES_BUNDLE_CHARS}). Keep it to text and small base64 assets; pass larger files through attachments or reference media by URL.`
16888
+ );
16889
+ }
16890
+ const delimiter = /^=== FILE: (.+?)(?: (base64))? ===[ \t]*$/gm;
16891
+ const matches = [...bundle.matchAll(delimiter)];
16892
+ if (matches.length === 0) {
16893
+ throw new Error(
16894
+ "files bundle has no `=== FILE: <path> ===` sections. Start each file with that line (add ` base64` before the closing === for binary content)."
16895
+ );
16896
+ }
16897
+ return matches.map((match, i) => {
16898
+ const path = match[1].trim();
16899
+ const start = (match.index ?? 0) + match[0].length;
16900
+ const end = i + 1 < matches.length ? matches[i + 1].index ?? bundle.length : bundle.length;
16901
+ const content = bundle.slice(start, end).replace(/^\r?\n/, "").replace(/\r?\n$/, "");
16902
+ return {
16903
+ path,
16904
+ bytes: match[2] === "base64" ? base64ToBytes(content) : textEncoder.encode(content)
16905
+ };
16906
+ });
16907
+ }
16908
+ async function unzip(z) {
16909
+ const u16 = (o) => z[o] | z[o + 1] << 8;
16910
+ const u32 = (o) => (u16(o) | u16(o + 2) << 16) >>> 0;
16911
+ let end = z.length - 22;
16912
+ while (end >= 0 && !(u32(end) === 101010256 && end + 22 + u16(end + 20) === z.length))
16913
+ end--;
16914
+ if (end < 0) throw new Error("not a zip file (no end-of-central-directory)");
16915
+ const out = [];
16916
+ let p = u32(end + 16);
16917
+ let total = 0;
16918
+ const decoder = new TextDecoder();
16919
+ for (let i = 0, n = u16(end + 10); i < n; i++) {
16920
+ const path = decoder.decode(z.slice(p + 46, p + 46 + u16(p + 28)));
16921
+ const method = u16(p + 10);
16922
+ const size = u32(p + 24);
16923
+ total += size;
16924
+ if (total > MAX_UNZIP_BYTES) {
16925
+ throw new Error(
16926
+ `zip expands past ${MAX_UNZIP_BYTES} bytes; a static site is at most 20 MB`
16927
+ );
16928
+ }
16929
+ const at = u32(p + 42);
16930
+ const start = at + 30 + u16(at + 26) + u16(at + 28);
16931
+ const comp = z.slice(start, start + u32(p + 20));
16932
+ p += 46 + u16(p + 28) + u16(p + 30) + u16(p + 32);
16933
+ if (path.endsWith("/")) continue;
16934
+ let bytes;
16935
+ if (method === 0) bytes = comp;
16936
+ else if (method === 8) {
16937
+ bytes = new Uint8Array(
16938
+ await new Response(
16939
+ new Blob([comp]).stream().pipeThrough(new DecompressionStream("deflate-raw"))
16940
+ ).arrayBuffer()
16941
+ );
16942
+ } else
16943
+ throw new Error(
16944
+ `zip entry '${path}' uses unsupported compression method ${method}`
16945
+ );
16946
+ if (bytes.length !== size) {
16947
+ throw new Error(
16948
+ `zip entry '${path}' declares ${size} bytes but holds ${bytes.length}`
16949
+ );
16950
+ }
16951
+ out.push({ path, bytes });
16952
+ }
16953
+ return out;
16954
+ }
16955
+ var isZip = (f) => f.bytes.length >= 4 && f.bytes[0] === 80 && f.bytes[1] === 75 && (f.bytes[2] === 3 && f.bytes[3] === 4 || f.bytes[2] === 5 && f.bytes[3] === 6) || /\.zip$/i.test(f.path);
16956
+ var isRootIndex = (p) => /^index\.html?$/i.test(p);
16957
+ async function normalizeSiteFiles(input) {
16958
+ let list = [];
16959
+ for (const f of input) {
16960
+ if (isZip(f)) list.push(...await unzip(f.bytes));
16961
+ else list.push(f);
16962
+ }
16963
+ list = list.map((f) => ({
16964
+ ...f,
16965
+ path: f.path.replace(/\\/g, "/").replace(/^\.?\//, "")
16966
+ })).filter(
16967
+ (f) => f.path && !f.path.startsWith("__MACOSX/") && !/(^|\/)\.DS_Store$/.test(f.path) && !f.path.split("/").includes("..")
16968
+ );
16969
+ if (list.length === 0) {
16970
+ throw new Error(
16971
+ "No files to upload: pass the site in `files` or `attachments`."
16972
+ );
16973
+ }
16974
+ if (list.length > MAX_SITE_FILES) {
16975
+ throw new Error(
16976
+ `Too many files (${list.length}); a site upload is at most ${MAX_SITE_FILES}.`
16977
+ );
16978
+ }
16979
+ if (!list.some((f) => isRootIndex(f.path))) {
16980
+ const first = list[0].path.split("/")[0];
16981
+ if (list.every((f) => f.path.includes("/") && f.path.split("/")[0] === first)) {
16982
+ list = list.map((f) => ({
16983
+ ...f,
16984
+ path: f.path.split("/").slice(1).join("/")
16985
+ }));
16986
+ }
16987
+ }
16988
+ const rootHtml = list.filter(
16989
+ (f) => !f.path.includes("/") && /\.html?$/i.test(f.path)
16990
+ );
16991
+ if (!list.some((f) => isRootIndex(f.path)) && rootHtml.length === 1) {
16992
+ list = list.map(
16993
+ (f) => f === rootHtml[0] ? { ...f, path: "index.html" } : f
16994
+ );
16995
+ }
16996
+ if (!list.some((f) => isRootIndex(f.path))) {
16997
+ const shown = list.slice(0, 8).map((f) => f.path).join(", ");
16998
+ throw new Error(
16999
+ `The site needs an index.html at its root. Files received: ${shown}${list.length > 8 ? ", \u2026" : ""}`
17000
+ );
17001
+ }
17002
+ return list;
17003
+ }
17004
+ function guessContentType(path) {
17005
+ const ext = (/\.([a-zA-Z0-9]+)$/.exec(path)?.[1] ?? "").toLowerCase();
17006
+ const map = {
17007
+ html: "text/html",
17008
+ htm: "text/html",
17009
+ css: "text/css",
17010
+ js: "text/javascript",
17011
+ mjs: "text/javascript",
17012
+ json: "application/json",
17013
+ svg: "image/svg+xml",
17014
+ txt: "text/plain",
17015
+ md: "text/markdown",
17016
+ xml: "application/xml",
17017
+ webmanifest: "application/manifest+json",
17018
+ ico: "image/x-icon",
17019
+ png: "image/png",
17020
+ jpg: "image/jpeg",
17021
+ jpeg: "image/jpeg",
17022
+ gif: "image/gif",
17023
+ webp: "image/webp",
17024
+ avif: "image/avif",
17025
+ woff: "font/woff",
17026
+ woff2: "font/woff2",
17027
+ ttf: "font/ttf",
17028
+ otf: "font/otf",
17029
+ pdf: "application/pdf",
17030
+ mp4: "video/mp4",
17031
+ webm: "video/webm",
17032
+ mp3: "audio/mpeg"
17033
+ };
17034
+ return map[ext] ?? "application/octet-stream";
17035
+ }
17036
+ function buildFormData(files) {
17037
+ const fd = new FormData();
17038
+ for (const f of files) {
17039
+ fd.append(
17040
+ "files",
17041
+ new Blob([f.bytes], { type: guessContentType(f.path) }),
17042
+ f.path
17043
+ );
17044
+ }
17045
+ return fd;
17046
+ }
17047
+
17048
+ // src/api-call/upload-headless-website-files.ts
17049
+ var UPLOAD_HEADLESS_WEBSITE_FILES_TOOL_NAME = "UploadHeadlessWebsiteFiles";
17050
+ var HEADLESS_BUSINESS = "https://www.wixapis.com/headless-business-setup/v1/headless-business";
17051
+ function dropAttribution(clientName) {
17052
+ const slug = (clientName ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
17053
+ return `?campaign=mcp&agent=${encodeURIComponent(slug || "wix-mcp")}`;
17054
+ }
17055
+ var UPLOAD_HEADLESS_WEBSITE_FILES_PARAM_DESCRIPTIONS = {
17056
+ files: "The site files as text, so file bytes never pass through a code string. Sections delimited by a line `=== FILE: <path> ===` (text, raw) or `=== FILE: <path> base64 ===` (binary, base64); `<path>` is relative to the site root and must include `index.html`. Use base64 for small binaries (icons, logos, fonts); large images/video belong behind a URL in the HTML, not in the bundle. Optional when `attachments` carries the files.",
17057
+ attachments: "ChatGPT only. Files attached in the chat or created in your sandbox \u2014 any type: a zip of the whole site, HTML, CSS, JS, images. Pass each file as you have it, its file id or its /mnt/data path, and ChatGPT swaps it for { download_url, file_id, file_name?, mime_type? } before the call reaches Wix. No other client does this swap, so outside ChatGPT a path or file:// URL here fails at once; leave this out and use `files`, or, with a shell that reaches the internet, drop the files with curl as the StartDeployWebsite recipe shows. A zip is unpacked; a top-level folder wrapping the site is stripped. Prefer this over base64 in `files` whenever you have a file handle. Up to 10 MB per call.",
17058
+ siteId: "The metaSiteId of the Wix Headless site to upload into: the one returned by the provision call of the StartDeployWebsite recipe for a new site, or of a site deployed earlier to replace its files. Every upload replaces the whole file set, so send all files; the URL stays the same.",
17059
+ calledStartDeployWebsite: "Whether you called StartDeployWebsite earlier in this conversation and are following the recipe it returned. Answer truthfully; it is recorded, not checked.",
17060
+ agentIdentity: START_DEPLOY_WEBSITE_PARAM_DESCRIPTIONS.agentIdentity,
17061
+ reason: "One sentence: the user request that led here and what is being published."
17062
+ };
17063
+ function formatDropResult(r) {
17064
+ const shown = r.files.slice(0, 60);
17065
+ return [
17066
+ "Uploaded; the site is live with this file set.",
17067
+ `Live URL: ${r.siteUrl ?? "(not returned)"}`,
17068
+ `metaSiteId: ${r.metaSiteId} \u2014 pass it as \`siteId\` to update this site later; every upload replaces the whole file set.`,
17069
+ `Dashboard: https://manage.wix.com/dashboard/${r.metaSiteId}`,
17070
+ `${r.files.length} file(s): ${shown.join(", ")}${r.files.length > shown.length ? ", \u2026" : ""}`,
17071
+ "",
17072
+ "Give the user the live URL and the dashboard link.",
17073
+ `The upload step is done. For what comes next \u2014 changing the site later, connecting a domain, or adding a Wix backend (forms, bookings, a store) to this same site \u2014 continue with the guide you are following, from where it left off: the StartDeployWebsite recipe (${START_DEPLOY_WEBSITE_RECIPE_URL}) or, for a site built through StartHeadlessBuild, the headless guide (${HEADLESS_GUIDE_URL}).`
17074
+ ].join("\n");
17075
+ }
17076
+ async function readJson(res, what) {
17077
+ const text = await res.text();
17078
+ if (!res.ok)
17079
+ throw new WixFetchError(res.status, res.statusText, `${what}: ${text}`);
17080
+ try {
17081
+ return JSON.parse(text);
17082
+ } catch {
17083
+ throw new Error(`${what} returned a non-JSON body: ${text.slice(0, 200)}`);
17084
+ }
17085
+ }
17086
+ function errorResult2(message) {
17087
+ return {
17088
+ isError: true,
17089
+ content: [
17090
+ {
17091
+ type: "text",
17092
+ text: `UploadHeadlessWebsiteFiles error: ${message}`
17093
+ }
17094
+ ]
17095
+ };
17096
+ }
17097
+ function addUploadHeadlessWebsiteFilesTool(server, options) {
17098
+ const { authStrategy, toolDescriptions } = options;
17099
+ const clientName = () => options.clientName ?? server.clientName?.() ?? server.server?.getClientVersion?.()?.name;
17100
+ const fetchImpl = options.fetchImpl ?? ((u, i) => fetch(u, i));
17101
+ const d = UPLOAD_HEADLESS_WEBSITE_FILES_PARAM_DESCRIPTIONS;
17102
+ const registered = server.tool(
17103
+ UPLOAD_HEADLESS_WEBSITE_FILES_TOOL_NAME,
17104
+ toolDescriptions?.[UPLOAD_HEADLESS_WEBSITE_FILES_TOOL_NAME] ?? defaultToolDescriptions.UploadHeadlessWebsiteFiles,
17105
+ {
17106
+ files: external_exports.string().optional().describe(d.files),
17107
+ attachments: external_exports.array(
17108
+ external_exports.object({
17109
+ download_url: external_exports.string(),
17110
+ file_id: external_exports.string(),
17111
+ file_name: external_exports.string().optional(),
17112
+ mime_type: external_exports.string().optional()
17113
+ })
17114
+ ).optional().describe(d.attachments),
17115
+ siteId: external_exports.string().uuid("siteId must be a Wix metaSiteId (UUID)").describe(d.siteId),
17116
+ calledStartDeployWebsite: external_exports.boolean().describe(d.calledStartDeployWebsite),
17117
+ agentIdentity: external_exports.string().min(1).describe(d.agentIdentity),
17118
+ reason: external_exports.string().describe(d.reason)
17119
+ },
17120
+ // openWorldHint true: it fetches agent-supplied attachment URLs and publishes a
17121
+ // public website.
17122
+ { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
17123
+ async (rawArgs, extra) => {
17124
+ const {
17125
+ files,
17126
+ attachments,
17127
+ siteId,
17128
+ calledStartDeployWebsite,
17129
+ agentIdentity,
17130
+ reason
17131
+ } = rawArgs;
17132
+ logger.log(
17133
+ `[UploadHeadlessWebsiteFiles] siteId=${siteId} calledStartDeployWebsite=${calledStartDeployWebsite} agent="${agentIdentity}" files=${files?.length ?? 0} chars attachments=${attachments?.length ?? 0} reason="${reason}"`
17134
+ );
17135
+ if (!files?.trim() && !(attachments && attachments.length)) {
17136
+ return errorResult2(
17137
+ "Nothing to publish: pass the site in `files` (text bundle) or `attachments` (files from this chat or your sandbox)."
17138
+ );
17139
+ }
17140
+ let siteFiles;
17141
+ try {
17142
+ const fromAttachments = (await resolveAttachments(attachments, fetchImpl)).map((a) => ({ path: a.path, bytes: base64ToBytes(a.content) }));
17143
+ siteFiles = await normalizeSiteFiles([
17144
+ ...parseFileBundle(files),
17145
+ ...fromAttachments
17146
+ ]);
17147
+ } catch (e) {
17148
+ if (e instanceof AttachmentError || e instanceof Error)
17149
+ return errorResult2(e.message);
17150
+ throw e;
17151
+ }
17152
+ const authHeaders = await authStrategy.getAccountAuthHeaders();
17153
+ try {
17154
+ const metaSiteId = siteId;
17155
+ extra?.setToolBiParams?.({ siteId: metaSiteId });
17156
+ const dropped = await readJson(
17157
+ await fetchImpl(
17158
+ `${HEADLESS_BUSINESS}/${metaSiteId}/drop${dropAttribution(clientName())}`,
17159
+ {
17160
+ method: "POST",
17161
+ headers: { ...authHeaders },
17162
+ // fetch sets the multipart boundary itself
17163
+ body: buildFormData(siteFiles)
17164
+ }
17165
+ ),
17166
+ "drop"
17167
+ );
17168
+ return {
17169
+ content: [
17170
+ {
17171
+ type: "text",
17172
+ text: formatDropResult({
17173
+ metaSiteId,
17174
+ siteUrl: dropped.siteUrl,
17175
+ uploadId: dropped.uploadId,
17176
+ files: siteFiles.map((f) => f.path)
17177
+ })
17178
+ }
17179
+ ]
17180
+ };
17181
+ } catch (err) {
17182
+ const message = err instanceof Error ? err.message : String(err);
17183
+ logger.log(`[UploadHeadlessWebsiteFiles] Error: ${message}`);
17184
+ rethrowIfUnauthorized(err);
17185
+ await authStrategy.enforceValidWixToken?.(err);
17186
+ return errorResult2(message);
17187
+ }
17188
+ }
17189
+ );
17190
+ registered?.update?.({
17191
+ _meta: { ...registered._meta, "openai/fileParams": ["attachments"] }
17192
+ });
17193
+ }
17194
+
16923
17195
  // src/code-mode/index.ts
16924
17196
  var defaultCodeModeFetch = (url, init) => fetch(url, init);
16925
17197
  var SEARCH_TOOL_NAME = "SearchWixAPISpec";
@@ -17134,7 +17406,7 @@ function addExecuteTool(server, options) {
17134
17406
  "One sentence explaining the original user request and why you are executing code to complete it."
17135
17407
  ),
17136
17408
  hasMutations: external_exports.boolean().describe(
17137
- 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."
17409
+ visitor ? "True if the code changes site data on the visitor\u2019s behalf (create, update, delete, import, upload); false if it only reads (get, query, list, search)." : "True if the code changes anything in the Wix account or a site (create, update, delete, publish, install, import, upload); false if it only reads (get, query, list, search)."
17138
17410
  ),
17139
17411
  sourceDocUrls: external_exports.array(external_exports.string()).describe(d.sourceDocUrls),
17140
17412
  files: external_exports.string().optional().describe(
@@ -17158,7 +17430,7 @@ function addExecuteTool(server, options) {
17158
17430
  mime_type: external_exports.string().optional()
17159
17431
  })
17160
17432
  ).optional().describe(
17161
- 'Files attached in this chat, or files you created in your own file sandbox (any type: a zip of a site, HTML, images, PDFs). Pass the files themselves and the platform resolves each into { download_url, file_id, file_name?, mime_type? }; the bytes are fetched here and appended to the `files` global as { path, content, encoding: "base64" } entries (path = file_name), so `wix.multipart()` and `wix.unzip()` work on them unchanged. Prefer this over base64 in `files` whenever you have a file handle. Up to 10 MB per call.'
17433
+ 'ChatGPT only. Files attached in the chat or created in your sandbox (any type: a zip of a site, HTML, images, PDFs): pass each file as you have it, its file id or its /mnt/data path, and ChatGPT swaps it for { download_url, file_id, file_name?, mime_type? } before the call reaches Wix. No other client does this swap, so outside ChatGPT a path or file:// URL here fails at once; leave this out and use `files`, or upload from your shell if it reaches the internet. The bytes are fetched here and appended to the `files` global as { path, content, encoding: "base64" } entries (path = file_name), so `wix.multipart()` and `wix.unzip()` work on them unchanged. Prefer this over base64 in `files` whenever you have a file handle. Up to 10 MB per call.'
17162
17434
  ),
17163
17435
  // Visitor mode takes an agent-supplied token and pins the site; account/site
17164
17436
  // mode lets the agent pick the site.
@@ -17172,7 +17444,10 @@ function addExecuteTool(server, options) {
17172
17444
  )
17173
17445
  }
17174
17446
  },
17175
- { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
17447
+ // openWorldHint true: agent code picks the Wix endpoints at run time (and may
17448
+ // receive platform-resolved attachment URLs); the sandbox's host allowlist
17449
+ // bounds it, but the operation set is open-ended, which is what the hint means.
17450
+ { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
17176
17451
  async (rawArgs, extra) => {
17177
17452
  const {
17178
17453
  code,
@@ -19317,6 +19592,13 @@ function createWixToolkit(options) {
19317
19592
  maxResponseSize
19318
19593
  });
19319
19594
  }
19595
+ if (requestedTools.has("UploadHeadlessWebsiteFiles") && !disableTools?.includes("UploadHeadlessWebsiteFiles")) {
19596
+ addUploadHeadlessWebsiteFilesTool(server, {
19597
+ authStrategy,
19598
+ toolDescriptions: effectiveToolDescriptions,
19599
+ clientName
19600
+ });
19601
+ }
19320
19602
  const requestedGraphqlTools = GRAPHQL_TOOL_NAMES.filter(
19321
19603
  (name) => requestedTools.has(name)
19322
19604
  );