@wix/mcp 1.0.94 → 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.
- package/README.md +2 -2
- package/build/bin-standalone.js +1457 -332
- package/build/cjs/index.cjs +3379 -1333
- package/build/cjs/toolkit.cjs +2158 -373
- package/build/dts/api-call/headless-release.d.ts +56 -0
- package/build/dts/api-call/headless-release.js +135 -0
- package/build/dts/api-call/index.d.ts +9 -1
- package/build/dts/api-call/index.js +36 -8
- package/build/dts/api-call/site-files.d.ts +14 -0
- package/build/dts/api-call/site-files.js +106 -10
- package/build/dts/api-call/upload-headless-website-files.d.ts +117 -5
- package/build/dts/api-call/upload-headless-website-files.js +565 -20
- package/build/dts/api-call/upload-image-tool.d.ts +2 -0
- package/build/dts/api-call/upload-image-tool.js +8 -4
- package/build/dts/bin.d.ts +1 -1
- package/build/dts/bin.js +3 -1
- package/build/dts/code-mode/attachments.d.ts +9 -6
- package/build/dts/code-mode/attachments.js +23 -16
- package/build/dts/code-mode/index.d.ts +6 -1
- package/build/dts/code-mode/index.js +32 -12
- package/build/dts/code-mode/truncate-response.js +5 -1
- package/build/dts/config/default-config.d.ts +35 -5
- package/build/dts/config/default-config.js +339 -182
- package/build/dts/config/file-params.d.ts +22 -0
- package/build/dts/config/file-params.js +24 -0
- package/build/dts/config/param-descriptions.d.ts +14 -2
- package/build/dts/config/param-descriptions.js +33 -14
- package/build/dts/constants.d.ts +1 -0
- package/build/dts/constants.js +1 -0
- package/build/dts/docs/docs.d.ts +12 -1
- package/build/dts/docs/docs.js +274 -43
- package/build/dts/docs/fetch-article.d.ts +21 -0
- package/build/dts/docs/fetch-article.js +66 -4
- package/build/dts/docs/get-to-know.d.ts +7 -2
- package/build/dts/docs/get-to-know.js +36 -14
- package/build/dts/docs/guessed-url.d.ts +41 -0
- package/build/dts/docs/guessed-url.js +185 -0
- package/build/dts/docs/mintlify-docs.d.ts +121 -0
- package/build/dts/docs/mintlify-docs.js +391 -0
- package/build/dts/docs/semanticSearch.d.ts +2 -1
- package/build/dts/docs/semanticSearch.js +14 -7
- package/build/dts/docs/start-deploy-website.d.ts +7 -6
- package/build/dts/docs/start-deploy-website.js +13 -8
- package/build/dts/docs/start-headless-build.d.ts +8 -10
- package/build/dts/docs/start-headless-build.js +14 -28
- package/build/dts/index.d.ts +2 -2
- package/build/dts/index.js +2 -2
- package/build/dts/infra/redact-personal-data.d.ts +4 -0
- package/build/dts/infra/redact-personal-data.js +148 -0
- package/build/dts/site-widget-tools/constants.d.ts +50 -2
- package/build/dts/site-widget-tools/constants.js +65 -8
- package/build/dts/site-widget-tools/index.d.ts +1 -0
- package/build/dts/site-widget-tools/index.js +1 -0
- package/build/dts/site-widget-tools/pick-images-tool/index.d.ts +14 -0
- package/build/dts/site-widget-tools/pick-images-tool/index.js +138 -0
- package/build/dts/site-widget-tools/pull-site-creation-job-tool/index.js +1 -1
- package/build/dts/site-widget-tools/search-site-templates-tool/index.js +25 -2
- package/build/dts/site-widget-tools/site-builder-tool/index.js +35 -3
- package/build/dts/site-widget-tools/site-creation-tool/index.d.ts +7 -1
- package/build/dts/site-widget-tools/site-creation-tool/index.js +22 -51
- package/build/dts/site-widget-tools/suggested-domain-tool/index.js +26 -12
- package/build/dts/support/index.d.ts +4 -0
- package/build/dts/support/index.js +13 -7
- package/build/dts/tool-utils.d.ts +8 -1
- package/build/dts/tool-utils.js +26 -2
- package/build/dts/toolkit-entry.d.cts +15 -10
- package/build/dts/toolkit-entry.d.ts +15 -10
- package/build/dts/toolkit.d.ts +2 -2
- package/build/dts/toolkit.js +21 -8
- package/build/dts/wix-mcp-server.d.ts +10 -1
- package/build/dts/wix-mcp-server.js +25 -7
- package/build/dts/wixel/constants.d.ts +8 -1
- package/build/dts/wixel/constants.js +8 -1
- package/build/dts/wixel/index.d.ts +1 -1
- package/build/dts/wixel/index.js +2 -2
- package/build/dts/wixel/wixel-a2a-client.d.ts +83 -0
- package/build/dts/wixel/wixel-a2a-client.js +166 -0
- package/build/dts/wixel/wixel-bi.d.ts +2 -1
- package/build/dts/wixel/wixel-bi.js +2 -1
- package/build/dts/wixel/{external-agent-api.d.ts → wixel-session-entries.d.ts} +11 -33
- package/build/dts/wixel/{external-agent-api.js → wixel-session-entries.js} +33 -54
- package/build/dts/wixel/wixel-session-tools.d.ts +21 -26
- package/build/dts/wixel/wixel-session-tools.js +494 -599
- package/build/esm/index.js +3422 -1388
- package/build/esm/toolkit.js +2158 -373
- package/package.json +4 -4
|
@@ -5,5 +5,7 @@ export declare const UPLOAD_IMAGE_TOOL_NAME = "UploadImageToWixSite";
|
|
|
5
5
|
export declare function addUploadImageTool(server: WixMcpServer, strategy: McpAuthenticationStrategy, options?: {
|
|
6
6
|
description?: string;
|
|
7
7
|
hints?: ToolHintsConfig;
|
|
8
|
+
/** Set when the session also has the image picker (Claude clients). */
|
|
9
|
+
imagePickerToolName?: string;
|
|
8
10
|
}): void;
|
|
9
11
|
//# sourceMappingURL=upload-image-tool.d.ts.map
|
|
@@ -51,7 +51,7 @@ function errorResponse(text) {
|
|
|
51
51
|
// ---------------------------------------------------------------------------
|
|
52
52
|
// Upload flows
|
|
53
53
|
// ---------------------------------------------------------------------------
|
|
54
|
-
async function uploadFromUrl(item, authHeaders, displayName) {
|
|
54
|
+
async function uploadFromUrl(item, authHeaders, displayName, imagePickerToolName) {
|
|
55
55
|
if (!item.download_url) {
|
|
56
56
|
return {
|
|
57
57
|
success: false,
|
|
@@ -61,7 +61,7 @@ async function uploadFromUrl(item, authHeaders, displayName) {
|
|
|
61
61
|
if (!/^https?:\/\//i.test(item.download_url)) {
|
|
62
62
|
return {
|
|
63
63
|
success: false,
|
|
64
|
-
error: `
|
|
64
|
+
error: `This server can't read files on your machine or sandbox (got: "${item.download_url}"). Send this image as imageBase64 (the whole file, one per call) or as a public URL in imageUrls${imagePickerToolName ? `, or call ${imagePickerToolName} so the user picks it` : ''}.`
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
try {
|
|
@@ -165,7 +165,11 @@ function formatBatchResults(results) {
|
|
|
165
165
|
// Tool registration
|
|
166
166
|
// ---------------------------------------------------------------------------
|
|
167
167
|
export function addUploadImageTool(server, strategy, options) {
|
|
168
|
-
const
|
|
168
|
+
const imagePickerToolName = options?.imagePickerToolName;
|
|
169
|
+
const baseDescription = options?.description || defaultToolDescriptions.UploadImageToWixSite;
|
|
170
|
+
const description = imagePickerToolName
|
|
171
|
+
? `${baseDescription}\nIf you can't deliver the image any of these ways, call ${imagePickerToolName} so the user picks it.`
|
|
172
|
+
: baseDescription;
|
|
169
173
|
const hints = {
|
|
170
174
|
readOnlyHint: options?.hints?.readOnlyHint ?? false,
|
|
171
175
|
destructiveHint: options?.hints?.destructiveHint ?? false,
|
|
@@ -226,7 +230,7 @@ export function addUploadImageTool(server, strategy, options) {
|
|
|
226
230
|
logger.log(`[WixMCP] Uploading ${total} image(s) to Wix site: ${siteId}`);
|
|
227
231
|
try {
|
|
228
232
|
const authHeaders = await strategy.getSiteAuthHeaders(siteId);
|
|
229
|
-
const urlResults = await Promise.all(urlItems.map((item) => uploadFromUrl(item, authHeaders, displayName)));
|
|
233
|
+
const urlResults = await Promise.all(urlItems.map((item) => uploadFromUrl(item, authHeaders, displayName, imagePickerToolName)));
|
|
230
234
|
let base64Result = null;
|
|
231
235
|
if (imageBase64) {
|
|
232
236
|
const result = await uploadFromBase64(imageBase64, mimeType, authHeaders, displayName);
|
package/build/dts/bin.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import './infra/sentry.js';
|
|
2
|
-
declare const PUBLIC_TOOLS: readonly ["WDS", "REST", "SDK", "BUILD_APPS", "WIX_HEADLESS", "VELO", "BUSINESS_SOLUTIONS", "CLI", "OVERVIEW"];
|
|
2
|
+
declare const PUBLIC_TOOLS: readonly ["WDS", "REST", "SDK", "BUILD_APPS", "WIX_HEADLESS", "VELO", "BUSINESS_SOLUTIONS", "CLI", "OVERVIEW", "HELP_CENTER"];
|
|
3
3
|
declare const EXPERIMENTAL_TOOLS: readonly ["WIX_API", "CLI_COMMAND", "GET_TO_KNOW_WIX", "CREATE_WIX_BUSINESS_GUIDE", "VELO_README", "WIX_API_THROUGH_FS", "GRAPHQL"];
|
|
4
4
|
type Tool = (typeof PUBLIC_TOOLS)[number] | (typeof EXPERIMENTAL_TOOLS)[number];
|
|
5
5
|
export declare const DEFAULT_TOOLS: Tool[];
|
package/build/dts/bin.js
CHANGED
|
@@ -95,7 +95,9 @@ if (docsTools.length > 0 || hasGetToKnowWix) {
|
|
|
95
95
|
logger.log('Adding docs tools:', docsTools);
|
|
96
96
|
addDocsTools(server, docsTools, {
|
|
97
97
|
getToKnowWixEnabled: hasGetToKnowWix,
|
|
98
|
-
dynamicContextEnabled: hasDynamicContext
|
|
98
|
+
dynamicContextEnabled: hasDynamicContext,
|
|
99
|
+
// Mintlify docs: --docsSite=mintlify-links (or WIX_MCP_DOCS_SITE).
|
|
100
|
+
docsSite: parsedArgs['docsSite'] ?? process.env.WIX_MCP_DOCS_SITE
|
|
99
101
|
});
|
|
100
102
|
}
|
|
101
103
|
if (activeTools.includes('VELO_README')) {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Chat file attachments for ExecuteWixAPI (`openai/fileParams`).
|
|
3
3
|
*
|
|
4
4
|
* ChatGPT resolves a file the user attached — or one the model wrote in its
|
|
5
5
|
* own sandbox — into `{ download_url, file_id, file_name?, mime_type? }` for a
|
|
6
|
-
* param the tool lists under `_meta["openai/fileParams"]`.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* param the tool lists under `_meta["openai/fileParams"]`. For ExecuteWixAPI the
|
|
7
|
+
* Wix AI Assistant (Aria) does the same by swapping an `attachment://<id>`
|
|
8
|
+
* reference in the tool args for a signed URL on its own Wix storage. The
|
|
9
|
+
* bytes never go through the model. We fetch each `download_url` host-side and
|
|
10
|
+
* hand the runtime ready-made base64 file entries, the same shape the `files` bundle
|
|
9
11
|
* parses into, so `files`, `wix.multipart()` and `wix.unzip()` are unchanged.
|
|
10
12
|
*
|
|
11
13
|
* This is additive: clients without the extension never populate the param
|
|
@@ -24,7 +26,8 @@ export interface AttachmentFile {
|
|
|
24
26
|
encoding: 'base64';
|
|
25
27
|
}
|
|
26
28
|
export declare const MAX_ATTACHMENTS_BYTES: number;
|
|
27
|
-
export declare
|
|
29
|
+
export declare const WIX_AI_ASSISTANT_ATTACHMENT_HOSTS: ReadonlySet<string>;
|
|
30
|
+
export declare function isAllowedAttachmentHost(hostname: string, extraHosts?: ReadonlySet<string>): boolean;
|
|
28
31
|
export declare class AttachmentError extends Error {
|
|
29
32
|
}
|
|
30
33
|
/**
|
|
@@ -33,5 +36,5 @@ export declare class AttachmentError extends Error {
|
|
|
33
36
|
* (ChatGPT passed a sandbox path or a bare id instead of a URL), a host we
|
|
34
37
|
* don't trust, a failed download, or more bytes than MAX_ATTACHMENTS_BYTES.
|
|
35
38
|
*/
|
|
36
|
-
export declare function resolveAttachments(attachments: unknown, fetchImpl?: typeof fetch): Promise<AttachmentFile[]>;
|
|
39
|
+
export declare function resolveAttachments(attachments: unknown, fetchImpl?: typeof fetch, extraHosts?: ReadonlySet<string>): Promise<AttachmentFile[]>;
|
|
37
40
|
//# sourceMappingURL=attachments.d.ts.map
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Chat file attachments for ExecuteWixAPI (`openai/fileParams`).
|
|
3
3
|
*
|
|
4
4
|
* ChatGPT resolves a file the user attached — or one the model wrote in its
|
|
5
5
|
* own sandbox — into `{ download_url, file_id, file_name?, mime_type? }` for a
|
|
6
|
-
* param the tool lists under `_meta["openai/fileParams"]`.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* param the tool lists under `_meta["openai/fileParams"]`. For ExecuteWixAPI the
|
|
7
|
+
* Wix AI Assistant (Aria) does the same by swapping an `attachment://<id>`
|
|
8
|
+
* reference in the tool args for a signed URL on its own Wix storage. The
|
|
9
|
+
* bytes never go through the model. We fetch each `download_url` host-side and
|
|
10
|
+
* hand the runtime ready-made base64 file entries, the same shape the `files` bundle
|
|
9
11
|
* parses into, so `files`, `wix.multipart()` and `wix.unzip()` are unchanged.
|
|
10
12
|
*
|
|
11
13
|
* This is additive: clients without the extension never populate the param
|
|
@@ -26,7 +28,7 @@ const MAX_REDIRECTS = 4;
|
|
|
26
28
|
// - Azure blob accounts behind them, one per OpenAI region.
|
|
27
29
|
// `blob.core.windows.net` is a shared namespace, so these are exact — a
|
|
28
30
|
// prefix could be registered by anyone. A new region shows up as an
|
|
29
|
-
// "is not a
|
|
31
|
+
// "is not a chat attachment host" error in BI; add it here.
|
|
30
32
|
const OAI_USERCONTENT_HOST = /^[a-z0-9-]+\.oaiusercontent\.com$/;
|
|
31
33
|
const OAI_BLOB_ACCOUNTS = new Set([
|
|
32
34
|
'australiaeast',
|
|
@@ -55,18 +57,23 @@ const OAI_BLOB_ACCOUNTS = new Set([
|
|
|
55
57
|
'westus2',
|
|
56
58
|
'westus3'
|
|
57
59
|
].map((region) => `oaisdmntpr${region}.blob.core.windows.net`));
|
|
58
|
-
export
|
|
60
|
+
export const WIX_AI_ASSISTANT_ATTACHMENT_HOSTS = new Set([
|
|
61
|
+
'wixmp-a85a29aaf55baa3e5171a82d.wixmp.com'
|
|
62
|
+
]);
|
|
63
|
+
export function isAllowedAttachmentHost(hostname, extraHosts) {
|
|
59
64
|
const h = hostname.toLowerCase();
|
|
60
|
-
return OAI_USERCONTENT_HOST.test(h) ||
|
|
65
|
+
return (OAI_USERCONTENT_HOST.test(h) ||
|
|
66
|
+
OAI_BLOB_ACCOUNTS.has(h) ||
|
|
67
|
+
!!extraHosts?.has(h));
|
|
61
68
|
}
|
|
62
69
|
/** https, default port, allowed host — applied to the first URL and every redirect. */
|
|
63
|
-
function checkAttachmentUrl(url) {
|
|
70
|
+
function checkAttachmentUrl(url, extraHosts) {
|
|
64
71
|
if (url.protocol !== 'https:')
|
|
65
72
|
return 'is not an https URL';
|
|
66
73
|
if (url.port)
|
|
67
74
|
return 'uses a non-default port';
|
|
68
|
-
if (!isAllowedAttachmentHost(url.hostname)) {
|
|
69
|
-
return `host "${url.hostname}" is not a
|
|
75
|
+
if (!isAllowedAttachmentHost(url.hostname, extraHosts)) {
|
|
76
|
+
return `host "${url.hostname}" is not a chat attachment host`;
|
|
70
77
|
}
|
|
71
78
|
return undefined;
|
|
72
79
|
}
|
|
@@ -98,7 +105,7 @@ function toBase64(bytes) {
|
|
|
98
105
|
* GET with manual redirects: every hop must pass checkAttachmentUrl, so an
|
|
99
106
|
* allowed host cannot bounce the server to an arbitrary or internal one.
|
|
100
107
|
*/
|
|
101
|
-
async function fetchAllowed(start, fetchImpl, label) {
|
|
108
|
+
async function fetchAllowed(start, fetchImpl, label, extraHosts) {
|
|
102
109
|
let url = start;
|
|
103
110
|
for (let hop = 0;; hop++) {
|
|
104
111
|
const res = await fetchImpl(url.toString(), { redirect: 'manual' });
|
|
@@ -117,7 +124,7 @@ async function fetchAllowed(start, fetchImpl, label) {
|
|
|
117
124
|
catch {
|
|
118
125
|
throw new AttachmentError(`${label}: redirect to an invalid URL.`);
|
|
119
126
|
}
|
|
120
|
-
const problem = checkAttachmentUrl(next);
|
|
127
|
+
const problem = checkAttachmentUrl(next, extraHosts);
|
|
121
128
|
if (problem) {
|
|
122
129
|
throw new AttachmentError(`${label}: the download redirected to a URL that ${problem}; refused.`);
|
|
123
130
|
}
|
|
@@ -166,7 +173,7 @@ async function readWithin(res, budget, overBudget) {
|
|
|
166
173
|
* (ChatGPT passed a sandbox path or a bare id instead of a URL), a host we
|
|
167
174
|
* don't trust, a failed download, or more bytes than MAX_ATTACHMENTS_BYTES.
|
|
168
175
|
*/
|
|
169
|
-
export async function resolveAttachments(attachments, fetchImpl = fetch) {
|
|
176
|
+
export async function resolveAttachments(attachments, fetchImpl = fetch, extraHosts) {
|
|
170
177
|
if (!Array.isArray(attachments) || attachments.length === 0)
|
|
171
178
|
return [];
|
|
172
179
|
const items = [];
|
|
@@ -185,9 +192,9 @@ export async function resolveAttachments(attachments, fetchImpl = fetch) {
|
|
|
185
192
|
if (!a || !url) {
|
|
186
193
|
throw new AttachmentError(`attachments[${i}] was not resolved to a download URL (got ${JSON.stringify(typeof raw === 'string' ? raw : ((a && a.download_url) ?? raw))}). \`attachments\` works only in ChatGPT, which swaps each file for a download URL before the call reaches Wix. Here it arrived as written, and Wix cannot read a path on your machine, so retrying the same value fails the same way. Upload another way. With a shell that reaches the internet, send the file from there (for a site, the StartDeployWebsite recipe's curl drop). Otherwise leave \`attachments\` out and put the files in \`files\`: text as is, and a binary as a \`=== FILE: <path> base64 ===\` entry only when it is a few KB (an icon, a logo), since a larger base64 payload does not survive your own output. A larger image goes in by public URL or through the user (the Media Manager).`);
|
|
187
194
|
}
|
|
188
|
-
const problem = checkAttachmentUrl(url);
|
|
195
|
+
const problem = checkAttachmentUrl(url, extraHosts);
|
|
189
196
|
if (problem) {
|
|
190
|
-
throw new AttachmentError(`attachments[${i}].download_url ${problem}.
|
|
197
|
+
throw new AttachmentError(`attachments[${i}].download_url ${problem}. \`attachments\` takes only files the chat platform attached and resolved; leave it out. For a file at a URL, import it in \`code\` with \`POST https://www.wixapis.com/site-media/v1/files/import\` and \`{ url }\` (Wix fetches it); for a small file you have as text, use the \`files\` bundle.`);
|
|
191
198
|
}
|
|
192
199
|
items.push({ a, url });
|
|
193
200
|
});
|
|
@@ -198,7 +205,7 @@ export async function resolveAttachments(attachments, fetchImpl = fetch) {
|
|
|
198
205
|
const label = `attachments[${i}] (${path})`;
|
|
199
206
|
let res;
|
|
200
207
|
try {
|
|
201
|
-
res = await fetchAllowed(url, fetchImpl, label);
|
|
208
|
+
res = await fetchAllowed(url, fetchImpl, label, extraHosts);
|
|
202
209
|
}
|
|
203
210
|
catch (e) {
|
|
204
211
|
if (e instanceof AttachmentError)
|
|
@@ -20,8 +20,13 @@ export interface CodeModeOptions {
|
|
|
20
20
|
};
|
|
21
21
|
disableTools?: string[];
|
|
22
22
|
toolDescriptions?: Record<string, string>;
|
|
23
|
-
/** Max response size in chars before truncation (
|
|
23
|
+
/** Max response size in chars before truncation (defaults: ExecuteWixAPI 100,000, SearchWixAPISpec 64,000). */
|
|
24
24
|
maxResponseSize?: number;
|
|
25
|
+
/**
|
|
26
|
+
* The connection's docsSite (see ../docs/mintlify-docs.ts). SearchWixAPISpec
|
|
27
|
+
* passes it on, so a Mintlify mode also finds schemas by Mintlify page URL.
|
|
28
|
+
*/
|
|
29
|
+
docsSite?: string;
|
|
25
30
|
}
|
|
26
31
|
export declare const EXECUTE_TOOL_NAME = "ExecuteWixAPI";
|
|
27
32
|
export declare function addCodeModeTools(server: WixMcpServer, options: CodeModeOptions): Promise<void>;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { AttachmentError, resolveAttachments } from './attachments.js';
|
|
2
|
+
import { AttachmentError, WIX_AI_ASSISTANT_ATTACHMENT_HOSTS, resolveAttachments } from './attachments.js';
|
|
3
|
+
import { fileParamNote } from '../config/file-params.js';
|
|
3
4
|
import { logger } from '../infra/logger.js';
|
|
4
5
|
import { defaultToolDescriptions } from '../config/default-config.js';
|
|
5
6
|
import { truncateResponse } from './truncate-response.js';
|
|
6
7
|
import { MAX_ALLOWED_RESPONSE_CHARS } from '../infra/response-size-limits.js';
|
|
7
8
|
import { validateExecuteCode } from './validation.js';
|
|
8
9
|
import { desc } from '../config/param-descriptions.js';
|
|
10
|
+
import { isMintlifyDocsSite, rewriteDocsLinks, toDevWixHostText } from '../docs/mintlify-docs.js';
|
|
9
11
|
import { rethrowIfUnauthorized } from '../auth/index.js';
|
|
10
12
|
import { WixFetchError } from '../wix-fetch-error.js';
|
|
11
13
|
import { addUploadHeadlessWebsiteFilesTool, UPLOAD_HEADLESS_WEBSITE_FILES_TOOL_NAME } from '../api-call/upload-headless-website-files.js';
|
|
@@ -44,13 +46,13 @@ export async function addCodeModeTools(server, options) {
|
|
|
44
46
|
* lightIndex.
|
|
45
47
|
*/
|
|
46
48
|
export function addSearchCodeModeTool(server, options) {
|
|
47
|
-
const { codeModeBaseUrl, codeModeFetch, authStrategy, disableTools = [], toolDescriptions, maxResponseSize } = options;
|
|
49
|
+
const { codeModeBaseUrl, codeModeFetch, authStrategy, disableTools = [], toolDescriptions, maxResponseSize, docsSite } = options;
|
|
48
50
|
if (disableTools.includes(SEARCH_TOOL_NAME)) {
|
|
49
51
|
return;
|
|
50
52
|
}
|
|
51
53
|
const description = toolDescriptions?.[SEARCH_TOOL_NAME] ??
|
|
52
54
|
defaultToolDescriptions.SearchWixAPISpec;
|
|
53
|
-
registerSearchTool(server, codeModeBaseUrl, description, maxResponseSize, authStrategy, codeModeFetch);
|
|
55
|
+
registerSearchTool(server, codeModeBaseUrl, description, maxResponseSize, authStrategy, codeModeFetch, docsSite);
|
|
54
56
|
}
|
|
55
57
|
export function addExecuteCodeModeTool(server, options) {
|
|
56
58
|
const { codeModeBaseUrl, codeModeFetch, authStrategy, visitor, disableTools = [], toolDescriptions, maxResponseSize } = options;
|
|
@@ -67,6 +69,9 @@ export function addExecuteCodeModeTool(server, options) {
|
|
|
67
69
|
});
|
|
68
70
|
}
|
|
69
71
|
const SEARCH_MAX_RESPONSE_CHARS = 64_000;
|
|
72
|
+
// ExecuteWixAPI's own default, ~25,000 tokens, so a full API listing comes back
|
|
73
|
+
// whole. A session's maxResponseSize still overrides it.
|
|
74
|
+
const EXECUTE_MAX_RESPONSE_CHARS = 100_000;
|
|
70
75
|
/**
|
|
71
76
|
* Search needs no auth of its own; without a header the call is bucketed by the
|
|
72
77
|
* Worker's own IP (see postExecute). Anonymous sessions throw here and stay on
|
|
@@ -80,7 +85,7 @@ async function resolveLoopbackAuthorization(authStrategy) {
|
|
|
80
85
|
return undefined;
|
|
81
86
|
}
|
|
82
87
|
}
|
|
83
|
-
function registerSearchTool(server, codeModeBaseUrl, description, maxResponseSize, authStrategy, codeModeFetch = defaultCodeModeFetch) {
|
|
88
|
+
function registerSearchTool(server, codeModeBaseUrl, description, maxResponseSize, authStrategy, codeModeFetch = defaultCodeModeFetch, docsSite) {
|
|
84
89
|
const searchUrl = `${codeModeBaseUrl}/api/code-mode/search`;
|
|
85
90
|
server.tool(SEARCH_TOOL_NAME, description, {
|
|
86
91
|
code: z
|
|
@@ -99,7 +104,8 @@ function registerSearchTool(server, codeModeBaseUrl, description, maxResponseSiz
|
|
|
99
104
|
'Content-Type': 'application/json',
|
|
100
105
|
...(authorization ? { Authorization: authorization } : {})
|
|
101
106
|
},
|
|
102
|
-
|
|
107
|
+
// docsSite only when set: without it the request is as before.
|
|
108
|
+
body: JSON.stringify(docsSite ? { code, docsSite } : { code })
|
|
103
109
|
});
|
|
104
110
|
if (!resp.ok) {
|
|
105
111
|
const errorBody = await resp.text();
|
|
@@ -126,7 +132,13 @@ function registerSearchTool(server, codeModeBaseUrl, description, maxResponseSiz
|
|
|
126
132
|
]
|
|
127
133
|
};
|
|
128
134
|
}
|
|
129
|
-
|
|
135
|
+
// Mintlify docs: the index shows docsUrls on the Mintlify host by rule.
|
|
136
|
+
// Map them back, then resolve each to its Mintlify page as the docs tools
|
|
137
|
+
// do, so a page the site doesn't have keeps its dev.wix.com link.
|
|
138
|
+
const result = isMintlifyDocsSite(docsSite) && data.result !== undefined
|
|
139
|
+
? JSON.parse((await rewriteDocsLinks(toDevWixHostText(JSON.stringify(data.result)), { label: 'search SearchWixAPISpec' })).text)
|
|
140
|
+
: data.result;
|
|
141
|
+
const text = truncateResponse(result, maxResponseSize ?? SEARCH_MAX_RESPONSE_CHARS);
|
|
130
142
|
return {
|
|
131
143
|
content: [{ type: 'text', text }]
|
|
132
144
|
};
|
|
@@ -167,10 +179,13 @@ async function postExecute(executeUrl, body, authorization, codeModeFetch = defa
|
|
|
167
179
|
}
|
|
168
180
|
return (await resp.json());
|
|
169
181
|
}
|
|
170
|
-
function executeErrorResult(message) {
|
|
182
|
+
function executeErrorResult(message, failedCalls) {
|
|
171
183
|
return {
|
|
172
184
|
isError: true,
|
|
173
|
-
content: [{ type: 'text', text: `Execute error: ${message}` }]
|
|
185
|
+
content: [{ type: 'text', text: `Execute error: ${message}` }],
|
|
186
|
+
// The failed calls as fields, request ids included. The text carries the
|
|
187
|
+
// ids too, for clients that don't read structured results.
|
|
188
|
+
...(failedCalls?.length ? { structuredContent: { failedCalls } } : {})
|
|
174
189
|
};
|
|
175
190
|
}
|
|
176
191
|
function executeTextResult(message) {
|
|
@@ -180,7 +195,7 @@ function executeTextResult(message) {
|
|
|
180
195
|
}
|
|
181
196
|
function formatExecuteResult(data, maxResponseSize, reportBi) {
|
|
182
197
|
if (data.error) {
|
|
183
|
-
return executeErrorResult(data.error);
|
|
198
|
+
return executeErrorResult(data.error, data.failedCalls);
|
|
184
199
|
}
|
|
185
200
|
// Before the slice -- resultTextLength is measured after it and saturates.
|
|
186
201
|
reportBi?.({
|
|
@@ -203,7 +218,10 @@ function formatExecuteResult(data, maxResponseSize, reportBi) {
|
|
|
203
218
|
}
|
|
204
219
|
return {
|
|
205
220
|
content: [
|
|
206
|
-
{
|
|
221
|
+
{
|
|
222
|
+
type: 'text',
|
|
223
|
+
text: truncateResponse(data.result, maxResponseSize ?? EXECUTE_MAX_RESPONSE_CHARS)
|
|
224
|
+
}
|
|
207
225
|
]
|
|
208
226
|
};
|
|
209
227
|
}
|
|
@@ -269,7 +287,8 @@ function addExecuteTool(server, options) {
|
|
|
269
287
|
mime_type: z.string().optional()
|
|
270
288
|
}))
|
|
271
289
|
.optional()
|
|
272
|
-
.describe('
|
|
290
|
+
.describe(fileParamNote('use `files`, or upload from your shell if it reaches the internet') +
|
|
291
|
+
' Any type: a zip of a site, HTML, images, PDFs. The bytes are fetched here and appended to the `files` global as { path, content, encoding: "base64" } entries (path = file_name), so `wix.multipart()` and `wix.unzip()` work on them unchanged. Prefer this over base64 in `files` whenever you have a file handle. Up to 10 MB per call.'),
|
|
273
292
|
// Visitor mode takes an agent-supplied token and pins the site; account/site
|
|
274
293
|
// mode lets the agent pick the site.
|
|
275
294
|
...(visitor
|
|
@@ -309,7 +328,7 @@ function addExecuteTool(server, options) {
|
|
|
309
328
|
// an agent-facing error, not a server failure.
|
|
310
329
|
let attachmentFiles = [];
|
|
311
330
|
try {
|
|
312
|
-
attachmentFiles = await resolveAttachments(attachments);
|
|
331
|
+
attachmentFiles = await resolveAttachments(attachments, undefined, WIX_AI_ASSISTANT_ATTACHMENT_HOSTS);
|
|
313
332
|
}
|
|
314
333
|
catch (e) {
|
|
315
334
|
if (e instanceof AttachmentError)
|
|
@@ -360,6 +379,7 @@ function addExecuteTool(server, options) {
|
|
|
360
379
|
sessionId,
|
|
361
380
|
toolInvocationId,
|
|
362
381
|
clientName: server.server?.getClientVersion?.()?.name,
|
|
382
|
+
...(visitor ? { visitorMode: true } : {}),
|
|
363
383
|
// A visitor token is a site-scoped bearer token: sent as siteToken
|
|
364
384
|
// (→ Authorization + wix-site-id) and as accountToken only to satisfy
|
|
365
385
|
// the runtime's required field. Account/site mode uses strategy tokens.
|
|
@@ -17,6 +17,10 @@ export function truncateResponse(content, maxChars = DEFAULT_MAX_CHARS) {
|
|
|
17
17
|
const truncated = text.slice(0, effectiveMax);
|
|
18
18
|
const estimatedTokens = Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
19
19
|
const limitTokens = Math.ceil(effectiveMax / CHARS_PER_TOKEN);
|
|
20
|
-
|
|
20
|
+
const cutChars = text.length - effectiveMax;
|
|
21
|
+
const note = `Response was ~${estimatedTokens.toLocaleString()} tokens (limit: ${limitTokens.toLocaleString()}). Showing the first ${effectiveMax.toLocaleString()} of ${text.length.toLocaleString()} characters; the last ${cutChars.toLocaleString()} were cut. To get the rest, run again and return less per call — only the fields you need, the next page (cursor or offset), or the next slice of a large array — and repeat until you have it all.`;
|
|
22
|
+
// At the top as well as the end: a client that cuts a long result keeps its
|
|
23
|
+
// head, so a note only at the end is the first thing lost.
|
|
24
|
+
return `[TRUNCATED: ${note}]\n\n${truncated}\n\n--- TRUNCATED ---\n${note}`;
|
|
21
25
|
}
|
|
22
26
|
//# sourceMappingURL=truncate-response.js.map
|
|
@@ -21,29 +21,48 @@ export interface ModeDefaultConfig {
|
|
|
21
21
|
skillsPrOverrideEnabled?: boolean;
|
|
22
22
|
graphqlEnabled?: boolean;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* CallWixSiteAPI's advice when the Wix MCP lists no ExecuteWixAPI. True only where the server serves
|
|
26
|
+
* ExecuteWixAPI (a client with an old or cached tool list); `addApiCallTool` swaps it for
|
|
27
|
+
* `CALL_WIX_SITE_API_NO_EXECUTE_LINE` where ExecuteWixAPI is off on purpose.
|
|
28
|
+
*/
|
|
29
|
+
export declare const CALL_WIX_SITE_API_RECONNECT_LINE = "If the Wix MCP offers no ExecuteWixAPI at all, your Wix connection is likely an old or cached version: tell the user once to reconnect the Wix connector or restart the client, and use this tool until then.";
|
|
30
|
+
export declare const CALL_WIX_SITE_API_NO_EXECUTE_LINE = "If ExecuteWixAPI isn't in your tool list, use this tool.";
|
|
24
31
|
export declare const SYSTEM_REMINDER_GUIDELINES: string;
|
|
25
32
|
export declare function buildSystemReminder(includeReadme: boolean, includeImageUploadGuidance?: boolean): string;
|
|
26
33
|
export declare const SYSTEM_REMINDER: string;
|
|
34
|
+
/** SearchWixHelpCenter's description and response both carry this: Help Center is for people, the developer docs for the agent's own actions. */
|
|
35
|
+
export declare const HELP_CENTER_IS_FOR_PEOPLE = "The Help Center is written for people doing things by hand in Wix's dashboard and editors. To act on a site yourself, the Wix developer docs come first: find the method with SearchWixRESTDocumentation or BrowseWixRESTDocsMenu.";
|
|
36
|
+
/**
|
|
37
|
+
* The MCP server's \`instructions\`, sent in its reply to initialize. Clients that show them
|
|
38
|
+
* (ChatGPT and Codex use the first 512 characters; Claude Code loads them at session start and
|
|
39
|
+
* cuts at 4,096) get the site-creation routing before any tool definition. Sent only to a session
|
|
40
|
+
* that has every tool it names (SERVER_INSTRUCTIONS_TOOLS).
|
|
41
|
+
* Each tool named here carries a comment pointing back to this text. A change to any of them (a
|
|
42
|
+
* name, what it is for, a new site-creation tool) updates this text and that list with it.
|
|
43
|
+
*/
|
|
44
|
+
export declare const SERVER_INSTRUCTIONS = "Wix: build and manage Wix sites. A new 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 (forms, a store, bookings, a CMS, members, payments). WixHarmonyEditorBuild or SearchSiteTemplates only when the user asked for Wix's editor, the Wix Harmony Editor, a template, Wix Studio or the Classic Editor. Call WixREADME first, except ListWixSites, UploadImageToWixSite, GetSuggestedDomains.";
|
|
45
|
+
/** The tools SERVER_INSTRUCTIONS names. */
|
|
46
|
+
export declare const SERVER_INSTRUCTIONS_TOOLS: string[];
|
|
27
47
|
export declare const SITE_CREATION_COPY: {
|
|
28
48
|
readonly templatePick: {
|
|
29
49
|
readonly nonWidget: "After the user selects a template, call `CreateSiteFromTemplate`.";
|
|
30
50
|
readonly widget: "The gallery creates the site when the user picks one.";
|
|
31
51
|
};
|
|
32
|
-
readonly unstatedMethod: {
|
|
33
|
-
readonly nonWidget: "you MUST ask whether they want to build with AI or start from a template, and create nothing and show nothing until they answer";
|
|
34
|
-
readonly widget: "you MUST call `WixSiteCreation` and let them choose";
|
|
35
|
-
};
|
|
36
52
|
};
|
|
37
53
|
/** Rewrites non-widget copy into its widget form; a no-op on text that carries no divergence. */
|
|
38
54
|
export declare const toWidget: (text: string) => string;
|
|
39
55
|
export declare const executeWixApiDescription: string;
|
|
40
|
-
export declare function buildToolDescriptions(includeReadme: boolean, includeImageUploadGuidance?: boolean
|
|
56
|
+
export declare function buildToolDescriptions(includeReadme: boolean, includeImageUploadGuidance?: boolean, { usageGuideOnce }?: {
|
|
57
|
+
usageGuideOnce?: boolean;
|
|
58
|
+
}): Record<string, string>;
|
|
41
59
|
/**
|
|
42
60
|
* Canonical (README-inclusive) tool descriptions used by the live MCP server and
|
|
43
61
|
* all existing importers.
|
|
44
62
|
*/
|
|
45
63
|
export declare const defaultToolDescriptions: Record<string, string>;
|
|
46
64
|
export declare const executeCodeModeToolDescriptions: Record<string, string>;
|
|
65
|
+
export declare const WIX_SKILLS_README_SECTION: string;
|
|
47
66
|
/**
|
|
48
67
|
* Default readme docs - content for WixREADME tool response.
|
|
49
68
|
* Assumes both ExecuteWixAPI and SearchWixAPISpec are available (execute-and-search mode).
|
|
@@ -51,6 +70,9 @@ export declare const executeCodeModeToolDescriptions: Record<string, string>;
|
|
|
51
70
|
* Joined rather than interpolated into one `dedent` — `dedent` reads indentation from the literal
|
|
52
71
|
* parts only, so a multi-line interpolation gets flattened.
|
|
53
72
|
*/
|
|
73
|
+
export declare function buildReadmeDocs({ recipesIndex }?: {
|
|
74
|
+
recipesIndex?: boolean;
|
|
75
|
+
}): ReadmeDocConfig[];
|
|
54
76
|
export declare const defaultReadmeDocs: ReadmeDocConfig[];
|
|
55
77
|
/**
|
|
56
78
|
* Merge base config with overrides
|
|
@@ -64,6 +86,14 @@ export declare function mergeConfig(base: ModeDefaultConfig, overrides: Partial<
|
|
|
64
86
|
* All tools enabled, all descriptions, good readmeDocs
|
|
65
87
|
*/
|
|
66
88
|
export declare const defaultConfig: ModeDefaultConfig;
|
|
89
|
+
/**
|
|
90
|
+
* For a session without WixREADME (mode=code, getToKnowWixEnabled off, WixREADME disabled): the
|
|
91
|
+
* usage guide WixREADME carries goes back onto every tool, as it was before usageGuideOnce.
|
|
92
|
+
* Custom tool descriptions are kept.
|
|
93
|
+
*/
|
|
94
|
+
export declare function withUsageGuideOnEveryTool<Config extends {
|
|
95
|
+
toolDescriptions?: Record<string, string>;
|
|
96
|
+
}>(config: Config): Config;
|
|
67
97
|
/**
|
|
68
98
|
* Delta for clients that declared the MCP-Apps UI extension. Merged over the mode config at
|
|
69
99
|
* request time, since the mode override and the widget variant have to compose.
|