@wix/mcp 1.0.95 → 1.0.96

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.
@@ -71493,6 +71493,140 @@ async function fetchPlatformizedMenu(menuUrl) {
71493
71493
  return data.content;
71494
71494
  }
71495
71495
 
71496
+ // src/docs/guessed-url.ts
71497
+ var SKILLS_BASE = "https://www.wix.com/skills/";
71498
+ var registryUrl = (url) => normalizeSkillsUrl(url).replace(
71499
+ /^https:\/\/(?:dev\.wix\.com(?:\/docs)?|www\.wix\.com\/velo\/reference)\/skills\//,
71500
+ SKILLS_BASE
71501
+ );
71502
+ var DOCS_BASE = "https://dev.wix.com/docs/";
71503
+ var FILLER = /* @__PURE__ */ new Set([
71504
+ "",
71505
+ "md",
71506
+ "a",
71507
+ "an",
71508
+ "the",
71509
+ "and",
71510
+ "or",
71511
+ "of",
71512
+ "to",
71513
+ "wix",
71514
+ "s"
71515
+ ]);
71516
+ var slugWords = (slug) => new Set(
71517
+ slug.toLowerCase().replace(/\.md$/, "").split(/[^a-z0-9]+/).filter((w) => !FILLER.has(w)).map((w) => w.length > 3 && w.endsWith("s") ? w.slice(0, -1) : w)
71518
+ );
71519
+ function slugSimilarity(a2, b) {
71520
+ const x = slugWords(a2);
71521
+ const y = slugWords(b);
71522
+ if (x.size === 0 || y.size === 0) return 0;
71523
+ let shared = 0;
71524
+ for (const w of x) if (y.has(w)) shared++;
71525
+ return shared / (x.size + y.size - shared);
71526
+ }
71527
+ var lastSegment = (path10) => path10.slice(path10.lastIndexOf("/") + 1);
71528
+ var folderOf = (path10) => path10.includes("/") ? path10.slice(0, path10.lastIndexOf("/")) : "";
71529
+ var SKILL_FILE_MATCH = 0.5;
71530
+ function closestSkillFile(url, manifest, manifestUrl) {
71531
+ const normalized = registryUrl(url).replace(/[?#].*$/, "");
71532
+ if (!normalized.startsWith(SKILLS_BASE)) return null;
71533
+ const [, ...rest] = normalized.slice(SKILLS_BASE.length).split("/");
71534
+ const asked = rest.join("/");
71535
+ const paths = (manifest.files ?? []).map((f) => f.path);
71536
+ if (!asked || paths.length === 0) return null;
71537
+ const base = manifest.base ?? manifestUrl.replace(/\.manifest\.json$/, "/");
71538
+ const name = lastSegment(asked);
71539
+ const folder = folderOf(asked);
71540
+ const sameName = paths.filter((p) => lastSegment(p) === name);
71541
+ if (sameName.length === 1) {
71542
+ return { kind: "file", path: sameName[0], url: base + sameName[0] };
71543
+ }
71544
+ const ranked = paths.filter((p) => folderOf(p) === folder).map((p) => ({ p, score: slugSimilarity(lastSegment(p), name) })).sort((a2, b) => b.score - a2.score);
71545
+ const [best, next] = ranked;
71546
+ if (best && best.score >= SKILL_FILE_MATCH && best.score > (next?.score ?? 0)) {
71547
+ return { kind: "file", path: best.p, url: base + best.p };
71548
+ }
71549
+ if (ranked.length > 0) {
71550
+ return {
71551
+ kind: "folder",
71552
+ folder,
71553
+ files: ranked.map((r) => base + r.p),
71554
+ manifestUrl
71555
+ };
71556
+ }
71557
+ const top = [...new Set(paths.map((p) => p.split("/")[0]))].sort();
71558
+ return {
71559
+ kind: "folder",
71560
+ folder: "",
71561
+ files: top.map((t) => base + t),
71562
+ manifestUrl
71563
+ };
71564
+ }
71565
+ function skillManifestUrl(url) {
71566
+ const normalized = registryUrl(url);
71567
+ if (!normalized.startsWith(SKILLS_BASE)) return null;
71568
+ const skill = normalized.slice(SKILLS_BASE.length).split(/[/?#]/)[0];
71569
+ return skill ? `${SKILLS_BASE}${skill}.manifest.json` : null;
71570
+ }
71571
+ var MENU_WALK_UP_LEVELS = 3;
71572
+ var MIN_MENU_SEGMENTS = 3;
71573
+ function parentMenuUrls(url) {
71574
+ const clean = url.replace(/[?#].*$/, "").replace(/\/+$/, "");
71575
+ if (!clean.startsWith(DOCS_BASE)) return [];
71576
+ const segments = clean.slice(DOCS_BASE.length).split("/");
71577
+ const out = [];
71578
+ for (let up = 1; up <= MENU_WALK_UP_LEVELS; up++) {
71579
+ const kept = segments.length - up;
71580
+ if (kept < MIN_MENU_SEGMENTS) break;
71581
+ out.push(DOCS_BASE + segments.slice(0, kept).join("/"));
71582
+ }
71583
+ return out;
71584
+ }
71585
+ function closestMenuLinks(menuText, menuUrl, askedUrl, limit = 3) {
71586
+ const asked = lastSegment(
71587
+ askedUrl.replace(/[?#].*$/, "").replace(/\/+$/, "")
71588
+ );
71589
+ const under = `${menuUrl.replace(/\/+$/, "")}/`;
71590
+ const links = [
71591
+ ...new Set(
71592
+ menuText.match(/https:\/\/dev\.wix\.com\/docs\/[^\s)\]>"'`]+/g) ?? []
71593
+ )
71594
+ ].filter((l) => l.startsWith(under));
71595
+ return links.map((l) => ({
71596
+ l,
71597
+ score: slugSimilarity(lastSegment(l.replace(/\.md$/, "")), asked)
71598
+ })).filter((r) => r.score > 0).sort((a2, b) => b.score - a2.score).slice(0, limit).map((r) => r.l);
71599
+ }
71600
+ var SMALL_MENU = 3;
71601
+ var MENU_ENTRY_MATCH = 0.75;
71602
+ function subMenusToRead(menuText, menuUrl, askedUrl, limit = 2) {
71603
+ const asked = lastSegment(
71604
+ askedUrl.replace(/[?#].*$/, "").replace(/\/+$/, "")
71605
+ );
71606
+ const under = `${menuUrl.replace(/\/+$/, "")}/`;
71607
+ const methods = new Set(
71608
+ [
71609
+ ...menuText.matchAll(
71610
+ /\]\((https:\/\/dev\.wix\.com\/docs\/[^)\s]+)\)\s*_\((?:GET|POST|PUT|PATCH|DELETE)\)_/g
71611
+ )
71612
+ ].map((m) => m[1])
71613
+ );
71614
+ const entries = [
71615
+ ...new Set(
71616
+ menuText.match(/https:\/\/dev\.wix\.com\/docs\/[^\s)\]>"'`]+/g) ?? []
71617
+ )
71618
+ ].filter(
71619
+ (l) => l.startsWith(under) && !l.slice(under.length).replace(/\.md$/, "").includes("/") && !methods.has(l)
71620
+ );
71621
+ const scored = entries.map((l) => ({
71622
+ l,
71623
+ score: slugSimilarity(lastSegment(l.replace(/\.md$/, "")), asked)
71624
+ })).sort((a2, b) => b.score - a2.score);
71625
+ if (scored.some((s) => s.score >= MENU_ENTRY_MATCH)) return [];
71626
+ if (entries.length <= SMALL_MENU) return entries;
71627
+ return scored.filter((s) => s.score > 0).slice(0, limit).map((s) => s.l);
71628
+ }
71629
+
71496
71630
  // ../../node_modules/dedent/dist/dedent.mjs
71497
71631
  function ownKeys2(object3, enumerableOnly) {
71498
71632
  var keys = Object.keys(object3);
@@ -71697,7 +71831,8 @@ var DISCOVERY_FLOWS = [
71697
71831
  function directRouteLines(includeReadme, includeImageUploadGuidance) {
71698
71832
  return [
71699
71833
  ...includeReadme ? [
71700
- `- **Creating a new site.** Skip the docs too. A site is written in code and uploaded: StartDeployWebsite returns the upload guide; StartHeadlessBuild returns the headless guide, for a site that needs Wix business features (${WIX_BUSINESS_FEATURES}). Call one right away, without asking the user anything first: StartHeadlessBuild when the site needs Wix business features, otherwise StartDeployWebsite. WixSiteBuilder or SearchSiteTemplates only when the user asked for Wix's editor, the Wix Harmony Editor, a template, Wix Studio or the Classic Editor. Never create a site through ManageWixSite, CallWixSiteAPI or ExecuteWixAPI.`
71834
+ `- **Creating a new site.** Skip the docs too. A site is written in code and uploaded: StartDeployWebsite returns the upload guide; StartHeadlessBuild returns the headless guide, for a site that needs Wix business features (${WIX_BUSINESS_FEATURES}). Call one right away, without asking the user anything first: StartHeadlessBuild when the site needs Wix business features, otherwise StartDeployWebsite. WixHarmonyEditorBuild or SearchSiteTemplates only when the user asked for Wix's editor, the Wix Harmony Editor, a template, Wix Studio or the Classic Editor. Never create a site through ManageWixSite, CallWixSiteAPI or ExecuteWixAPI.`,
71835
+ "- **Suggesting domain names**: call GetSuggestedDomains directly, with the business or site name as the query."
71701
71836
  ] : [],
71702
71837
  "- **Listing, showing or finding their Wix sites**: call ListWixSites directly.",
71703
71838
  ...includeImageUploadGuidance ? [
@@ -71771,7 +71906,7 @@ var toWidget = (text) => Object.values(SITE_CREATION_COPY).reduce(
71771
71906
  (acc, { nonWidget, widget }) => acc.replaceAll(nonWidget, widget),
71772
71907
  text
71773
71908
  );
71774
- var WIX_API_DATA = "products, inventory, carts, checkouts, orders, bookings, services, staff, time slots, rentals, restaurant menus, online orders, reservations, events, tickets, RSVPs, blog posts, CMS collections and items, pricing plans, subscriptions, portfolios, gift cards, coupons, donations, forms, form submissions, contacts, members, loyalty points, groups, reviews, inbox messages, CRM tasks, automations, email campaigns, payments, invoices, SEO, redirects, translations, locations, analytics, media files, site properties, installed apps, and the account's sites and domains";
71909
+ var WIX_API_DATA = "products, inventory, carts, checkouts, orders, bookings, services, staff, time slots, rentals, restaurant menus, online orders, reservations, events, tickets, RSVPs, blog posts, CMS collections and items, pricing plans, subscriptions, portfolios, gift cards, coupons, donations, forms, form submissions, contacts, members, loyalty points, groups, reviews, inbox messages, CRM tasks, automations, email campaigns, payments, invoices, SEO, redirects, translations, locations, analytics, media files, site properties, installed apps, and the account's sites and domains (searching whether a domain is available, buying one, and connecting one bought elsewhere)";
71775
71910
  var EXECUTE_WIX_API_INTRO = dedent_default`
71776
71911
  Reads and changes the data of a Wix site or account: query, list, count, create, update and delete it with one JavaScript function over the Wix REST API.
71777
71912
  One function holds the whole task: chained calls, pagination, loops, branching, and the reads that feed its writes.
@@ -72087,8 +72222,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true,
72087
72222
  ${reminder}
72088
72223
  `,
72089
72224
  ManageWixSite: dedent_default`
72090
- Use account level API in order to update a site.
72091
- 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\`.
72225
+ Calls an account-level Wix API: one that acts on the account itself (its sites list, domains, account settings) or on sites as objects in it (create, rename, duplicate, delete a site). It sends no site, so an API that reads or writes inside a site (its products, data, bookings, members, pages, publishing) fails here: call that through ExecuteWixAPI with the site's \`siteId\`.
72092
72226
  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"}]).
72093
72227
  The API endpoint url param MUST ALWAYS be taken from the conversation context.
72094
72228
  By conversation context we mean the endpoint url was given in the user prompt or got into the conversation context ${includeReadme ? 'by the "WixREADME" tool or ' : ""}by the "SearchWixRESTDocumentation" tool or by the "BrowseWixRESTDocsMenu" tool or by the "ReadFullDocsArticle" tool.
@@ -72158,7 +72292,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true,
72158
72292
  ${reminder}
72159
72293
  `,
72160
72294
  WixSiteCreation: dedent_default`
72161
- A new Wix site is written in code and uploaded: call StartDeployWebsite for the upload guide, or StartHeadlessBuild when the site needs Wix business features. WixSiteBuilder or SearchSiteTemplates only when the user asked for the Wix Harmony Editor, a template, Wix Studio or the Classic Editor.
72295
+ Returns which Wix tool creates a new site, and creates nothing itself. A new site is written in code and uploaded: StartDeployWebsite returns the upload guide, and StartHeadlessBuild returns the guide for a site that needs Wix business features. Use WixHarmonyEditorBuild or SearchSiteTemplates only when the user asked for Wix's editor, a template, Wix Studio or the Classic Editor.
72162
72296
  `,
72163
72297
  "import-claude-design-from-url": dedent_default`
72164
72298
  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.
@@ -72600,16 +72734,16 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true,
72600
72734
  `,
72601
72735
  ExecuteWixAPI: executeWixApiDescription,
72602
72736
  // Named in SERVER_INSTRUCTIONS: when this tool's name or what it is for changes, update them to match.
72603
- WixSiteBuilder: dedent_default`
72604
- A secondary site-creation tool, only for a user who asked for the Wix Harmony Editor or for Wix to design the site. Don't call it for any other site request, not even to check with the flag false. A new site is otherwise written in code and uploaded, with StartDeployWebsite, or StartHeadlessBuild when it needs Wix business features.
72737
+ WixHarmonyEditorBuild: dedent_default`
72738
+ A secondary site-creation tool, only for a user who asked for the Wix Harmony Editor or for Wix to design the site in Wix's editor. A request for a site on Wix, or for Wix to build or make one, is neither. Don't call it for any other site request, not even to check with the flag false. A new site is otherwise written in code and uploaded, with StartDeployWebsite, or StartHeadlessBuild when it needs Wix business features.
72605
72739
  Builds a site with the Wix Harmony Editor: Wix designs a first version from the user's description, and the user keeps designing it in Wix's editor. Shows the build's progress and a preview.
72606
- Call it when the user asked for the Wix Harmony Editor, or for Wix to design the site, in their own words or as their answer to your question. ${EDITOR_TAIL}
72740
+ Call it when the user asked for the Wix Harmony Editor, or for Wix to design the site in Wix's editor, in their own words or as their answer to your question, and quote those words in \`editorRequest\`. ${EDITOR_TAIL}
72607
72741
  `,
72608
72742
  // Named in SERVER_INSTRUCTIONS: when this tool's name or what it is for changes, update them to match.
72609
72743
  SearchSiteTemplates: dedent_default`
72610
72744
  A secondary site-creation tool, only for a user who asked for a template, Wix Studio or the Classic Editor. Don't call it for any other site request, not even to check with the flag false. A new site is otherwise written in code and uploaded, with StartDeployWebsite, or StartHeadlessBuild when it needs Wix business features.
72611
72745
  Shows a gallery of Wix templates, from Harmony, Wix Studio or the Classic Editor, for a site the user will design in Wix's editor. Wix templates come with Wix's business features (a store, online ordering, bookings, forms), so a request for a template comes here even when the site needs them.
72612
- Call it when the user asked for a template, Wix Studio or the Classic Editor, in their own words or as their answer to your question. ${EDITOR_TAIL} ${SITE_CREATION_COPY.templatePick.nonWidget}
72746
+ Call it when the user asked for a template, Wix Studio or the Classic Editor, in their own words or as their answer to your question, and quote those words in \`templateRequest\`. ${EDITOR_TAIL} ${SITE_CREATION_COPY.templatePick.nonWidget}
72613
72747
  `,
72614
72748
  CreateSiteFromTemplate: dedent_default`
72615
72749
  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.
@@ -72646,20 +72780,20 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true,
72646
72780
  UploadHeadlessWebsiteFiles: dedent_default`
72647
72781
  The upload step of the StartDeployWebsite and StartHeadlessBuild guides, and nothing else: puts a finished website's files — a static site (index.html plus its CSS/JS/images, a static build, or a zip of the site) or a Wix Astro build — 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.
72648
72782
  Pass the files themselves: in ${FILE_PARAM_CLIENTS} as \`attachments\` (any type, a zip is unpacked), elsewhere as the \`files\` text bundle (\`=== FILE: <path> ===\` sections, \`base64\` tag for small binaries). A static site must contain index.html at its root. A Wix Astro build with no file handle, from a shell that reaches the internet, goes in two calls: zip the build, call with \`prepare: true\` and the zip's \`size\` in bytes, PUT the zips it names with the \`curl\` commands it returns (the build, and the project source when the site's repository accepts it), then call again with the \`uploadKey\` it returned; that releases the build. The two calls, literally: {siteId, prepare: true, size} then {siteId, uploadKey, sourceUploadId?, expectedHeadRevisionId?, releaseNotes?}. A sandbox whose network is limited to a few hosts cannot upload to those URLs. A static site does not use the two calls. A Wix Astro build (the project's .wix/ and dist/ after \`astro build\` with the Wix hosting adapter, zipped from the project root) needs no index.html: it is released to the site's Wix hosting instead, server-rendered pages, the same site and URL. The project's source is stored with the release when the upload holds the project and the site's repository accepts it.
72649
- NOT a site-creation tool: a request to create, build, design or generate a site — with or without a description — never comes here first. Unless the user asked for Wix's editor or a template (WixSiteBuilder, SearchSiteTemplates), call StartDeployWebsite right away, without asking the user anything: it also takes files the user brought, uploaded first even when the site needs business features. StartHeadlessBuild instead for a new site with no files that needs Wix business features. Also NOT for media uploads to an existing site (UploadImageToWixSite) or any other API call (ExecuteWixAPI).
72783
+ NOT a site-creation tool: a request to create, build, design or generate a site — with or without a description — never comes here first. Unless the user asked for Wix's editor or a template (WixHarmonyEditorBuild, SearchSiteTemplates), call StartDeployWebsite right away, without asking the user anything: it also takes files the user brought, uploaded first even when the site needs business features. StartHeadlessBuild instead for a new site with no files that needs Wix business features. Also NOT for media uploads to an existing site (UploadImageToWixSite) or any other API call (ExecuteWixAPI).
72650
72784
  `,
72651
72785
  // Named in SERVER_INSTRUCTIONS: when this tool's name or what it is for changes, update them to match.
72652
72786
  StartDeployWebsite: dedent_default`
72653
72787
  Creates a website on Wix and puts it live, or replaces the files of one deployed earlier. You write the site's code, or take the files the user brought (HTML/CSS/JS, a static build, a zip, another builder's export), and this returns the guide to create the site and upload them. Updates keep the same site and URL. Works with or without a Wix login (anonymous deploy with a save link), through the Wix MCP alone or from a shell. Call it right away, without asking the user anything first.
72654
72788
  When a new site needs Wix business features (${WIX_BUSINESS_FEATURES}), call StartHeadlessBuild instead. When the user brought files and the site needs those features, upload the files here first; the upload's result then points to StartHeadlessBuild to connect them.
72655
72789
  Returns the guide; follow it and do the work yourself. Upload with UploadHeadlessWebsiteFiles once the site exists; the drop page is only the fallback.
72656
- NOT for: a user who asked for Wix's editor, the Wix Harmony Editor, a template, Wix Studio or the Classic Editor (WixSiteBuilder, SearchSiteTemplates); 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 editor site; uploading images or documents to a site's media (UploadImageToWixSite).
72790
+ NOT for: a user who asked for Wix's editor, the Wix Harmony Editor, a template, Wix Studio or the Classic Editor (WixHarmonyEditorBuild, SearchSiteTemplates); 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 editor site; uploading images or documents to a site's media (UploadImageToWixSite).
72657
72791
  `,
72658
72792
  // Named in SERVER_INSTRUCTIONS: when this tool's name or what it is for changes, update them to match.
72659
72793
  StartHeadlessBuild: dedent_default`
72660
72794
  Returns Wix's headless guide, for a site that needs Wix business features (${WIX_BUSINESS_FEATURES}), run by Wix as a Wix Headless site. You write the site; the guide creates it, connects the features, and uploads it. Also to add those features to an existing Wix Headless site (one deployed from files is one), from a project download link, or to move a Wix site to code.
72661
72795
  Call it right away: it only returns the guide, and the site's details are not needed first.
72662
- NOT for: a site that needs no Wix business feature, or files the user brought that are not live yet (StartDeployWebsite: upload them first, then come back here for the features); a user who asked for Wix's editor, the Wix Harmony Editor, a template, Wix Studio or the Classic Editor, even when the site needs business features (WixSiteBuilder, SearchSiteTemplates); a Claude Design URL (import-claude-design-from-url).
72796
+ NOT for: a site that needs no Wix business feature, or files the user brought that are not live yet (StartDeployWebsite: upload them first, then come back here for the features); a user who asked for Wix's editor, the Wix Harmony Editor, a template, Wix Studio or the Classic Editor, even when the site needs business features (WixHarmonyEditorBuild, SearchSiteTemplates); a Claude Design URL (import-claude-design-from-url).
72663
72797
  `
72664
72798
  };
72665
72799
  descriptions.SupportAndFeedback = descriptions.ReportIssueToWix;
@@ -72849,7 +72983,7 @@ var defaultReadmeDocs = buildReadmeDocs();
72849
72983
  var widgetSiteCreationConfig = {
72850
72984
  toolDescriptions: Object.fromEntries(
72851
72985
  [
72852
- "WixSiteBuilder",
72986
+ "WixHarmonyEditorBuild",
72853
72987
  "WixSiteCreation",
72854
72988
  "SearchSiteTemplates",
72855
72989
  "CreateSiteFromTemplate"
@@ -74270,9 +74404,16 @@ function shouldTryMenuFallbackForDocsRead(error2, articleUrl) {
74270
74404
  if (!message.includes("404") || !isMenuFallbackUrl) {
74271
74405
  return false;
74272
74406
  }
74273
- return !["This resource is deprecated", "No schema found"].some(
74274
- (knownArticleError) => message.includes(knownArticleError)
74275
- );
74407
+ return !isKnownArticleError(error2);
74408
+ }
74409
+ function isKnownArticleError(error2) {
74410
+ const message = getErrorMessage(error2);
74411
+ return [
74412
+ "This resource is deprecated",
74413
+ "This method is deprecated",
74414
+ "This article is deprecated",
74415
+ "No schema found"
74416
+ ].some((knownArticleError) => message.includes(knownArticleError));
74276
74417
  }
74277
74418
  function formatDocsReadError(toolAction, error2) {
74278
74419
  return `Could not fetch ${toolAction}: "${getErrorMessage(error2)}".`;
@@ -74359,6 +74500,58 @@ var addDocsTools = (server2, allowedTools = [
74359
74500
  }
74360
74501
  return menuContent.length > MENU_CONTENT_THRESHOLD ? summarizeLargeMenu(menuContent, url) : normalizeMenuContent(menuContent);
74361
74502
  };
74503
+ const nearestToMissingPage = async (httpClient, url, isSkillsPage) => {
74504
+ try {
74505
+ if (isSkillsPage) {
74506
+ const manifestUrl = skillManifestUrl(url);
74507
+ if (!manifestUrl) return null;
74508
+ const manifest = JSON.parse(
74509
+ await fetchArticleContent(httpClient, manifestUrl, "article")
74510
+ );
74511
+ const guess = closestSkillFile(url, manifest, manifestUrl);
74512
+ if (!guess) return null;
74513
+ if (guess.kind === "file") {
74514
+ const text = await fetchArticleContent(
74515
+ httpClient,
74516
+ guess.url,
74517
+ "article"
74518
+ );
74519
+ return `Note: "${url}" does not exist. Found a similar file in the skill, ${guess.url}, and read it:
74520
+
74521
+ ${text}`;
74522
+ }
74523
+ return `Note: "${url}" does not exist, and no file in ${guess.folder ? `its folder (${guess.folder}/)` : "the skill"} has a name close to it. ${guess.folder ? "The files in that folder" : "The skill's top-level folders and files"}, to read the one you need (every file: ${guess.manifestUrl}):
74524
+
74525
+ ${guess.files.map((f) => `- ${f}`).join("\n")}`;
74526
+ }
74527
+ for (const menuUrl of parentMenuUrls(url)) {
74528
+ const text = await fetchMenuTextForUrl(httpClient, menuUrl);
74529
+ if (!text) continue;
74530
+ const subMenus = (await Promise.all(
74531
+ subMenusToRead(text, menuUrl, url).map(async (sub) => ({
74532
+ sub,
74533
+ text: await fetchMenuTextForUrl(httpClient, sub).catch(() => null)
74534
+ }))
74535
+ )).filter((m) => !!m.text);
74536
+ const closest = closestMenuLinks(
74537
+ [text, ...subMenus.map((m) => m.text)].join("\n"),
74538
+ menuUrl,
74539
+ url
74540
+ );
74541
+ return [
74542
+ `Note: "${url}" does not exist. Showing the nearest documentation menu above it, ${menuUrl}; read the page you need from its links.`,
74543
+ ...closest.length ? [
74544
+ `Closest to what you asked for:
74545
+ ${closest.map((l) => `- ${l}`).join("\n")}`
74546
+ ] : [],
74547
+ text
74548
+ ].join("\n\n");
74549
+ }
74550
+ } catch (error2) {
74551
+ logger2.error(`[docs] no nearest page for ${url}: ${error2}`);
74552
+ }
74553
+ return null;
74554
+ };
74362
74555
  if (startDeployWebsiteEnabled) {
74363
74556
  addStartDeployWebsiteTool(server2, disableTools || [], toolDescriptions, {
74364
74557
  skillsPortalUrl,
@@ -75095,6 +75288,16 @@ ${text}`
75095
75288
  } catch {
75096
75289
  }
75097
75290
  }
75291
+ if (getErrorMessage(error2).includes("404") && !isKnownArticleError(error2)) {
75292
+ const nearest = await nearestToMissingPage(
75293
+ httpClient,
75294
+ articleUrl,
75295
+ isSkillsPage
75296
+ );
75297
+ if (nearest) {
75298
+ return { content: [{ type: "text", text: nearest }] };
75299
+ }
75300
+ }
75098
75301
  return {
75099
75302
  isError: true,
75100
75303
  content: [
@@ -79336,11 +79539,11 @@ var getEncodingTransformGenerator = (binary, encoding, skipped) => {
79336
79539
  final: encodingStringFinal.bind(void 0, stringDecoder)
79337
79540
  };
79338
79541
  };
79339
- var encodingUint8ArrayGenerator = function* (textEncoder3, chunk) {
79542
+ var encodingUint8ArrayGenerator = function* (textEncoder4, chunk) {
79340
79543
  if (Buffer3.isBuffer(chunk)) {
79341
79544
  yield bufferToUint8Array(chunk);
79342
79545
  } else if (typeof chunk === "string") {
79343
- yield textEncoder3.encode(chunk);
79546
+ yield textEncoder4.encode(chunk);
79344
79547
  } else {
79345
79548
  yield chunk;
79346
79549
  }
@@ -86146,6 +86349,16 @@ var getRetryHint = (errorDetails) => {
86146
86349
  }
86147
86350
  return /"operator":"\{/.test(errorDetails) ? SINGLE_OPERATOR_PER_FIELD_HINT : FIELD_CAPABILITIES_HINT;
86148
86351
  };
86352
+ var NO_SITE_IN_REQUEST = /META_SITE_NOT_FOUND|MetaSite(?:Id)? (?:context )?(?:is missing|was not found|not found)|NO_METASITE_IN_CALL_CONTEXT|MISSING_METASITE_ID|MISSING_REQUEST_SITE_CONTEXT|metasiteId from the request context/i;
86353
+ function missingSiteContext(status, responseData) {
86354
+ const details = typeof responseData === "string" ? responseData : JSON.stringify(responseData ?? "");
86355
+ if (NO_SITE_IN_REQUEST.test(details)) return "certain";
86356
+ const parsed = typeof responseData === "string" ? safeParseJSON(responseData) : responseData;
86357
+ if (status === 403 && parsed && typeof parsed === "object" && !parsed.message) {
86358
+ return "likely";
86359
+ }
86360
+ return null;
86361
+ }
86149
86362
  var DEFAULT_OPENAI_SECURITY_SCHEMES = [
86150
86363
  { type: "oauth2", scopes: ["offline_access"] }
86151
86364
  ];
@@ -86160,7 +86373,8 @@ var buildErrorMessage = ({
86160
86373
  status,
86161
86374
  statusText,
86162
86375
  requestId,
86163
- responseData
86376
+ responseData,
86377
+ retryHint
86164
86378
  }) => {
86165
86379
  const responseDataObject = typeof responseData === "string" ? safeParseJSON(responseData) : responseData;
86166
86380
  const errorDetails = typeof responseDataObject === "object" ? JSON.stringify(responseDataObject) : responseDataObject;
@@ -86172,7 +86386,7 @@ var buildErrorMessage = ({
86172
86386
  ].filter((str) => !!str).join("\n");
86173
86387
  return `${baseMessage}
86174
86388
 
86175
- ${getRetryHint(String(errorDetails))}`;
86389
+ ${retryHint ?? getRetryHint(String(errorDetails))}`;
86176
86390
  };
86177
86391
  var handleWixAPIResponse = async (response) => {
86178
86392
  const requestId = response.headers?.["x-wix-request-id"];
@@ -86313,6 +86527,49 @@ function createToolBiEvent(params) {
86313
86527
  return wixMcpRequestSrc39Evid1607(params);
86314
86528
  }
86315
86529
 
86530
+ // src/api-call/site-files.ts
86531
+ var MAX_FILES_BUNDLE_CHARS = 4e6;
86532
+ var MAX_UNZIP_BYTES = 64 * 1024 * 1024;
86533
+ var textEncoder3 = new TextEncoder();
86534
+ function base64ToBytes(b64) {
86535
+ const clean = b64.replace(/^data:[^,]*,/, "").replace(/\s+/g, "");
86536
+ return Uint8Array.from(atob(clean), (c3) => c3.charCodeAt(0));
86537
+ }
86538
+ function parseFileBundle(bundle) {
86539
+ if (!bundle || !bundle.trim()) return [];
86540
+ if (bundle.length > MAX_FILES_BUNDLE_CHARS) {
86541
+ throw new Error(
86542
+ `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.`
86543
+ );
86544
+ }
86545
+ const delimiter = /^=== FILE: (.+?)(?: (base64))? ===[ \t]*$/gm;
86546
+ const matches = [...bundle.matchAll(delimiter)];
86547
+ if (matches.length === 0) {
86548
+ throw new Error(
86549
+ "files bundle has no `=== FILE: <path> ===` sections. Start each file with that line (add ` base64` before the closing === for binary content)."
86550
+ );
86551
+ }
86552
+ return matches.map((match, i2) => {
86553
+ const path10 = match[1].trim();
86554
+ const start = (match.index ?? 0) + match[0].length;
86555
+ const end = i2 + 1 < matches.length ? matches[i2 + 1].index ?? bundle.length : bundle.length;
86556
+ const content = bundle.slice(start, end).replace(/^\r?\n/, "").replace(/\r?\n$/, "");
86557
+ return {
86558
+ path: path10,
86559
+ bytes: match[2] === "base64" ? base64ToBytes(content) : textEncoder3.encode(content)
86560
+ };
86561
+ });
86562
+ }
86563
+ var CRC_TABLE = (() => {
86564
+ const t = new Uint32Array(256);
86565
+ for (let n2 = 0; n2 < 256; n2++) {
86566
+ let c3 = n2;
86567
+ for (let k = 0; k < 8; k++) c3 = c3 & 1 ? 3988292384 ^ c3 >>> 1 : c3 >>> 1;
86568
+ t[n2] = c3 >>> 0;
86569
+ }
86570
+ return t;
86571
+ })();
86572
+
86316
86573
  // src/infra/redact-personal-data.ts
86317
86574
  var FREE_TEXT_INFO_PARAMS = [
86318
86575
  "userRequest",
@@ -86322,7 +86579,9 @@ var FREE_TEXT_INFO_PARAMS = [
86322
86579
  "agentIdentity",
86323
86580
  "wixAccess",
86324
86581
  "reason",
86325
- "whyNotExecuteWixAPI"
86582
+ "whyNotExecuteWixAPI",
86583
+ "editorRequest",
86584
+ "templateRequest"
86326
86585
  ];
86327
86586
  var EMAIL = /[A-Z0-9._%+-]+@[A-Z0-9-]+(?:\.[A-Z0-9-]+)*\.[A-Z]{2,}/gi;
86328
86587
  var NAMED_SECRET = /\b(passwords?|passwd|pwd|passcodes?|secrets?|tokens?|api[ _-]?keys?|access[ _-]?keys?|credentials?)(\s*[:=]\s*|\s+(?:is|was|to)\s+)(?:"[^"]*"|'[^']*'|`[^`]*`|[^\s"'`,;][^,;\n]*?(?=[.!?]\s|[,;\n]|$))/gi;
@@ -86370,9 +86629,65 @@ function redactInfoParams(value) {
86370
86629
  }
86371
86630
  return redacted;
86372
86631
  }
86632
+ var MAX_LISTED_PATHS_CHARS = 2e3;
86633
+ function summarizeFileBundle(bundle) {
86634
+ let sections;
86635
+ try {
86636
+ sections = parseFileBundle(bundle).map((file) => ({
86637
+ path: file.path,
86638
+ bytes: file.bytes.length
86639
+ }));
86640
+ } catch {
86641
+ sections = [];
86642
+ }
86643
+ if (sections.length === 0) {
86644
+ return `${new TextEncoder().encode(bundle).length} bytes, no file sections`;
86645
+ }
86646
+ return formatFileSummary(sections);
86647
+ }
86648
+ function formatFileSummary(files) {
86649
+ const total = files.reduce((sum, file) => sum + file.bytes, 0);
86650
+ let listed = "";
86651
+ let shown = 0;
86652
+ for (const file of files) {
86653
+ const entry = `${shown ? ", " : ""}${file.path} (${file.bytes})`;
86654
+ if (listed.length + entry.length > MAX_LISTED_PATHS_CHARS) break;
86655
+ listed += entry;
86656
+ shown++;
86657
+ }
86658
+ const more = files.length - shown;
86659
+ return `${files.length} file(s), ${total} bytes: ${listed}${more ? `${shown ? ", " : ""}and ${more} more` : ""}`;
86660
+ }
86661
+ function compactFileParams(value) {
86662
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
86663
+ return value;
86664
+ }
86665
+ const record2 = value;
86666
+ const files = record2.files;
86667
+ if (typeof files === "string") {
86668
+ return { ...record2, files: summarizeFileBundle(files) };
86669
+ }
86670
+ if (Array.isArray(files) && files.length > 0 && files.every(
86671
+ (entry) => entry && typeof entry === "object" && typeof entry.content === "string"
86672
+ )) {
86673
+ return {
86674
+ ...record2,
86675
+ files: formatFileSummary(
86676
+ files.map((entry) => ({
86677
+ path: entry.path ?? "?",
86678
+ bytes: new TextEncoder().encode(entry.content).length
86679
+ }))
86680
+ )
86681
+ };
86682
+ }
86683
+ return value;
86684
+ }
86685
+ function prepareForBi(value) {
86686
+ return compactFileParams(redactInfoParams(value));
86687
+ }
86373
86688
  function serializeToolParamsForBi(args) {
86374
86689
  return JSON.stringify(
86375
- Array.isArray(args) ? args.map(redactInfoParams) : redactInfoParams(args)
86690
+ Array.isArray(args) ? args.map(prepareForBi) : prepareForBi(args)
86376
86691
  );
86377
86692
  }
86378
86693
 
@@ -87109,6 +87424,9 @@ If you can't deliver the image any of these ways, call ${imagePickerToolName} so
87109
87424
  var MAX_ALLOWED_RESPONSE_CHARS = 2e6;
87110
87425
 
87111
87426
  // src/api-call/index.ts
87427
+ var siteToolCall = (executeServed) => executeServed ? "through ExecuteWixAPI with that site's `siteId` (`scope: \"site\"`), loading ExecuteWixAPI through your client's tool search if it isn't loaded" : "through CallWixSiteAPI with that site's `siteId`";
87428
+ var manageNoSiteHint = (executeServed) => `This is a site-level API: it acts inside a site, and ManageWixSite sends no site, so the same call fails here every time. Call the same URL, method and body ${siteToolCall(executeServed)}.`;
87429
+ var manageEmpty403Hint = (executeServed) => `If this endpoint acts inside a site (its products, data, bookings, members, pages, publishing), it is site-level, and ManageWixSite sends no site: call the same URL, method and body ${siteToolCall(executeServed)}. If it acts on the account or on sites as objects in it, the account lacks the permission; don't retry.`;
87112
87430
  var DEFAULT_MAX_RESPONSE_SIZE = 5e4;
87113
87431
  function extractReturnedRows(responseData) {
87114
87432
  if (responseData === null || typeof responseData !== "object") {
@@ -87432,6 +87750,18 @@ function addApiCallTool(server2, strategy, allowedTools = ["CallWixSiteAPI", "Li
87432
87750
  if (!response) {
87433
87751
  throw new Error(`Failed to call Wix Account API: ${error2}`);
87434
87752
  }
87753
+ const noSite = missingSiteContext(response.status, response.data);
87754
+ if (noSite) {
87755
+ throw new Error(
87756
+ buildErrorMessage({
87757
+ status: response.status,
87758
+ statusText: response.statusText,
87759
+ requestId: response.headers?.["x-wix-request-id"] ?? null,
87760
+ responseData: response.data,
87761
+ retryHint: (noSite === "certain" ? manageNoSiteHint : manageEmpty403Hint)(!!options?.executeWixAPIServed)
87762
+ })
87763
+ );
87764
+ }
87435
87765
  return handleWixAPIResponse(response);
87436
87766
  }
87437
87767
  }