@wix/mcp 1.0.0 → 1.0.2

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.
package/README.md CHANGED
@@ -18,7 +18,7 @@ args: ["/Users/absolute/path/to/repo/dir/."]
18
18
 
19
19
  ```
20
20
  command: node
21
- args: ["/Users/absolute/path/to/build/index.js"]
21
+ args: ["/Users/absolute/path/to/build/bin.js"]
22
22
  ```
23
23
 
24
24
  # Optional config arguments:
@@ -29,6 +29,9 @@ args: ["/Users/absolute/path/to/build/index.js"]
29
29
  ### enable experimental tools:
30
30
  `--experimental=WIX_API,CLI_COMMAND` - enable specific experimental tools (disabled by default)
31
31
 
32
+ ### choose a CLI mode:
33
+ `--cli=wix-one` - specifies to use Wix One CLI mode (if not specified, default CLI mode is used)
34
+
32
35
  ### choose a logger:
33
36
  `--logger=mcp` - log to MCP server (default)
34
37
  `--logger=file` - log to file (in `~/wix-mcp-log.txt`)
@@ -63,6 +66,11 @@ It's exactly parallel to the real docs url, for example:
63
66
  Docs URL: https://dev.wix.com/docs/picasso/wix-ai-docs/best-practices/sdk-best-practices
64
67
  Resource URI: wix-docs://picasso/wix-ai-docs/best-practices/sdk-best-practices
65
68
 
69
+ pass:
70
+ `--portals=<docs-portal-name>`
71
+ to load all docs from the portal as resources
72
+
73
+
66
74
  # Cursor
67
75
 
68
76
  ## Cursor MCP docs:
@@ -78,9 +86,10 @@ https://docs.cursor.com/context/model-context-protocol
78
86
  ```
79
87
  {
80
88
  "mcpServers": {
81
- "IGOR_TOOLS": {
89
+ "wix-local-mcp": {
82
90
  "command": "npx",
83
91
  "args": [
92
+ "-y",
84
93
  "@wix/mcp"
85
94
  ]
86
95
  }
@@ -92,7 +101,7 @@ https://docs.cursor.com/context/model-context-protocol
92
101
  ```
93
102
  {
94
103
  "mcpServers": {
95
- "IGOR_TOOLS": {
104
+ "wix-local-mcp": {
96
105
  "command": "npx",
97
106
  "args": [
98
107
  "/Users/absolute/path/to/repo/dir/."
@@ -110,10 +119,10 @@ https://docs.cursor.com/context/model-context-protocol
110
119
  ```
111
120
  {
112
121
  "mcpServers": {
113
- "IGOR_TOOLS": {
122
+ "wix-local-mcp": {
114
123
  "command": "node",
115
124
  "args": [
116
- "/Users/absolute/path/to/build/index.js"
125
+ "/Users/absolute/path/to/build/bin.js"
117
126
  ]
118
127
  }
119
128
  }
@@ -123,7 +132,9 @@ https://docs.cursor.com/context/model-context-protocol
123
132
  ### You can try using bun + index.ts
124
133
 
125
134
  # Troubleshooting
126
-
135
+ - check the logs from claude desktop / cursor
136
+ - is the error related to node / fnm / nvm ? make sure you have the latest node version as the default on in the path
137
+ - is the error related to npx? make sure you use -y and the correct npm registry
127
138
  - try including full path to node
128
139
  - try using bun and index.ts directly
129
140
  - make sure build files have permissions for cursor to access
package/bin.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import './build/index.js';
2
+ import './build/bin.js';
@@ -0,0 +1,2 @@
1
+ import { WixMcpServer } from '../wix-mcp-server.js';
2
+ export declare function addApiCallTool(server: WixMcpServer, getSiteAccessToken: (siteId: string) => Promise<string>, getAccountAccessToken: () => Promise<string>): void;
@@ -0,0 +1,118 @@
1
+ import { z } from 'zod';
2
+ import { logger } from '../logger.js';
3
+ import { handleWixAPIResponse } from '../tool-utils.js';
4
+ export function addApiCallTool(server, getSiteAccessToken, getAccountAccessToken) {
5
+ server.tool('CallWixSiteAPI', 'Call Wix apis on a business or site. Use this to create, read, update, and delete data and other Wix entities in your Wix site,' +
6
+ `You should ALWAYS check the rest docs - "SearchWixRESTDocumentation" for the specific API you want to call, don't just call it without knowing what it does, CHECK THE DOCS`, {
7
+ siteId: z
8
+ .string()
9
+ .describe('The id of the site selected using site selection tool'),
10
+ url: z
11
+ .string()
12
+ .describe('The url of the api to call - ALWAYS get the information from the Wix REST docs or from the conversation context, the URL MUST BE ABSOLUTE URL'),
13
+ method: z
14
+ .string()
15
+ .describe('The HTTP method to use for the API call (e.g. GET, POST, PUT, DELETE)'),
16
+ body: z
17
+ .string()
18
+ .optional()
19
+ .describe('A string representing of a valid JSON object to describe the body of the request')
20
+ }, async ({ url, body, method, siteId }) => {
21
+ logger.log(`Calling Wix Site API: ${siteId} ${url}, body: ${JSON.stringify(body)}`);
22
+ const authorization = await getSiteAccessToken(siteId);
23
+ try {
24
+ const response = await fetch(url, {
25
+ method,
26
+ headers: {
27
+ Authorization: authorization,
28
+ ...(body ? { 'Content-Type': 'application/json' } : {})
29
+ },
30
+ body: method === 'GET' ? undefined : body
31
+ });
32
+ const responseData = await handleWixAPIResponse(response);
33
+ return {
34
+ content: [
35
+ {
36
+ type: 'text',
37
+ text: `Wix Site API call successful: ${JSON.stringify(responseData)}`
38
+ }
39
+ ]
40
+ };
41
+ }
42
+ catch (error) {
43
+ logger.error(`Failed to call Wix Site API: ${error}`);
44
+ throw new Error(`Failed to call Wix Site API: ${error}`);
45
+ }
46
+ });
47
+ server.tool('ListWixSites', 'List Wix sites for the current user', async () => {
48
+ const sitesRes = await fetch('https://www.wixapis.com/site-list/v2/sites/query', {
49
+ method: 'POST',
50
+ headers: {
51
+ 'Content-Type': 'application/json',
52
+ Accept: 'application/json, text/plain, */*',
53
+ Authorization: await getAccountAccessToken()
54
+ },
55
+ body: JSON.stringify({
56
+ query: {
57
+ cursorPaging: { limit: 50 }
58
+ }
59
+ })
60
+ });
61
+ const result = await sitesRes.json().then(({ sites }) => sites?.map(({ id, displayName }) => ({
62
+ id,
63
+ name: displayName
64
+ })) ?? []);
65
+ return {
66
+ content: [
67
+ {
68
+ type: 'text',
69
+ text: JSON.stringify(result)
70
+ },
71
+ {
72
+ type: 'text',
73
+ text: 'if there is more than one site returned, the user should pick one from a list, list the sites (only name) for the user and ask them to pick one'
74
+ }
75
+ ]
76
+ };
77
+ });
78
+ server.tool('ManageWixSite', `Use account level API in order to create a site, update a site and publish site.
79
+ ALWAYS use "SearchWixRESTDocumentation" to search for the API you should invoke, NEVER GUESS THE SITE API URL
80
+ You should ALWAYS check the rest docs - "SearchWixRESTDocumentation" for the specific API you want to call, don't just call it without knowing what it does, CHECK THE DOCS`, {
81
+ url: z
82
+ .string()
83
+ .describe('The url of the api to call - ALWAYS get the information from the Wix REST docs DONT GUESS IT, the URL MUST BE ABSOLUTE URL'),
84
+ method: z
85
+ .string()
86
+ .describe('The HTTP method to use for the API call (e.g. GET, POST, PUT, DELETE)'),
87
+ body: z
88
+ .string()
89
+ .optional()
90
+ .describe('A string representing of a valid JSON object to describe the body of the request')
91
+ }, async ({ url, body, method }) => {
92
+ logger.log(`Calling Wix Account level API: ${url}, body: ${JSON.stringify(body)}`);
93
+ const authorization = await getAccountAccessToken();
94
+ try {
95
+ const response = await fetch(url, {
96
+ method,
97
+ headers: {
98
+ Authorization: authorization,
99
+ ...(body ? { 'Content-Type': 'application/json' } : {})
100
+ },
101
+ body: method === 'GET' ? undefined : body
102
+ });
103
+ const responseData = await handleWixAPIResponse(response);
104
+ return {
105
+ content: [
106
+ {
107
+ type: 'text',
108
+ text: `Wix Account API call successful: ${JSON.stringify(responseData)}`
109
+ }
110
+ ]
111
+ };
112
+ }
113
+ catch (error) {
114
+ logger.error(`Failed to call Wix Account API: ${error}`);
115
+ throw new Error(`Failed to call Wix Account API: ${error}`);
116
+ }
117
+ });
118
+ }
package/build/bin.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import './sentry.js';
2
+ declare const PUBLIC_TOOLS: readonly ["WDS", "REST", "SDK", "BUILD_APPS", "WIX_HEADLESS", "BUSINESS_SOLUTIONS"];
3
+ declare const EXPERIMENTAL_TOOLS: readonly ["WIX_API", "CLI_COMMAND"];
4
+ type Tool = (typeof PUBLIC_TOOLS)[number] | (typeof EXPERIMENTAL_TOOLS)[number];
5
+ export declare const DEFAULT_TOOLS: Tool[];
6
+ export {};
package/build/bin.js ADDED
@@ -0,0 +1,98 @@
1
+ import './sentry.js';
2
+ import minimist from 'minimist';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { addDocsTools, VALID_DOCS_TOOLS } from './docs/docs.js';
5
+ import { addCliTools, VALID_CLI_TOOLS } from './cli-tools/cli.js';
6
+ import { logger, attachStdErrLogger, attachMcpServerLogger, attachFileLogger } from './logger.js';
7
+ import { WixMcpServer } from './wix-mcp-server.js';
8
+ import { addDocsResources } from './resources/docs.js';
9
+ const PUBLIC_TOOLS = [...VALID_DOCS_TOOLS];
10
+ const EXPERIMENTAL_TOOLS = ['WIX_API', 'CLI_COMMAND'];
11
+ export const DEFAULT_TOOLS = [...VALID_DOCS_TOOLS];
12
+ const parsedArgs = minimist(process.argv.slice(2));
13
+ function parseExperimentalArg() {
14
+ const experimentalArg = parsedArgs['experimental'];
15
+ if (!experimentalArg)
16
+ return [];
17
+ return experimentalArg
18
+ .split(',')
19
+ .map((t) => t.trim())
20
+ .filter((tool) => EXPERIMENTAL_TOOLS.includes(tool));
21
+ }
22
+ function parseToolsArg() {
23
+ const toolsArg = parsedArgs['tools'];
24
+ const experimentalTools = parseExperimentalArg();
25
+ if (!toolsArg) {
26
+ // When no tools specified, return both default and experimental tools
27
+ return [...DEFAULT_TOOLS, ...experimentalTools];
28
+ }
29
+ const requestedTools = toolsArg.split(',').map((t) => t.trim());
30
+ const tools = [
31
+ // Include valid non-experimental tools
32
+ ...requestedTools.filter((tool) => PUBLIC_TOOLS.includes(tool)),
33
+ // Include enabled experimental tools
34
+ ...experimentalTools
35
+ ];
36
+ // Warn about enabled experimental tools
37
+ tools.forEach((tool) => {
38
+ if (EXPERIMENTAL_TOOLS.includes(tool)) {
39
+ logger.log(`Warning: ${tool} is an experimental tool and may have limited functionality or breaking changes`);
40
+ }
41
+ });
42
+ return tools;
43
+ }
44
+ const loggerType = parsedArgs['logger'] || 'mcp';
45
+ if (loggerType === 'file') {
46
+ attachFileLogger();
47
+ }
48
+ else {
49
+ // Initially we log to stderr, because MCP server is not connected yet
50
+ // When the server is connected, we attach the MCP server logger
51
+ attachStdErrLogger();
52
+ }
53
+ logger.log('--------------------------------');
54
+ logger.log('starting WIX MCP server');
55
+ logger.log('--------------------------------');
56
+ const server = new WixMcpServer({
57
+ name: 'wix-mcp-server',
58
+ version: '1.0.0'
59
+ }, {
60
+ capabilities: {
61
+ tools: {},
62
+ prompts: {},
63
+ logging: {},
64
+ resources: {}
65
+ }
66
+ });
67
+ const activeTools = parseToolsArg();
68
+ logger.log('Active tools:', activeTools);
69
+ const docsTools = activeTools.filter((tool) => VALID_DOCS_TOOLS.includes(tool));
70
+ if (docsTools.length > 0) {
71
+ logger.log('Adding docs tools:', docsTools);
72
+ addDocsTools(server, docsTools);
73
+ }
74
+ const isWixOne = parsedArgs['cli'] === 'wix-one';
75
+ const cliTools = activeTools.filter((tool) => VALID_CLI_TOOLS.includes(tool));
76
+ if (cliTools.length > 0) {
77
+ logger.log('Adding cli tools:', cliTools, 'isWixOne:', isWixOne);
78
+ addCliTools(server, cliTools, isWixOne);
79
+ }
80
+ try {
81
+ const portals = parsedArgs['portals']?.split(',') || [];
82
+ if (portals.length > 0) {
83
+ logger.log('Adding docs resources for portals:', portals);
84
+ await addDocsResources(server, portals);
85
+ }
86
+ }
87
+ catch (error) {
88
+ logger.error('Error adding docs resources:', error);
89
+ }
90
+ logger.log('Starting server');
91
+ const transport = new StdioServerTransport();
92
+ logger.log('Connecting to transport');
93
+ await server.connect(transport);
94
+ logger.log('Transport connected');
95
+ if (loggerType === 'mcp') {
96
+ // From now on, we log to the MCP server
97
+ attachMcpServerLogger(server);
98
+ }
@@ -0,0 +1,4 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare const VALID_CLI_TOOLS: readonly ["WIX_API", "CLI_COMMAND", "CLI_COMMAND_INTERACTIVE_MODE"];
3
+ export type CLITool = (typeof VALID_CLI_TOOLS)[number];
4
+ export declare function addCliTools(server: McpServer, allowedTools?: CLITool[], isWixOne?: boolean): void;
@@ -1,8 +1,9 @@
1
1
  import { z } from 'zod';
2
- import { getCliAuthTokenForSiteId, getSiteIdFromCliAppConfig, handleWixAPIResponse } from './utils.js';
2
+ import { getCliAuthTokenForSiteId, getSiteIdFromCliAppConfig } from './utils.js';
3
3
  import { logger } from '../logger.js';
4
4
  import { execa } from 'execa';
5
5
  import { mockInteractiveGenerateCommandTool } from '../interactive-command-tools/interactive-command-utils.js';
6
+ import { handleWixAPIResponse } from '../tool-utils.js';
6
7
  export const VALID_CLI_TOOLS = [
7
8
  'WIX_API',
8
9
  'CLI_COMMAND',
@@ -12,7 +13,7 @@ export function addCliTools(server, allowedTools = [
12
13
  'WIX_API',
13
14
  'CLI_COMMAND',
14
15
  'CLI_COMMAND_INTERACTIVE_MODE'
15
- ]) {
16
+ ], isWixOne = false) {
16
17
  if (allowedTools.includes('WIX_API')) {
17
18
  server.tool('CallWixAPI', 'Call Wix apis on an app. Use this to create, read, update, and delete data and other Wix entities in your Wix app.', {
18
19
  appPath: z
@@ -239,7 +240,7 @@ export function addCliTools(server, allowedTools = [
239
240
  // Format generate command with proper arguments
240
241
  commandName = 'npx';
241
242
  commandArgs = [
242
- 'wix',
243
+ isWixOne ? 'wix-one' : 'wix',
243
244
  'app',
244
245
  'generate',
245
246
  '-t',
@@ -254,6 +255,9 @@ export function addCliTools(server, allowedTools = [
254
255
  commandName = commandParts[0];
255
256
  commandArgs = commandParts.slice(1);
256
257
  }
258
+ if (isWixOne && commandName === 'wix') {
259
+ commandName = 'wix-one';
260
+ }
257
261
  try {
258
262
  logger.log(`Executing Wix CLI command: ${commandName} ${commandArgs.join(' ')}`);
259
263
  // Execute with timeout
@@ -0,0 +1,2 @@
1
+ export declare function getCliAuthTokenForSiteId(siteIdOrAccountsFileName: string): string;
2
+ export declare function getSiteIdFromCliAppConfig(appPath: string): any;
@@ -10,22 +10,3 @@ export function getSiteIdFromCliAppConfig(appPath) {
10
10
  const appConfig = JSON.parse(readFileSync(path.join(appPath, '.wix/app.config.json'), 'utf8'));
11
11
  return appConfig.siteId;
12
12
  }
13
- const safeParseJSON = (text) => {
14
- try {
15
- return JSON.parse(text);
16
- }
17
- catch {
18
- return text;
19
- }
20
- };
21
- export const handleWixAPIResponse = async (response) => {
22
- const responseText = await response.text();
23
- const responseData = safeParseJSON(responseText);
24
- if (!response.ok) {
25
- const errorDetails = typeof responseData === 'object'
26
- ? JSON.stringify(responseData)
27
- : responseData;
28
- throw new Error(`Failed to call Wix API: ${response.status} ${response.statusText}. ${errorDetails}`);
29
- }
30
- return responseData;
31
- };
@@ -0,0 +1,4 @@
1
+ import type { WixMcpServer } from '../wix-mcp-server.js';
2
+ export declare const VALID_DOCS_TOOLS: readonly ["WDS", "REST", "SDK", "BUILD_APPS", "WIX_HEADLESS", "BUSINESS_SOLUTIONS"];
3
+ export type DocsTool = (typeof VALID_DOCS_TOOLS)[number];
4
+ export declare const addDocsTools: (server: WixMcpServer, allowedTools?: DocsTool[]) => void;
@@ -7,14 +7,16 @@ export const VALID_DOCS_TOOLS = [
7
7
  'REST',
8
8
  'SDK',
9
9
  'BUILD_APPS',
10
- 'WIX_HEADLESS'
10
+ 'WIX_HEADLESS',
11
+ 'BUSINESS_SOLUTIONS'
11
12
  ];
12
13
  export const addDocsTools = (server, allowedTools = [
13
14
  'WDS',
14
15
  'REST',
15
16
  'SDK',
16
17
  'BUILD_APPS',
17
- 'WIX_HEADLESS'
18
+ 'WIX_HEADLESS',
19
+ 'BUSINESS_SOLUTIONS'
18
20
  ]) => {
19
21
  // WDS Documentation
20
22
  if (allowedTools.includes('WDS')) {
@@ -33,13 +35,17 @@ export const addDocsTools = (server, allowedTools = [
33
35
  .min(1)
34
36
  .max(15)
35
37
  .optional()
36
- .default(5)
38
+ .default(10)
37
39
  }, async ({ searchTerm, maxResults }, { panorama }) => {
38
40
  try {
39
41
  logger.log(`Searching for ${searchTerm} in Wix WDS`);
40
- const result = await runSemanticSearchAndFormat('WDS', {
41
- searchTerm: searchTerm
42
- }, Math.max(1, Math.min(maxResults ?? 5, 15)));
42
+ const result = await runSemanticSearchAndFormat({
43
+ toolName: 'WDS',
44
+ toolParams: {
45
+ searchTerm: searchTerm
46
+ },
47
+ maxResults: Math.max(1, Math.min(maxResults ?? 5, 15))
48
+ });
43
49
  return {
44
50
  content: [
45
51
  {
@@ -82,18 +88,27 @@ export const addDocsTools = (server, allowedTools = [
82
88
  .min(1)
83
89
  .max(15)
84
90
  .optional()
85
- .default(5)
91
+ .default(10)
86
92
  }, async ({ searchTerm, maxResults }, { panorama }) => {
87
93
  try {
88
94
  logger.log(`Searching for ${searchTerm} in Wix REST API`);
89
- const result = await runSemanticSearchAndFormat('REST', {
90
- searchTerm: searchTerm
91
- }, Math.max(1, Math.min(maxResults ?? 5, 15)));
95
+ const result = await runSemanticSearchAndFormat({
96
+ toolName: 'REST',
97
+ toolParams: {
98
+ searchTerm: searchTerm
99
+ },
100
+ maxResults: Math.max(1, Math.min(maxResults ?? 5, 15)),
101
+ linesInEachResult: 15
102
+ });
92
103
  return {
93
104
  content: [
94
105
  {
95
106
  type: 'text',
96
107
  text: result
108
+ },
109
+ {
110
+ type: 'text',
111
+ text: 'Next, you MUST use the ReadFullDocsArticle tool if you want to read the full documentation for a specific article or method.'
97
112
  }
98
113
  ]
99
114
  };
@@ -114,6 +129,61 @@ export const addDocsTools = (server, allowedTools = [
114
129
  }
115
130
  });
116
131
  }
132
+ if (allowedTools.includes('BUSINESS_SOLUTIONS')) {
133
+ server.tool('WixBusinessFlowsDocumentation', [
134
+ 'This tool provides step-by-step recipes for setting up complex Wix business solutions that involve multiple API calls.',
135
+ 'It excels at guiding the creation of features like booking systems with payments.',
136
+ 'For example, it can guide the setup of a service in Wix Bookings where customers can pay using Wix Payments, such as creating a bookable yoga class',
137
+ 'It searches the Wix Business Solutions documentation for these integrated workflows involving services, bookings, payments, stores, blogs, and more.',
138
+ 'Before attempting to implement a multi step API calls on Wix, YOU MUST TRY THIS TOOL FIRST.',
139
+ 'If the result includes a link to another article, use ReadFullDocsArticle tool to fetch the full article.'
140
+ ].join('\n'), {
141
+ searchTerm: z
142
+ .string()
143
+ .describe('The search term for the required sample flow (e.g. "setup a new business")'),
144
+ maxResults: z
145
+ .number()
146
+ .describe('The maximum number of results to return, default is 5, max is 15')
147
+ .min(1)
148
+ .max(15)
149
+ .optional()
150
+ .default(5)
151
+ }, async ({ searchTerm, maxResults }, { panorama }) => {
152
+ try {
153
+ logger.log(`Searching for ${searchTerm} in Wix Sample Flows`);
154
+ const result = await runSemanticSearchAndFormat({
155
+ toolName: 'BUSINESS_SOLUTIONS',
156
+ toolParams: {
157
+ searchTerm: '**RECIPE**: Business Recipes - full setup flow for::: ' +
158
+ searchTerm
159
+ },
160
+ maxResults: Math.max(1, Math.min(maxResults ?? 5, 15))
161
+ });
162
+ return {
163
+ content: [
164
+ {
165
+ type: 'text',
166
+ text: result
167
+ }
168
+ ]
169
+ };
170
+ }
171
+ catch (error) {
172
+ panorama.errorMonitor().reportError(error);
173
+ captureException(error, {
174
+ tags: {
175
+ componentId: 'SearchWixSampleFlowsDocumentation',
176
+ toolName: 'SearchWixSampleFlowsDocumentation'
177
+ }
178
+ });
179
+ logger.error(`Error searching for ${searchTerm} in Wix Sample Flows: ${error}`);
180
+ return {
181
+ isError: true,
182
+ content: [{ type: 'text', text: 'Error: ' + error.message }]
183
+ };
184
+ }
185
+ });
186
+ }
117
187
  // SDK Documentation
118
188
  if (allowedTools.includes('SDK')) {
119
189
  server.tool('SearchWixSDKDocumentation', [
@@ -138,9 +208,14 @@ export const addDocsTools = (server, allowedTools = [
138
208
  panorama
139
209
  .transaction('SearchWixSDKDocumentation')
140
210
  .start({ maxResults });
141
- const result = await runSemanticSearchAndFormat('SDK', {
142
- searchTerm: searchTerm
143
- }, Math.max(1, Math.min(maxResults ?? 5, 15)));
211
+ const result = await runSemanticSearchAndFormat({
212
+ toolName: 'SDK',
213
+ toolParams: {
214
+ searchTerm: searchTerm
215
+ },
216
+ maxResults: Math.max(1, Math.min(maxResults ?? 5, 15)),
217
+ linesInEachResult: 15
218
+ });
144
219
  panorama
145
220
  .transaction('SearchWixSDKDocumentation')
146
221
  .finish({ maxResults });
@@ -149,6 +224,10 @@ export const addDocsTools = (server, allowedTools = [
149
224
  {
150
225
  type: 'text',
151
226
  text: result
227
+ },
228
+ {
229
+ type: 'text',
230
+ text: 'Next, you MUST use ReadFullDocsArticle tool if you want to read full documentation for a specific article or method.'
152
231
  }
153
232
  ]
154
233
  };
@@ -190,9 +269,13 @@ export const addDocsTools = (server, allowedTools = [
190
269
  }, async ({ searchTerm, maxResults }, { panorama }) => {
191
270
  try {
192
271
  logger.log(`Searching for ${searchTerm} in Build Apps`);
193
- const result = await runSemanticSearchAndFormat('BUILD_APPS', {
194
- searchTerm: `wix cli docs for ${searchTerm}`
195
- }, Math.max(1, Math.min(maxResults ?? 5, 15)));
272
+ const result = await runSemanticSearchAndFormat({
273
+ toolName: 'BUILD_APPS',
274
+ toolParams: {
275
+ searchTerm: `wix cli docs for ${searchTerm}`
276
+ },
277
+ maxResults: Math.max(1, Math.min(maxResults ?? 5, 15))
278
+ });
196
279
  return {
197
280
  content: [
198
281
  {
@@ -238,9 +321,13 @@ export const addDocsTools = (server, allowedTools = [
238
321
  }, async ({ searchTerm, maxResults }, { panorama }) => {
239
322
  try {
240
323
  logger.log(`Searching for ${searchTerm} in Headless`);
241
- const result = await runSemanticSearchAndFormat('WIX_HEADLESS', {
242
- searchTerm: searchTerm
243
- }, Math.max(1, Math.min(maxResults ?? 5, 15)));
324
+ const result = await runSemanticSearchAndFormat({
325
+ toolName: 'WIX_HEADLESS',
326
+ toolParams: {
327
+ searchTerm: searchTerm
328
+ },
329
+ maxResults: Math.max(1, Math.min(maxResults ?? 5, 15))
330
+ });
244
331
  return {
245
332
  content: [
246
333
  {
@@ -265,4 +352,91 @@ export const addDocsTools = (server, allowedTools = [
265
352
  }
266
353
  });
267
354
  }
355
+ server.tool('ReadFullDocsArticle', [
356
+ 'Fetches the full Wix docs article or method article.',
357
+ 'Use this tool when you read a summary of a docs article or method article, you have the docs url and want to read the full article.'
358
+ ].join('\n'), {
359
+ articleUrl: z
360
+ .string()
361
+ .describe('The URL of the docs article or method article to fetch. Should be something like https://dev.wix.com/docs/.../.../...')
362
+ }, async ({ articleUrl }) => {
363
+ try {
364
+ const articleContent = await fetchArticleContent(articleUrl, 'article');
365
+ if (!articleContent) {
366
+ throw new Error('Article content is empty');
367
+ }
368
+ return {
369
+ content: [
370
+ {
371
+ type: 'text',
372
+ text: articleContent
373
+ },
374
+ {
375
+ type: 'text',
376
+ text: [
377
+ '---',
378
+ 'Next, if you are dealing with a method - you MUST call ReadFullDocsMethodSchema tool - it will give you the full request schema for the method.'
379
+ ].join('\n')
380
+ }
381
+ ]
382
+ };
383
+ }
384
+ catch (error) {
385
+ logger.error(`Error fetching article content or method schema for ${articleUrl}: ${error}`);
386
+ return {
387
+ content: [
388
+ {
389
+ type: 'text',
390
+ text: 'Could not fetch article content, you should try completing the task without it.'
391
+ }
392
+ ]
393
+ };
394
+ }
395
+ });
396
+ server.tool('ReadFullDocsMethodSchema', [
397
+ 'Fetches the full method schema for a given method. Always use it before calling the method.',
398
+ 'This will give you the entire request/response schema with all the fields and their descriptions.'
399
+ ].join('\n'), {
400
+ articleUrl: z
401
+ .string()
402
+ .describe('The URL of the documentation to fetch. Should be something like https://dev.wix.com/docs/.../.../...')
403
+ }, async ({ articleUrl }) => {
404
+ try {
405
+ const articleContent = await fetchArticleContent(articleUrl, 'methodSchema');
406
+ if (!articleContent) {
407
+ throw new Error('Method schema is empty');
408
+ }
409
+ return {
410
+ content: [
411
+ {
412
+ type: 'text',
413
+ text: articleContent
414
+ }
415
+ ]
416
+ };
417
+ }
418
+ catch (error) {
419
+ logger.error(`Error fetching method schema for ${articleUrl}: ${error}`);
420
+ return {
421
+ content: [
422
+ {
423
+ type: 'text',
424
+ text: 'Could not fetch method schema, you should try completing the task without it.'
425
+ }
426
+ ]
427
+ };
428
+ }
429
+ });
268
430
  };
431
+ async function fetchArticleContent(articleUrl, mode) {
432
+ // https://dev.wix.com/digor/api/get-article-content?articleUrl=https://dev.wix.com/docs/rest/business-solutions/blog/draft-posts/create-draft-post&schema=true
433
+ const url = new URL(`https://dev.wix.com/digor/api/get-article-content`);
434
+ url.searchParams.set('articleUrl', articleUrl);
435
+ const schema = mode === 'methodSchema' ? 'true' : 'false';
436
+ url.searchParams.set('schema', schema);
437
+ logger.log(`Fetching resource from docs ${url.toString()}`);
438
+ const response = await fetch(url.toString());
439
+ const data = await response.json();
440
+ logger.log(`Fetched resource from docs: ${data.articleContent}`);
441
+ return data.articleContent;
442
+ }