@wix/mcp 1.0.94 → 1.0.95

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 (79) hide show
  1. package/README.md +2 -2
  2. package/build/bin-standalone.js +1116 -321
  3. package/build/cjs/index.cjs +2813 -1147
  4. package/build/cjs/toolkit.cjs +1788 -362
  5. package/build/dts/api-call/headless-release.d.ts +56 -0
  6. package/build/dts/api-call/headless-release.js +135 -0
  7. package/build/dts/api-call/index.d.ts +9 -1
  8. package/build/dts/api-call/index.js +13 -7
  9. package/build/dts/api-call/site-files.d.ts +14 -0
  10. package/build/dts/api-call/site-files.js +106 -10
  11. package/build/dts/api-call/upload-headless-website-files.d.ts +88 -5
  12. package/build/dts/api-call/upload-headless-website-files.js +473 -20
  13. package/build/dts/api-call/upload-image-tool.d.ts +2 -0
  14. package/build/dts/api-call/upload-image-tool.js +8 -4
  15. package/build/dts/bin.d.ts +1 -1
  16. package/build/dts/bin.js +3 -1
  17. package/build/dts/code-mode/index.d.ts +6 -1
  18. package/build/dts/code-mode/index.js +30 -10
  19. package/build/dts/code-mode/truncate-response.js +5 -1
  20. package/build/dts/config/default-config.d.ts +35 -5
  21. package/build/dts/config/default-config.js +334 -178
  22. package/build/dts/config/file-params.d.ts +22 -0
  23. package/build/dts/config/file-params.js +24 -0
  24. package/build/dts/config/param-descriptions.d.ts +14 -2
  25. package/build/dts/config/param-descriptions.js +33 -14
  26. package/build/dts/constants.d.ts +1 -0
  27. package/build/dts/constants.js +1 -0
  28. package/build/dts/docs/docs.d.ts +12 -1
  29. package/build/dts/docs/docs.js +210 -42
  30. package/build/dts/docs/fetch-article.d.ts +21 -0
  31. package/build/dts/docs/fetch-article.js +66 -4
  32. package/build/dts/docs/get-to-know.d.ts +7 -2
  33. package/build/dts/docs/get-to-know.js +36 -14
  34. package/build/dts/docs/mintlify-docs.d.ts +121 -0
  35. package/build/dts/docs/mintlify-docs.js +391 -0
  36. package/build/dts/docs/semanticSearch.d.ts +2 -1
  37. package/build/dts/docs/semanticSearch.js +14 -7
  38. package/build/dts/docs/start-deploy-website.d.ts +7 -6
  39. package/build/dts/docs/start-deploy-website.js +13 -8
  40. package/build/dts/docs/start-headless-build.d.ts +8 -10
  41. package/build/dts/docs/start-headless-build.js +14 -28
  42. package/build/dts/index.d.ts +2 -2
  43. package/build/dts/index.js +2 -2
  44. package/build/dts/infra/redact-personal-data.d.ts +4 -0
  45. package/build/dts/infra/redact-personal-data.js +79 -0
  46. package/build/dts/site-widget-tools/constants.d.ts +49 -1
  47. package/build/dts/site-widget-tools/constants.js +64 -7
  48. package/build/dts/site-widget-tools/index.d.ts +1 -0
  49. package/build/dts/site-widget-tools/index.js +1 -0
  50. package/build/dts/site-widget-tools/pick-images-tool/index.d.ts +14 -0
  51. package/build/dts/site-widget-tools/pick-images-tool/index.js +138 -0
  52. package/build/dts/site-widget-tools/search-site-templates-tool/index.js +16 -2
  53. package/build/dts/site-widget-tools/site-builder-tool/index.js +22 -2
  54. package/build/dts/site-widget-tools/site-creation-tool/index.d.ts +7 -1
  55. package/build/dts/site-widget-tools/site-creation-tool/index.js +22 -51
  56. package/build/dts/site-widget-tools/suggested-domain-tool/index.js +6 -3
  57. package/build/dts/support/index.d.ts +4 -0
  58. package/build/dts/support/index.js +13 -7
  59. package/build/dts/toolkit-entry.d.cts +15 -10
  60. package/build/dts/toolkit-entry.d.ts +15 -10
  61. package/build/dts/toolkit.d.ts +2 -2
  62. package/build/dts/toolkit.js +14 -7
  63. package/build/dts/wix-mcp-server.d.ts +10 -1
  64. package/build/dts/wix-mcp-server.js +25 -7
  65. package/build/dts/wixel/constants.d.ts +8 -1
  66. package/build/dts/wixel/constants.js +8 -1
  67. package/build/dts/wixel/index.d.ts +1 -1
  68. package/build/dts/wixel/index.js +2 -2
  69. package/build/dts/wixel/wixel-a2a-client.d.ts +83 -0
  70. package/build/dts/wixel/wixel-a2a-client.js +166 -0
  71. package/build/dts/wixel/wixel-bi.d.ts +2 -1
  72. package/build/dts/wixel/wixel-bi.js +2 -1
  73. package/build/dts/wixel/{external-agent-api.d.ts → wixel-session-entries.d.ts} +11 -33
  74. package/build/dts/wixel/{external-agent-api.js → wixel-session-entries.js} +33 -54
  75. package/build/dts/wixel/wixel-session-tools.d.ts +21 -26
  76. package/build/dts/wixel/wixel-session-tools.js +494 -599
  77. package/build/esm/index.js +2824 -1170
  78. package/build/esm/toolkit.js +1786 -360
  79. package/package.json +4 -4
@@ -2,15 +2,16 @@ import { z } from 'zod';
2
2
  import { runSemanticSearchAndFormat } from './semanticSearch.js';
3
3
  import { logger } from '../infra/logger.js';
4
4
  import { captureException } from '@sentry/node';
5
- import { fetchArticleContent, isSkillsRegistryUrl } from './fetch-article.js';
5
+ import { fetchArticleContent, fetchSourceContent, isSkillsRegistryUrl, parseSourceContentUrl } from './fetch-article.js';
6
6
  import { normalizeMenuContent, summarizeLargeMenu, fetchMenuContent, MENU_CONTENT_THRESHOLD } from './normalize-menu-content.js';
7
7
  import { fetchPlatformizedMenu, MenuNotFoundError } from './browse-menu.js';
8
8
  import { isQueryOrSearchEndpoint, QUERY_CAPABILITY_MARKER } from './searchFormatting.js';
9
9
  import { addGetToKnowTools, addGetSiteContextTool } from './get-to-know.js';
10
10
  import { addStartHeadlessBuildTool } from './start-headless-build.js';
11
11
  import { addStartDeployWebsiteTool } from './start-deploy-website.js';
12
- import { defaultToolDescriptions } from '../config/default-config.js';
13
- import { desc } from '../config/param-descriptions.js';
12
+ import { defaultToolDescriptions, HELP_CENTER_IS_FOR_PEOPLE } from '../config/default-config.js';
13
+ import { desc as paramDescriptionsOf } from '../config/param-descriptions.js';
14
+ import { isMintlifyDocsSite, isMintlifyUrl, MINTLIFY_URL_PREFIXES, mintlifyDocumentTypes, readMintlifyMarkdown, rewriteDocsLinks, toDevWixUrl, toMintlifyHostText } from './mintlify-docs.js';
14
15
  import dedent from 'dedent';
15
16
  export const VALID_DOCS_TOOLS = [
16
17
  'WDS',
@@ -21,7 +22,8 @@ export const VALID_DOCS_TOOLS = [
21
22
  'VELO',
22
23
  'BUSINESS_SOLUTIONS',
23
24
  'CLI',
24
- 'OVERVIEW'
25
+ 'OVERVIEW',
26
+ 'HELP_CENTER'
25
27
  ];
26
28
  const REST_SEARCH_API_GROUNDING_REMINDER = dedent `
27
29
  # Before calling a Wix API
@@ -66,18 +68,59 @@ export const addDocsTools = (server, allowedTools = [
66
68
  'WIX_HEADLESS',
67
69
  'BUSINESS_SOLUTIONS',
68
70
  'CLI',
69
- 'OVERVIEW'
71
+ 'OVERVIEW',
72
+ 'HELP_CENTER'
70
73
  ], options = {}) => {
71
- const { getToKnowWixEnabled = false, startDeployWebsiteEnabled = true, startHeadlessBuildEnabled = true, disableTools, toolDescriptions, readmeDocs, authStrategy, skillsPortalUrl, skillsRepo, repo, skillsPr, commitHash, skillsTags, dynamicContextEnabled, usePlatformizedSearch = true, usePlatformizedBrowse = true } = options;
74
+ const { getToKnowWixEnabled = false, startDeployWebsiteEnabled = true, startHeadlessBuildEnabled = true, disableTools, toolDescriptions, readmeDocs, authStrategy, skillsPortalUrl, skillsRepo, repo, skillsPr, commitHash, skillsTags, readmeRecipesIndexEnabled, dynamicContextEnabled, includeCompanionApp, usePlatformizedSearch = true, usePlatformizedBrowse = true, docsSite } = options;
72
75
  const effectiveRepo = repo ?? skillsRepo;
73
76
  const effectiveCommitHash = commitHash ?? skillsPr;
77
+ // Mintlify docs: search as today, then resolve the links in the results.
78
+ const mintlifyDocs = isMintlifyDocsSite(docsSite);
79
+ const runSearch = mintlifyDocs
80
+ ? async (params) => {
81
+ // Stage 2 (docsSite=mintlify): REST and SDK search the Mintlify KBs.
82
+ const documentTypes = mintlifyDocumentTypes(docsSite, params.toolName);
83
+ const result = await runSemanticSearchAndFormat(documentTypes ? { ...params, documentTypes } : params);
84
+ return (await rewriteDocsLinks(result, { label: `search ${params.toolName}` })).text;
85
+ }
86
+ : runSemanticSearchAndFormat;
87
+ const docsUrlPrefixes = mintlifyDocs
88
+ ? ['https://dev.wix.com/docs/', ...MINTLIFY_URL_PREFIXES]
89
+ : ['https://dev.wix.com/docs/'];
90
+ // The backends only know dev.wix.com; Mintlify paths map back one to one.
91
+ const toBackendUrl = (url) => mintlifyDocs && isMintlifyUrl(url) ? toDevWixUrl(url) : url;
92
+ // Mintlify docs: a separate text block naming where a read's content came from.
93
+ const sourceNote = (source) => mintlifyDocs
94
+ ? [{ type: 'text', text: `[docs source] ${source}` }]
95
+ : [];
96
+ // Mintlify docs: tool and parameter texts show docs URLs on the Mintlify host, or
97
+ // the model cites dev.wix.com (it copies the examples' host into answers).
98
+ const docsText = (text) => mintlifyDocs ? toMintlifyHostText(text) : text;
99
+ const desc = (toolName) => {
100
+ const descriptions = paramDescriptionsOf(toolName);
101
+ if (!mintlifyDocs)
102
+ return descriptions;
103
+ return Object.fromEntries(Object.entries(descriptions).map(([param, text]) => [
104
+ param,
105
+ docsText(text)
106
+ ]));
107
+ };
74
108
  // Helper to get description (custom or default)
75
- const getDescription = (toolName, defaultDesc) => toolDescriptions?.[toolName] ?? defaultDesc;
109
+ const getDescription = (toolName, defaultDesc) => docsText(toolDescriptions?.[toolName] ?? defaultDesc);
76
110
  // Menu text for a docs URL, shared by BrowseWixRESTDocsMenu and the
77
111
  // ReadFullDocs* category fallbacks. Platformized browse first — its 404 is a
78
112
  // real answer (null, no such menu); any other failure falls back to the
79
113
  // legacy get-menu-content scrape, which keeps its original thresholding.
80
114
  const fetchMenuTextForUrl = async (httpClient, url) => {
115
+ const text = await fetchMenuTextForBackendUrl(httpClient, toBackendUrl(url));
116
+ if (!text || !mintlifyDocs)
117
+ return text;
118
+ // Menu links are Portal Service nodes, the same tree the Mintlify
119
+ // navigation is built from, so they map by rule without a page check.
120
+ return (await rewriteDocsLinks(text, { verify: false, label: 'menu' }))
121
+ .text;
122
+ };
123
+ const fetchMenuTextForBackendUrl = async (httpClient, url) => {
81
124
  if (usePlatformizedBrowse) {
82
125
  try {
83
126
  return await fetchPlatformizedMenu(url);
@@ -96,16 +139,36 @@ export const addDocsTools = (server, allowedTools = [
96
139
  ? summarizeLargeMenu(menuContent, url)
97
140
  : normalizeMenuContent(menuContent);
98
141
  };
142
+ // The two site-creation guides register first, so they head the tool list: an agent that
143
+ // loads every definition at once and gets a cut-down copy keeps the start and the end.
144
+ // On by default: they are routing slots, not docs sources.
145
+ if (startDeployWebsiteEnabled) {
146
+ addStartDeployWebsiteTool(server, disableTools || [], toolDescriptions, {
147
+ skillsPortalUrl,
148
+ repo: effectiveRepo,
149
+ commitHash: effectiveCommitHash
150
+ });
151
+ }
152
+ // Same reach as StartDeployWebsite: the slot for a site that needs Wix business features.
153
+ if (startHeadlessBuildEnabled) {
154
+ addStartHeadlessBuildTool(server, disableTools || [], toolDescriptions, {
155
+ skillsPortalUrl,
156
+ repo: effectiveRepo,
157
+ commitHash: effectiveCommitHash
158
+ });
159
+ }
99
160
  if (dynamicContextEnabled &&
100
161
  authStrategy &&
101
162
  !disableTools?.includes('GetSiteContext')) {
102
163
  addGetSiteContextTool({
103
164
  server,
104
165
  authStrategy,
166
+ includeCompanionApp,
105
167
  toolDescriptions,
106
168
  disableTools: disableTools || []
107
169
  });
108
170
  }
171
+ // Named in SERVER_INSTRUCTIONS (config/default-config.ts): when this tool's name or what it is for changes, update them to match.
109
172
  if (getToKnowWixEnabled && !disableTools?.includes('WixREADME')) {
110
173
  addGetToKnowTools({
111
174
  server,
@@ -117,24 +180,9 @@ export const addDocsTools = (server, allowedTools = [
117
180
  dynamicContextEnabled,
118
181
  repo: effectiveRepo,
119
182
  commitHash: effectiveCommitHash,
120
- skillsTags
121
- });
122
- }
123
- // On by default: it is a routing slot, not a docs source — the agent with
124
- // ready-made site files must find it next to the site-creation tools.
125
- if (startDeployWebsiteEnabled) {
126
- addStartDeployWebsiteTool(server, disableTools || [], toolDescriptions, {
127
- skillsPortalUrl,
128
- repo: effectiveRepo,
129
- commitHash: effectiveCommitHash
130
- });
131
- }
132
- // Same reach as StartDeployWebsite: the slot for a site built in code with no code yet.
133
- if (startHeadlessBuildEnabled) {
134
- addStartHeadlessBuildTool(server, disableTools || [], toolDescriptions, {
135
- skillsPortalUrl,
136
- repo: effectiveRepo,
137
- commitHash: effectiveCommitHash
183
+ skillsTags,
184
+ readmeRecipesIndexEnabled,
185
+ docsSite
138
186
  });
139
187
  }
140
188
  // WDS Documentation
@@ -155,7 +203,7 @@ export const addDocsTools = (server, allowedTools = [
155
203
  })(), { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async ({ searchTerm, maxResults }, { panorama }) => {
156
204
  try {
157
205
  logger.log(`Searching for ${searchTerm} in Wix WDS`);
158
- const result = await runSemanticSearchAndFormat({
206
+ const result = await runSearch({
159
207
  toolName: 'WDS',
160
208
  toolParams: {
161
209
  searchTerm: searchTerm
@@ -207,7 +255,7 @@ export const addDocsTools = (server, allowedTools = [
207
255
  })(), { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async ({ searchTerm, maxResults, reason }, { panorama }) => {
208
256
  try {
209
257
  logger.log(`[SearchWixRESTDocumentation] searchTerm="${searchTerm}", reason="${reason}"`);
210
- const result = await runSemanticSearchAndFormat({
258
+ const result = await runSearch({
211
259
  toolName: 'REST',
212
260
  toolParams: {
213
261
  searchTerm: searchTerm
@@ -270,7 +318,7 @@ export const addDocsTools = (server, allowedTools = [
270
318
  })(), { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async ({ searchTerm, maxResults }, { panorama }) => {
271
319
  try {
272
320
  logger.log(`Searching for ${searchTerm} in Wix Sample Flows`);
273
- const result = await runSemanticSearchAndFormat({
321
+ const result = await runSearch({
274
322
  toolName: 'BUSINESS_SOLUTIONS',
275
323
  toolParams: {
276
324
  searchTerm: '**RECIPE**: Business Recipes - full setup flow for::: ' +
@@ -328,7 +376,7 @@ export const addDocsTools = (server, allowedTools = [
328
376
  panorama
329
377
  .transaction('SearchWixSDKDocumentation')
330
378
  .start({ maxResults });
331
- const result = await runSemanticSearchAndFormat({
379
+ const result = await runSearch({
332
380
  toolName: 'SDK',
333
381
  toolParams: {
334
382
  searchTerm: searchTerm
@@ -386,7 +434,7 @@ export const addDocsTools = (server, allowedTools = [
386
434
  })(), { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async ({ searchTerm, maxResults }, { panorama }) => {
387
435
  try {
388
436
  logger.log(`Searching for ${searchTerm} in Build Apps`);
389
- const result = await runSemanticSearchAndFormat({
437
+ const result = await runSearch({
390
438
  toolName: 'BUILD_APPS',
391
439
  toolParams: {
392
440
  searchTerm: `wix cli docs for ${searchTerm}`
@@ -436,7 +484,7 @@ export const addDocsTools = (server, allowedTools = [
436
484
  })(), { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async ({ searchTerm, maxResults }, { panorama }) => {
437
485
  try {
438
486
  logger.log(`Searching for ${searchTerm} in Wix Overview`);
439
- const result = await runSemanticSearchAndFormat({
487
+ const result = await runSearch({
440
488
  toolName: 'OVERVIEW',
441
489
  toolParams: {
442
490
  searchTerm: searchTerm
@@ -486,7 +534,7 @@ export const addDocsTools = (server, allowedTools = [
486
534
  })(), { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async ({ searchTerm, maxResults }, { panorama }) => {
487
535
  try {
488
536
  logger.log(`Searching for ${searchTerm} in Headless`);
489
- const result = await runSemanticSearchAndFormat({
537
+ const result = await runSearch({
490
538
  toolName: 'WIX_HEADLESS',
491
539
  toolParams: {
492
540
  searchTerm: searchTerm
@@ -542,7 +590,7 @@ export const addDocsTools = (server, allowedTools = [
542
590
  panorama
543
591
  .transaction('SearchWixVeloDocumentation')
544
592
  .start({ maxResults });
545
- const result = await runSemanticSearchAndFormat({
593
+ const result = await runSearch({
546
594
  toolName: 'VELO',
547
595
  toolParams: {
548
596
  searchTerm: searchTerm
@@ -583,6 +631,66 @@ export const addDocsTools = (server, allowedTools = [
583
631
  });
584
632
  }
585
633
  // CLI Documentation
634
+ // Help Center (support.wix.com): how site owners do things in the Wix
635
+ // dashboard and editors. Articles are long and prose-only, so fewer results
636
+ // with a larger line budget than the developer docs tools.
637
+ if (allowedTools.includes('HELP_CENTER') &&
638
+ !disableTools?.includes('SearchWixHelpCenter')) {
639
+ server.tool('SearchWixHelpCenter', getDescription('SearchWixHelpCenter', defaultToolDescriptions.SearchWixHelpCenter ?? ''), (() => {
640
+ const d = desc('SearchWixHelpCenter');
641
+ return {
642
+ searchTerm: z.string().describe(d.searchTerm),
643
+ maxResults: z
644
+ .number()
645
+ .describe(d.maxResults)
646
+ .min(1)
647
+ .max(15)
648
+ .optional()
649
+ .default(15),
650
+ reason: z.string().describe(d.reason)
651
+ };
652
+ })(), { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async ({ searchTerm, maxResults, reason }, { panorama }) => {
653
+ try {
654
+ logger.log(`[SearchWixHelpCenter] searchTerm="${searchTerm}", reason="${reason}"`);
655
+ const result = await runSemanticSearchAndFormat({
656
+ toolName: 'HELP_CENTER',
657
+ toolParams: {
658
+ searchTerm: searchTerm
659
+ },
660
+ maxResults: Math.max(1, Math.min(maxResults ?? 15, 15)),
661
+ linesInEachResult: 60,
662
+ usePlatformizedSearch
663
+ });
664
+ return {
665
+ content: [
666
+ {
667
+ type: 'text',
668
+ text: `${HELP_CENTER_IS_FOR_PEOPLE}\n\n${result}`
669
+ }
670
+ ]
671
+ };
672
+ }
673
+ catch (error) {
674
+ panorama.errorMonitor().reportError(error);
675
+ captureException(error, {
676
+ tags: {
677
+ componentId: 'SearchWixHelpCenter',
678
+ toolName: 'SearchWixHelpCenter'
679
+ }
680
+ });
681
+ logger.error(`Error searching for ${searchTerm} in the Wix Help Center: ${error}`);
682
+ return {
683
+ isError: true,
684
+ content: [
685
+ {
686
+ type: 'text',
687
+ text: `Error: ${error.message}\n\n${HELP_CENTER_IS_FOR_PEOPLE}`
688
+ }
689
+ ]
690
+ };
691
+ }
692
+ });
693
+ }
586
694
  if (allowedTools.includes('CLI') &&
587
695
  !disableTools?.includes('SearchWixCLIDocumentation')) {
588
696
  server.tool('SearchWixCLIDocumentation', getDescription('SearchWixCLIDocumentation', defaultToolDescriptions.SearchWixCLIDocumentation ?? ''), (() => {
@@ -601,7 +709,7 @@ export const addDocsTools = (server, allowedTools = [
601
709
  })(), { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async ({ searchTerm, maxResults, reason }, { panorama }) => {
602
710
  try {
603
711
  logger.log(`[SearchWixCLIDocumentation] searchTerm="${searchTerm}", reason="${reason}"`);
604
- const result = await runSemanticSearchAndFormat({
712
+ const result = await runSearch({
605
713
  toolName: 'CLI',
606
714
  toolParams: {
607
715
  searchTerm: searchTerm
@@ -642,9 +750,32 @@ export const addDocsTools = (server, allowedTools = [
642
750
  articleUrl: z.string().url().describe(d.articleUrl)
643
751
  };
644
752
  })(), { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async ({ articleUrl }, { httpClient }) => {
645
- // URL validation: known docs prefixes, or a Wix skills registry page
753
+ // A file with no docs page, such as a skill kept out of the public
754
+ // mirror, is linked as get-source-content and read from its repository.
755
+ const source = parseSourceContentUrl(articleUrl);
756
+ if (source) {
757
+ try {
758
+ const text = await fetchSourceContent(httpClient, source.repo, source.path, source.commitHash);
759
+ return { content: [{ type: 'text', text }] };
760
+ }
761
+ catch (error) {
762
+ logger.error(`Error fetching source content for ${articleUrl}: ${error}`);
763
+ return {
764
+ isError: true,
765
+ content: [
766
+ {
767
+ type: 'text',
768
+ text: formatDocsReadError('source content', error)
769
+ }
770
+ ]
771
+ };
772
+ }
773
+ }
774
+ // URL validation: known docs prefixes, a Help Center article
775
+ // (get-article-content reads support.wix.com/<locale>/article/<slug>),
776
+ // or a Wix skills registry page
646
777
  // (https://www.wix.com/skills/..., https://dev.wix.com/docs/skills/...).
647
- const validPrefixes = ['https://dev.wix.com/docs/'];
778
+ const validPrefixes = [...docsUrlPrefixes, 'https://support.wix.com/'];
648
779
  if (skillsPortalUrl)
649
780
  validPrefixes.push(skillsPortalUrl);
650
781
  const isSkillsPage = isSkillsRegistryUrl(articleUrl);
@@ -655,7 +786,7 @@ export const addDocsTools = (server, allowedTools = [
655
786
  content: [
656
787
  {
657
788
  type: 'text',
658
- 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}"`
789
+ text: `Invalid article URL: must start with https://dev.wix.com/docs/ or https://support.wix.com/, or be a Wix skills page (https://www.wix.com/skills/..., https://www.wix.com/headless/..., or that file's github.com/wix/skills source). Got: "${articleUrl}"`
659
790
  }
660
791
  ]
661
792
  };
@@ -668,6 +799,30 @@ export const addDocsTools = (server, allowedTools = [
668
799
  articleUrl.startsWith('https://dev.wix.com/docs/')) {
669
800
  articleUrl = articleUrl.replace(/\.md$/, '');
670
801
  }
802
+ // Mintlify docs: read the page from the Mintlify site when it has one,
803
+ // else fall through to today's backend with the dev.wix.com URL.
804
+ if (mintlifyDocs && !isSkillsPage) {
805
+ try {
806
+ const page = await readMintlifyMarkdown(articleUrl);
807
+ if (page) {
808
+ return {
809
+ content: [
810
+ {
811
+ type: 'text',
812
+ text: isQueryOrSearchEndpoint(page.url)
813
+ ? [page.markdown, ARTICLE_CAPABILITY_NOTE].join('\n\n')
814
+ : page.markdown
815
+ },
816
+ ...sourceNote(`Mintlify site: ${page.url}.md`)
817
+ ]
818
+ };
819
+ }
820
+ }
821
+ catch (error) {
822
+ logger.error(`[mintlify] reading ${articleUrl} from Mintlify failed, falling back: ${error}`);
823
+ }
824
+ articleUrl = toBackendUrl(articleUrl);
825
+ }
671
826
  try {
672
827
  const articleContent = await fetchArticleContent(httpClient, articleUrl, 'article', false, skillsPortalUrl, effectiveRepo, effectiveCommitHash);
673
828
  if (!articleContent) {
@@ -682,7 +837,10 @@ export const addDocsTools = (server, allowedTools = [
682
837
  text: isQueryOrSearchEndpoint(articleUrl)
683
838
  ? [articleContent, ARTICLE_CAPABILITY_NOTE].join('\n\n')
684
839
  : articleContent
685
- }
840
+ },
841
+ ...sourceNote(isSkillsPage
842
+ ? `skills registry: ${articleUrl}`
843
+ : `today's backend (dev.wix.com/rawdocs, ingvar), not on the Mintlify site: ${articleUrl}`)
686
844
  ]
687
845
  };
688
846
  }
@@ -728,7 +886,7 @@ export const addDocsTools = (server, allowedTools = [
728
886
  };
729
887
  })(), { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async ({ articleUrl, reason }, { httpClient }) => {
730
888
  // URL validation: only allow known docs prefixes
731
- const validPrefixes = ['https://dev.wix.com/docs/'];
889
+ const validPrefixes = [...docsUrlPrefixes];
732
890
  if (skillsPortalUrl)
733
891
  validPrefixes.push(skillsPortalUrl);
734
892
  if (!validPrefixes.some((p) => articleUrl.startsWith(p))) {
@@ -746,18 +904,28 @@ export const addDocsTools = (server, allowedTools = [
746
904
  if (articleUrl.startsWith('https://dev.wix.com/docs/')) {
747
905
  articleUrl = articleUrl.replace(/\.md$/, '');
748
906
  }
907
+ // Mintlify docs: schemas stay on PAPI through ingvar, by dev.wix.com URL.
908
+ articleUrl = toBackendUrl(articleUrl);
749
909
  try {
750
910
  logger.log(`[ReadFullDocsMethodSchema] url="${articleUrl}", reason="${reason}"`);
751
911
  const articleContent = await fetchArticleContent(httpClient, articleUrl, 'methodSchema', false, skillsPortalUrl, effectiveRepo, effectiveCommitHash, true);
752
912
  if (!articleContent) {
753
913
  throw new Error('Method schema is empty');
754
914
  }
915
+ // Mintlify docs: the schema text links other methods on dev.wix.com;
916
+ // resolve those to their Mintlify pages, as in search results.
917
+ const schemaText = mintlifyDocs
918
+ ? (await rewriteDocsLinks(articleContent, {
919
+ label: 'schema ReadFullDocsMethodSchema'
920
+ })).text
921
+ : articleContent;
755
922
  return {
756
923
  content: [
757
924
  {
758
925
  type: 'text',
759
- text: articleContent
760
- }
926
+ text: schemaText
927
+ },
928
+ ...sourceNote(`PAPI, through today's backend (dev.wix.com/rawdocs, ingvar): ${articleUrl}`)
761
929
  ]
762
930
  };
763
931
  }
@@ -803,7 +971,7 @@ export const addDocsTools = (server, allowedTools = [
803
971
  };
804
972
  })(), { readOnlyHint: true, destructiveHint: false, openWorldHint: false }, async ({ menuUrl, reason }, { httpClient }) => {
805
973
  try {
806
- if (menuUrl && !menuUrl.startsWith('https://dev.wix.com/docs/')) {
974
+ if (menuUrl && !docsUrlPrefixes.some((p) => menuUrl.startsWith(p))) {
807
975
  return {
808
976
  content: [
809
977
  {
@@ -36,5 +36,26 @@ export declare function fetchRecipesIndex(): Promise<string>;
36
36
  */
37
37
  export declare function fetchArticle(articleUrl: string): Promise<string>;
38
38
  export declare function isSkillsRegistryUrl(url: string): boolean;
39
+ /**
40
+ * The forms of a skills URL agents hold that the registry serves under another
41
+ * name: wix.com without www (a 301 the fetch client does not follow), and the
42
+ * GitHub source of the same file. Both map to the www.wix.com/skills page.
43
+ */
44
+ export declare function normalizeSkillsUrl(url: string): string;
45
+ /**
46
+ * A file read from its source repository rather than the published docs, via
47
+ * the rawdocs get-source-content API (e.g. the management index as authored in
48
+ * wix-private/ax-skills). The API serves only the files it allows.
49
+ */
50
+ /**
51
+ * The repo and path a get-source-content URL names: what a file served from its
52
+ * source repository links a file that has no docs page to.
53
+ */
54
+ export declare function parseSourceContentUrl(url: string): {
55
+ repo: string;
56
+ path: string;
57
+ commitHash?: string;
58
+ } | undefined;
59
+ export declare function fetchSourceContent(httpClient: HttpClient, repo: string, path: string, commitHash?: string): Promise<string>;
39
60
  export declare function fetchArticleContent(httpClient: HttpClient, articleUrl: string, mode: 'article' | 'methodSchema' | 'raw', stripHeader?: boolean, skillsPortalUrl?: string, repo?: string, commitHash?: string, fetchMcpWhiteList?: boolean): Promise<string>;
40
61
  //# sourceMappingURL=fetch-article.d.ts.map
@@ -1,6 +1,6 @@
1
1
  import { HttpClient } from '@wix/http-client';
2
2
  import { logger } from '../infra/logger.js';
3
- import { GET_ARTICLE_CONTENT_URL } from '../constants.js';
3
+ import { GET_ARTICLE_CONTENT_URL, GET_SOURCE_CONTENT_URL } from '../constants.js';
4
4
  // ---------------------------------------------------------------------------
5
5
  // Public constants and utilities
6
6
  // ---------------------------------------------------------------------------
@@ -62,7 +62,22 @@ const SKILLS_REGISTRY_URL = new RegExp('^https://(?:dev\\.wix\\.com(?:/docs)?|ww
62
62
  // served as raw markdown like the registry, and read the same way.
63
63
  '|^https://www\\.wix\\.com/headless/');
64
64
  export function isSkillsRegistryUrl(url) {
65
- return SKILLS_REGISTRY_URL.test(url.trim());
65
+ return SKILLS_REGISTRY_URL.test(normalizeSkillsUrl(url));
66
+ }
67
+ // A GitHub view of the skills repository, as an installed plugin cross-references it:
68
+ // github.com/wix/skills/blob/<ref>/skills/<skill>/<path>, with or without a line anchor.
69
+ const GITHUB_SKILLS_URL = /^https:\/\/(?:www\.)?github\.com\/wix\/skills\/(?:blob|raw|tree)\/[^/]+\/skills\/([^#?]+)/;
70
+ /**
71
+ * The forms of a skills URL agents hold that the registry serves under another
72
+ * name: wix.com without www (a 301 the fetch client does not follow), and the
73
+ * GitHub source of the same file. Both map to the www.wix.com/skills page.
74
+ */
75
+ export function normalizeSkillsUrl(url) {
76
+ const apex = url
77
+ .trim()
78
+ .replace(/^https:\/\/wix\.com\//, 'https://www.wix.com/');
79
+ const gh = apex.match(GITHUB_SKILLS_URL);
80
+ return gh ? `https://www.wix.com/skills/${gh[1]}` : apex;
66
81
  }
67
82
  async function fetchSkillsRegistryPage(httpClient, url) {
68
83
  let data;
@@ -173,6 +188,53 @@ async function fetchArticleContentFromDigor(httpClient, articleUrl, mode, stripH
173
188
  }
174
189
  return articleContent;
175
190
  }
191
+ /**
192
+ * A file read from its source repository rather than the published docs, via
193
+ * the rawdocs get-source-content API (e.g. the management index as authored in
194
+ * wix-private/ax-skills). The API serves only the files it allows.
195
+ */
196
+ /**
197
+ * The repo and path a get-source-content URL names: what a file served from its
198
+ * source repository links a file that has no docs page to.
199
+ */
200
+ export function parseSourceContentUrl(url) {
201
+ let parsed;
202
+ try {
203
+ parsed = new URL(url);
204
+ }
205
+ catch {
206
+ return undefined;
207
+ }
208
+ if (`${parsed.origin}${parsed.pathname}` !== GET_SOURCE_CONTENT_URL) {
209
+ return undefined;
210
+ }
211
+ const repo = parsed.searchParams.get('repo');
212
+ const path = parsed.searchParams.get('path');
213
+ const commitHash = parsed.searchParams.get('commitHash');
214
+ if (!repo || !path)
215
+ return undefined;
216
+ return commitHash ? { repo, path, commitHash } : { repo, path };
217
+ }
218
+ export async function fetchSourceContent(httpClient, repo, path, commitHash) {
219
+ const url = new URL(GET_SOURCE_CONTENT_URL);
220
+ url.searchParams.set('repo', repo);
221
+ url.searchParams.set('path', path);
222
+ if (commitHash)
223
+ url.searchParams.set('commitHash', commitHash);
224
+ let data;
225
+ try {
226
+ ({ data } = await httpClient.get(url.toString()));
227
+ }
228
+ catch (error) {
229
+ const { message, status } = getHttpErrorDetails(error);
230
+ const statusSuffix = status ? ` (status ${status})` : '';
231
+ throw new Error(`${message}${statusSuffix}`);
232
+ }
233
+ if (!data?.content) {
234
+ throw new Error('Source content is empty');
235
+ }
236
+ return data.content;
237
+ }
176
238
  export async function fetchArticleContent(httpClient, articleUrl, mode, stripHeader = false, skillsPortalUrl, repo, commitHash, fetchMcpWhiteList) {
177
239
  // Apply portal URL override if configured
178
240
  const effectiveUrl = applyPortalUrlOverride(articleUrl, skillsPortalUrl);
@@ -190,12 +252,12 @@ export async function fetchArticleContent(httpClient, articleUrl, mode, stripHea
190
252
  // Fall through to the normal fetch for this URL: the registry for a
191
253
  // skills page (rawdocs cannot serve those), rawdocs otherwise.
192
254
  content = isSkillsRegistryUrl(articleUrl)
193
- ? await fetchSkillsRegistryPage(httpClient, articleUrl)
255
+ ? await fetchSkillsRegistryPage(httpClient, normalizeSkillsUrl(articleUrl))
194
256
  : await fetchArticleContentFromDigor(httpClient, articleUrl, mode, stripHeader);
195
257
  }
196
258
  }
197
259
  else if (isSkillsRegistryUrl(articleUrl)) {
198
- content = await fetchSkillsRegistryPage(httpClient, articleUrl);
260
+ content = await fetchSkillsRegistryPage(httpClient, normalizeSkillsUrl(articleUrl));
199
261
  }
200
262
  else if (mode === 'raw') {
201
263
  let parsedUrl;
@@ -28,14 +28,19 @@ export interface AddGetToKnowTools {
28
28
  repo?: string;
29
29
  commitHash?: string;
30
30
  skillsTags?: string[];
31
+ readmeRecipesIndexEnabled?: boolean;
32
+ /** The connection's docsSite (see ./mintlify-docs.ts): a Mintlify mode resolves the README's docs links. */
33
+ docsSite?: string;
31
34
  }
32
35
  interface AddGetSiteContextTool {
33
36
  server: WixMcpServer;
34
37
  authStrategy: McpAuthenticationStrategy;
35
38
  partnerContextEnabled?: boolean;
39
+ /** Render the site's companion app. Off by default: only Aria (via the toolkit) needs it. */
40
+ includeCompanionApp?: boolean;
36
41
  toolDescriptions?: Record<string, string>;
37
42
  disableTools?: string[];
38
43
  }
39
- export declare function addGetSiteContextTool({ server, authStrategy, toolDescriptions }: AddGetSiteContextTool): void;
40
- export declare function addGetToKnowTools({ server, disableTools, toolDescriptions, readmeDocs, authStrategy, skillsPortalUrl, dynamicContextEnabled, repo, commitHash, skillsTags }: AddGetToKnowTools): Promise<void>;
44
+ export declare function addGetSiteContextTool({ server, authStrategy, includeCompanionApp, toolDescriptions }: AddGetSiteContextTool): void;
45
+ export declare function addGetToKnowTools({ server, disableTools, toolDescriptions, readmeDocs, authStrategy, skillsPortalUrl, dynamicContextEnabled, repo, commitHash, skillsTags, readmeRecipesIndexEnabled, docsSite }: AddGetToKnowTools): Promise<void>;
41
46
  //# sourceMappingURL=get-to-know.d.ts.map