@wix/mcp 1.0.85 → 1.0.87
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/build/bin-standalone.js +97 -16
- package/build/cjs/index.cjs +126 -22
- package/build/cjs/toolkit.cjs +113 -16
- package/build/dts/code-mode/index.js +28 -0
- package/build/dts/config/api-request-rules.d.ts +1 -0
- package/build/dts/config/api-request-rules.js +1 -0
- package/build/dts/config/default-config.js +17 -3
- package/build/dts/config/param-descriptions.js +5 -2
- package/build/dts/docs/docs.js +5 -3
- package/build/dts/docs/get-to-know.js +14 -11
- package/build/dts/import-claude-design-tool/index.js +2 -2
- package/build/dts/infra/tool-bi.d.ts +5 -1
- package/build/dts/infra/tool-bi.js +22 -1
- package/build/dts/tool-utils.js +12 -1
- package/build/dts/toolkit-entry.d.cts +5 -1
- package/build/dts/toolkit-entry.d.ts +5 -1
- package/build/dts/toolkit.js +1 -1
- package/build/dts/wix-mcp-server.js +3 -0
- package/build/esm/index.js +126 -22
- package/build/esm/toolkit.js +113 -16
- package/package.json +3 -3
package/build/cjs/toolkit.cjs
CHANGED
|
@@ -17655,6 +17655,7 @@ function rethrowIfUnauthorized(error) {
|
|
|
17655
17655
|
|
|
17656
17656
|
// src/config/api-request-rules.ts
|
|
17657
17657
|
var ARRAY_QUERY_PARAMS_RULE = "Repeat array query params once per value: `?fieldsets=URL&fieldsets=RICH_CONTENT`. Do NOT comma-join values (`fieldsets=URL,RICH_CONTENT`), do NOT leave a trailing comma, and do NOT use a value the method schema does not list, even if a sibling method accepts it (draft posts have no `SEO` fieldset).";
|
|
17658
|
+
var WQL_FILTER_RULE = "Query and search filters are written in WQL: each field takes a single operator, so combine conditions with the logical operators \u2014 `$and` and `$or` take an array of expressions, `$not` takes one, and they can nest.";
|
|
17658
17659
|
|
|
17659
17660
|
// src/config/default-config.ts
|
|
17660
17661
|
var AGENT_GOAL = dedent_default`
|
|
@@ -17755,7 +17756,7 @@ var SITE_CREATION_COPY = {
|
|
|
17755
17756
|
var SITE_GUIDANCE = dedent_default`
|
|
17756
17757
|
A new-site request has two things to settle: what the site is for, and how to build it. The how is AI generation vs. starting from a template, so naming templates, a template id or Wix Studio (Wix Studio is also the template side) already settles it.
|
|
17757
17758
|
|
|
17758
|
-
One case skips all of this: the user ALREADY HAS the finished site as files — hand-written HTML/CSS/JS, a zip, or the export of an AI builder. There is nothing to build, so don't offer AI or templates and don't call any site-creation tool.
|
|
17759
|
+
One case skips all of this: the user ALREADY HAS the finished site as files — hand-written HTML/CSS/JS, a zip, or the export of an AI builder. There is nothing to build, so don't offer AI or templates and don't call any site-creation tool. For a full agentic flow, read the recipe first and publish the files yourself: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md — it walks you through creating the site and uploading the files end to end. If you can't follow it, or it doesn't apply (you can't reach the file bytes, or something fails), fall back to sending the user to https://www.wix.com/headless/drop?utm_campaign=mcp — they drag the files in and get a live Wix-hosted site. Give that URL exactly as written, keeping the utm_campaign=mcp parameter.
|
|
17759
17760
|
|
|
17760
17761
|
Settle what the site is for first: its purpose, topic, business type, audience, or main visitor action. Naming a method is not a description of the site, and neither is the business or industry on its own.
|
|
17761
17762
|
If you can't tell yet, ask the preferred question below and nothing else — one question, no creation-method question in the same reply, and no internal tool names.
|
|
@@ -17814,12 +17815,25 @@ var ACCOUNT_SITE_EXECUTE_SCOPE = dedent_default`
|
|
|
17814
17815
|
siteId?: string;
|
|
17815
17816
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
17816
17817
|
url: string;
|
|
17818
|
+
// Plain object → JSON. A string is sent verbatim (set your own Content-Type,
|
|
17819
|
+
// e.g. multipart/form-data). A Uint8Array/ArrayBuffer is sent as raw bytes
|
|
17820
|
+
// for binary uploads (set your own Content-Type).
|
|
17817
17821
|
body?: unknown;
|
|
17818
17822
|
headers?: Record<string, string>;
|
|
17823
|
+
// "base64" returns the response body as a base64 string (data), with
|
|
17824
|
+
// encoding: "base64" on the result — use it for binary downloads (zip,
|
|
17825
|
+
// image, pdf); decoding those as text corrupts them. Binary content types
|
|
17826
|
+
// are returned as base64 automatically even without this. Decode, edit and
|
|
17827
|
+
// re-upload binary WITHIN this script and return only a small summary. If you
|
|
17828
|
+
// must return the bytes, return the wix.request response object itself (so
|
|
17829
|
+
// encoding: "base64" is preserved and it isn't truncated); a bare base64
|
|
17830
|
+
// string you return is treated as plain text and may be truncated.
|
|
17831
|
+
responseType?: "base64";
|
|
17819
17832
|
}
|
|
17820
17833
|
interface WixResponse<T = unknown> {
|
|
17821
17834
|
status: number;
|
|
17822
17835
|
data: T;
|
|
17836
|
+
encoding?: "base64";
|
|
17823
17837
|
json(): Promise<T>;
|
|
17824
17838
|
}
|
|
17825
17839
|
declare const wix: {
|
|
@@ -17958,6 +17972,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
17958
17972
|
ReadFullDocsArticle: dedent_default`
|
|
17959
17973
|
Fetches the full Wix docs article or method article with code examples for using the method.
|
|
17960
17974
|
Docs articles looks like this: https://dev.wix.com/docs/... and they can either be general docs articles or method articles.
|
|
17975
|
+
Take the URL from the user's message or an earlier tool result. To reach an article you have no URL for, search or browse the docs first.
|
|
17961
17976
|
For REST docs, use the URL as-is. For SDK docs, the URL SHOULD include ?apiView=SDK.
|
|
17962
17977
|
${reminder}
|
|
17963
17978
|
`,
|
|
@@ -18090,7 +18105,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
18090
18105
|
`,
|
|
18091
18106
|
"import-claude-design-from-url": dedent_default`
|
|
18092
18107
|
Import a design created in Claude's design tool into Wix. Takes the signed claudeusercontent.com URL Claude Design produces for its self-contained HTML bundle (all images, fonts, and styles inlined); any other host is rejected. Creates a live Wix-hosted site and returns its URL.
|
|
18093
|
-
NOT for site files the user provides themselves (their own HTML/CSS/JS, a zip, another builder's export) — for those, don't call this tool:
|
|
18108
|
+
NOT for site files the user provides themselves (their own HTML/CSS/JS, a zip, another builder's export) — for those, don't call this tool. For a full agentic flow, read the recipe and publish the files yourself: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md — it covers creating the site and uploading the files end to end. If you can't follow it, or it doesn't apply, fall back to sending the user to https://www.wix.com/headless/drop?utm_campaign=mcp, where they upload the files and get a live hosted site. Give that URL exactly as written, keeping the utm_campaign=mcp parameter.
|
|
18094
18109
|
`,
|
|
18095
18110
|
RunWixCliCommand: dedent_default`
|
|
18096
18111
|
Executes Wix CLI commands for Wix app development and management.
|
|
@@ -18517,7 +18532,7 @@ function buildToolDescriptions(includeReadme, includeImageUploadGuidance = true)
|
|
|
18517
18532
|
CreateWixBusinessGuide: dedent_default`
|
|
18518
18533
|
Provides comprehensive documentation for creating a new Wix business (site, app, etc.).
|
|
18519
18534
|
Call this ONLY when the user explicitly asks for a headless site, a Wix Classic Editor site, or manual/advanced account/API-based site creation — these don't need the site's purpose first. For normal site-creation ${SITE_CREATION_COPY.normalCreationRoute.nonWidget}; for a specific template id, call \`CreateSiteFromTemplate\`.
|
|
18520
|
-
Exception, even when the user says "headless": if they ALREADY HAVE the finished site as files (their own HTML/CSS/JS, a zip, an AI-builder export) and want those files hosted, don't call this tool —
|
|
18535
|
+
Exception, even when the user says "headless": if they ALREADY HAVE the finished site as files (their own HTML/CSS/JS, a zip, an AI-builder export) and want those files hosted, don't call this tool. For a full agentic flow, read the recipe and publish the files yourself: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md — it covers creating the site and uploading the files end to end. If you can't follow it, or it doesn't apply, fall back to sending the user to https://www.wix.com/headless/drop?utm_campaign=mcp to upload the files and get a live hosted site (give that URL exactly, keeping the utm_campaign=mcp parameter). This guide's CLI flow is for BUILDING a headless frontend, not for hosting ready-made files.
|
|
18521
18536
|
|
|
18522
18537
|
## What This Tool Contains
|
|
18523
18538
|
|
|
@@ -18804,6 +18819,22 @@ function renderApps(apps, knownApps) {
|
|
|
18804
18819
|
}
|
|
18805
18820
|
return lines.join("\n");
|
|
18806
18821
|
}
|
|
18822
|
+
function sanitizeCustomAppName(raw) {
|
|
18823
|
+
return raw.replace(/[\x00-\x1f\x7f]/g, " ").replace(/[`*_[\]|<>]/g, "").replace(/\s+/g, " ").trim().slice(0, 60);
|
|
18824
|
+
}
|
|
18825
|
+
function renderCustomAppsWithSkillsTools(apps) {
|
|
18826
|
+
const relevant = apps.filter((a) => a.appId && (a.hasSkills || a.hasTools)).sort(
|
|
18827
|
+
(a, b) => (a.appName ?? a.appId ?? "").localeCompare(b.appName ?? b.appId ?? "")
|
|
18828
|
+
);
|
|
18829
|
+
if (!relevant.length) return "";
|
|
18830
|
+
const lines = [];
|
|
18831
|
+
for (const app of relevant) {
|
|
18832
|
+
const sanitizedName = app.appName ? sanitizeCustomAppName(app.appName) : "";
|
|
18833
|
+
const name = sanitizedName || app.appId;
|
|
18834
|
+
lines.push(`- \`${name}\` (ID: \`${app.appId}\`)`);
|
|
18835
|
+
}
|
|
18836
|
+
return lines.join("\n");
|
|
18837
|
+
}
|
|
18807
18838
|
function renderProperties(props) {
|
|
18808
18839
|
const sections = [];
|
|
18809
18840
|
const regionalFormat = [];
|
|
@@ -18866,6 +18897,15 @@ function renderSite(site, index, knownApps) {
|
|
|
18866
18897
|
lines.push("");
|
|
18867
18898
|
lines.push(appsContent);
|
|
18868
18899
|
}
|
|
18900
|
+
const customAppsWithSkillsToolsContent = renderCustomAppsWithSkillsTools(
|
|
18901
|
+
site.customApps ?? []
|
|
18902
|
+
);
|
|
18903
|
+
if (customAppsWithSkillsToolsContent) {
|
|
18904
|
+
lines.push("");
|
|
18905
|
+
lines.push("### Custom Apps with skills and tools");
|
|
18906
|
+
lines.push("");
|
|
18907
|
+
lines.push(customAppsWithSkillsToolsContent);
|
|
18908
|
+
}
|
|
18869
18909
|
lines.push("");
|
|
18870
18910
|
lines.push("---");
|
|
18871
18911
|
lines.push("");
|
|
@@ -19129,7 +19169,10 @@ var paramDescriptions = {
|
|
|
19129
19169
|
ARRAY_QUERY_PARAMS_RULE
|
|
19130
19170
|
].join("\n"),
|
|
19131
19171
|
method: "The HTTP method to use for the API call (e.g. GET, POST, PUT, DELETE)",
|
|
19132
|
-
body:
|
|
19172
|
+
body: [
|
|
19173
|
+
'The request body as a JSON object or array with all the required fields and values, including nested objects. Pass the actual value (object or array), NOT a JSON string. YOU MUST NEVER MAKE UP A BODY - the body should be based on the conversation context, i.e from the user prompt OR got into the conversation context by the "ReadFullDocsArticle" tool OR by the "SearchWixAPISpec" tool OR by the "ReadFullDocsMethodSchema" tool - i.e based on the API docs, a relevant recipe you read (preferably), a code example you found in the docs, a schema you read etc.. YOU MUST NEVER ASSUME YOU KNOW WHAT THE BODY SCHEMA IS WITHOUT CONCRETE EXAMPLES OR SCHEMA DEFINITIONS FROM THE CONVERSATION CONTEXT. Prefer reading relevant recipes if you have them in context for understand the body schema for API calls.',
|
|
19174
|
+
WQL_FILTER_RULE
|
|
19175
|
+
].join("\n"),
|
|
19133
19176
|
reason: "One sentence explaining the original user request and why you are calling this API to complete it.",
|
|
19134
19177
|
sourceDocUrl: [
|
|
19135
19178
|
"The URL of the documentation or recipe where you found this API endpoint.",
|
|
@@ -19453,15 +19496,17 @@ Failed to fetch content: ${errorMsg}`
|
|
|
19453
19496
|
const baseContentsPromise = Promise.all(
|
|
19454
19497
|
baseDocsWithoutRecipes.map(fetchDoc)
|
|
19455
19498
|
);
|
|
19456
|
-
const skillsContentPromise = getSkillsByPortal(httpClient, {
|
|
19499
|
+
const skillsContentPromise = skillsTags?.length ? getSkillsByPortal(httpClient, {
|
|
19457
19500
|
portalId: REST_PORTAL_ID,
|
|
19458
19501
|
...repo && { repo },
|
|
19459
19502
|
...commitHash && { commitHash },
|
|
19460
|
-
|
|
19503
|
+
tags: skillsTags
|
|
19461
19504
|
}).catch((err) => {
|
|
19462
19505
|
console.error("[WixREADME] Failed to fetch skills by portal:", err);
|
|
19463
19506
|
return null;
|
|
19464
|
-
})
|
|
19507
|
+
}) : fetchDoc({ action: "include", url: RECIPES_INDEX_URL }).then(
|
|
19508
|
+
(doc) => doc?.text ?? null
|
|
19509
|
+
);
|
|
19465
19510
|
const filter = {};
|
|
19466
19511
|
if (siteId) filter.siteId = siteId;
|
|
19467
19512
|
else if (siteName) filter.siteName = siteName;
|
|
@@ -19657,7 +19702,9 @@ function getErrorMessage(error) {
|
|
|
19657
19702
|
}
|
|
19658
19703
|
function shouldTryMenuFallbackForDocsRead(error, articleUrl) {
|
|
19659
19704
|
const message = getErrorMessage(error);
|
|
19660
|
-
|
|
19705
|
+
const { pathname } = new URL(articleUrl);
|
|
19706
|
+
const isMenuFallbackUrl = articleUrl.includes("/api-reference/") || pathname.startsWith("/docs/velo");
|
|
19707
|
+
if (!message.includes("404") || !isMenuFallbackUrl) {
|
|
19661
19708
|
return false;
|
|
19662
19709
|
}
|
|
19663
19710
|
return !["This resource is deprecated", "No schema found"].some(
|
|
@@ -20310,7 +20357,7 @@ var addDocsTools = (server, allowedTools = [
|
|
|
20310
20357
|
content: [
|
|
20311
20358
|
{
|
|
20312
20359
|
type: "text",
|
|
20313
|
-
text: `Note: "${articleUrl}" is
|
|
20360
|
+
text: `Note: "${articleUrl}" is not an article. Showing the closest documentation menu below; read the article you need from its links. For category URLs, use BrowseWixRESTDocsMenu directly next time.
|
|
20314
20361
|
|
|
20315
20362
|
${text}`
|
|
20316
20363
|
}
|
|
@@ -20402,7 +20449,7 @@ ${text}`
|
|
|
20402
20449
|
content: [
|
|
20403
20450
|
{
|
|
20404
20451
|
type: "text",
|
|
20405
|
-
text: `Note: "${articleUrl}" is
|
|
20452
|
+
text: `Note: "${articleUrl}" is not an article. Showing the closest documentation menu below; read the article you need from its links. For category URLs, use BrowseWixRESTDocsMenu directly next time.
|
|
20406
20453
|
|
|
20407
20454
|
${text}`
|
|
20408
20455
|
}
|
|
@@ -20498,6 +20545,14 @@ var import_http_client2 = __toESM(require_index_node(), 1);
|
|
|
20498
20545
|
|
|
20499
20546
|
// src/tool-utils.ts
|
|
20500
20547
|
var REST_METHOD_CONTRACT_RETRY_HINT = "Before retrying, make sure you have everything needed for a correct call: the right endpoint URL, HTTP method, request body shape, and response shape. Use the SearchWixRESTDocumentation tool or BrowseWixRESTDocsMenu tool to find the correct method or relevant related articles, the ReadFullDocsArticle tool to read the method description and all code examples, and the SearchWixAPISpec tool for precise field names and schema details.";
|
|
20548
|
+
var SINGLE_OPERATOR_PER_FIELD_HINT = 'WQL takes one operator per field object. Split the conditions into separate expressions under the matching logical operator \u2014 `{"$and": [{"field": {"$gte": ...}}, {"field": {"$lte": ...}}]}` \u2014 and retry without re-searching the docs.';
|
|
20549
|
+
var FIELD_CAPABILITIES_HINT = "This endpoint declares its filterable and sortable fields with their operators. Read the map with SearchWixAPISpec (`queryMethodData.queryFieldsCapabilitiesMap`, or `searchMethodData.searchFieldsCapabilitiesMap` on search methods) and use only what it lists; otherwise fetch a bounded page and filter or sort the results yourself.";
|
|
20550
|
+
var getRetryHint = (errorDetails) => {
|
|
20551
|
+
if (!/Filter contains unsupported operator/i.test(errorDetails)) {
|
|
20552
|
+
return REST_METHOD_CONTRACT_RETRY_HINT;
|
|
20553
|
+
}
|
|
20554
|
+
return /"operator":"\{/.test(errorDetails) ? SINGLE_OPERATOR_PER_FIELD_HINT : FIELD_CAPABILITIES_HINT;
|
|
20555
|
+
};
|
|
20501
20556
|
var safeParseJSON = (text) => {
|
|
20502
20557
|
try {
|
|
20503
20558
|
return JSON.parse(text);
|
|
@@ -20521,7 +20576,7 @@ var buildErrorMessage = ({
|
|
|
20521
20576
|
].filter((str) => !!str).join("\n");
|
|
20522
20577
|
return `${baseMessage}
|
|
20523
20578
|
|
|
20524
|
-
${
|
|
20579
|
+
${getRetryHint(String(errorDetails))}`;
|
|
20525
20580
|
};
|
|
20526
20581
|
var handleWixAPIResponse = async (response) => {
|
|
20527
20582
|
const requestId = response.headers?.["x-wix-request-id"];
|
|
@@ -22188,12 +22243,31 @@ function formatExecuteResult(data, maxResponseSize, reportBi) {
|
|
|
22188
22243
|
reportBi?.({
|
|
22189
22244
|
originalLength: typeof data.result === "string" ? data.result.length : JSON.stringify(data.result)?.length
|
|
22190
22245
|
});
|
|
22246
|
+
const base64Text = base64PayloadText(data.result);
|
|
22247
|
+
if (base64Text !== null) {
|
|
22248
|
+
if (base64Text.length <= MAX_ALLOWED_RESPONSE_CHARS) {
|
|
22249
|
+
return { content: [{ type: "text", text: base64Text }] };
|
|
22250
|
+
}
|
|
22251
|
+
const approxKb = Math.ceil(base64Text.length / 1024).toLocaleString();
|
|
22252
|
+
const limitKb = Math.floor(
|
|
22253
|
+
MAX_ALLOWED_RESPONSE_CHARS / 1024
|
|
22254
|
+
).toLocaleString();
|
|
22255
|
+
return executeErrorResult(
|
|
22256
|
+
`Binary (base64) result is ~${approxKb} KB, too large to return intact (limit ~${limitKb} KB); truncating it would corrupt the base64. Process the bytes inside your ExecuteWixAPI script \u2014 decode, edit, and re-upload in the same call \u2014 and return only a small summary.`
|
|
22257
|
+
);
|
|
22258
|
+
}
|
|
22191
22259
|
return {
|
|
22192
22260
|
content: [
|
|
22193
22261
|
{ type: "text", text: truncateResponse(data.result, maxResponseSize) }
|
|
22194
22262
|
]
|
|
22195
22263
|
};
|
|
22196
22264
|
}
|
|
22265
|
+
function base64PayloadText(result) {
|
|
22266
|
+
if (result !== null && typeof result === "object" && result.encoding === "base64" && typeof result.data === "string") {
|
|
22267
|
+
return JSON.stringify(result);
|
|
22268
|
+
}
|
|
22269
|
+
return null;
|
|
22270
|
+
}
|
|
22197
22271
|
function addExecuteTool(server, options) {
|
|
22198
22272
|
const {
|
|
22199
22273
|
codeModeBaseUrl,
|
|
@@ -22395,7 +22469,25 @@ function extractText(result) {
|
|
|
22395
22469
|
return result.content.filter((content) => content.type === "text" && content.text != null).map((content) => content.text).join("\n");
|
|
22396
22470
|
}
|
|
22397
22471
|
var TOOL_RESULT_PREVIEW_MAX_CHARS = 4e3;
|
|
22398
|
-
|
|
22472
|
+
var CODEX_OUTPUT_BUDGET_BYTES = 4e4;
|
|
22473
|
+
function getCodexTruncation(resultText, client) {
|
|
22474
|
+
const isCodex = [client?.name, client?.userAgent].some(
|
|
22475
|
+
(identity) => identity?.toLowerCase().includes("codex")
|
|
22476
|
+
);
|
|
22477
|
+
if (!isCodex) {
|
|
22478
|
+
return void 0;
|
|
22479
|
+
}
|
|
22480
|
+
const resultBytes = new TextEncoder().encode(resultText).length;
|
|
22481
|
+
if (resultBytes <= CODEX_OUTPUT_BUDGET_BYTES) {
|
|
22482
|
+
return void 0;
|
|
22483
|
+
}
|
|
22484
|
+
return {
|
|
22485
|
+
resultBytes,
|
|
22486
|
+
budgetBytes: CODEX_OUTPUT_BUDGET_BYTES,
|
|
22487
|
+
truncatedBytes: resultBytes - CODEX_OUTPUT_BUDGET_BYTES
|
|
22488
|
+
};
|
|
22489
|
+
}
|
|
22490
|
+
function formatToolResultAdditionalFields(result, extras, client) {
|
|
22399
22491
|
const resultText = extractText(result);
|
|
22400
22492
|
const contentTypes = [
|
|
22401
22493
|
...new Set(
|
|
@@ -22412,6 +22504,7 @@ function formatToolResultAdditionalFields(result, extras) {
|
|
|
22412
22504
|
resultTextLength: resultText.length,
|
|
22413
22505
|
resultPreviewTruncated: resultText.length > TOOL_RESULT_PREVIEW_MAX_CHARS,
|
|
22414
22506
|
resultContentTypes: contentTypes,
|
|
22507
|
+
codexTruncation: getCodexTruncation(resultText, client),
|
|
22415
22508
|
...extras
|
|
22416
22509
|
});
|
|
22417
22510
|
}
|
|
@@ -22667,10 +22760,14 @@ function createWixToolkit(options) {
|
|
|
22667
22760
|
if (!toolSucceeded) {
|
|
22668
22761
|
errorBody = extractText(cbResult);
|
|
22669
22762
|
} else {
|
|
22670
|
-
additionalFields = formatToolResultAdditionalFields(
|
|
22671
|
-
|
|
22672
|
-
|
|
22673
|
-
|
|
22763
|
+
additionalFields = formatToolResultAdditionalFields(
|
|
22764
|
+
cbResult,
|
|
22765
|
+
{
|
|
22766
|
+
returnedRows: toolBiParams.returnedRows,
|
|
22767
|
+
originalLength: toolBiParams.originalLength
|
|
22768
|
+
},
|
|
22769
|
+
{ name: clientName }
|
|
22770
|
+
);
|
|
22674
22771
|
}
|
|
22675
22772
|
if (!isDevEnvironment) {
|
|
22676
22773
|
if (toolSucceeded) {
|
|
@@ -2,6 +2,7 @@ import { z } from 'zod';
|
|
|
2
2
|
import { logger } from '../infra/logger.js';
|
|
3
3
|
import { defaultToolDescriptions } from '../config/default-config.js';
|
|
4
4
|
import { truncateResponse } from './truncate-response.js';
|
|
5
|
+
import { MAX_ALLOWED_RESPONSE_CHARS } from '../infra/response-size-limits.js';
|
|
5
6
|
import { validateExecuteCode } from './validation.js';
|
|
6
7
|
import { desc } from '../config/param-descriptions.js';
|
|
7
8
|
import { rethrowIfUnauthorized } from '../auth/index.js';
|
|
@@ -176,12 +177,39 @@ function formatExecuteResult(data, maxResponseSize, reportBi) {
|
|
|
176
177
|
? data.result.length
|
|
177
178
|
: JSON.stringify(data.result)?.length
|
|
178
179
|
});
|
|
180
|
+
// A base64/binary payload (a downloaded zip, image, …) must never be sliced:
|
|
181
|
+
// a cut base64 string decodes to garbage, so the usual char-limit truncation
|
|
182
|
+
// would silently corrupt it. Return it whole up to the hard ceiling; past
|
|
183
|
+
// that, fail cleanly rather than hand back a broken blob.
|
|
184
|
+
const base64Text = base64PayloadText(data.result);
|
|
185
|
+
if (base64Text !== null) {
|
|
186
|
+
if (base64Text.length <= MAX_ALLOWED_RESPONSE_CHARS) {
|
|
187
|
+
return { content: [{ type: 'text', text: base64Text }] };
|
|
188
|
+
}
|
|
189
|
+
const approxKb = Math.ceil(base64Text.length / 1024).toLocaleString();
|
|
190
|
+
const limitKb = Math.floor(MAX_ALLOWED_RESPONSE_CHARS / 1024).toLocaleString();
|
|
191
|
+
return executeErrorResult(`Binary (base64) result is ~${approxKb} KB, too large to return intact (limit ~${limitKb} KB); truncating it would corrupt the base64. Process the bytes inside your ExecuteWixAPI script — decode, edit, and re-upload in the same call — and return only a small summary.`);
|
|
192
|
+
}
|
|
179
193
|
return {
|
|
180
194
|
content: [
|
|
181
195
|
{ type: 'text', text: truncateResponse(data.result, maxResponseSize) }
|
|
182
196
|
]
|
|
183
197
|
};
|
|
184
198
|
}
|
|
199
|
+
// A base64 payload the caller returned as a WixResponse carrying
|
|
200
|
+
// `encoding: "base64"` — the shape `wix.request` produces for a binary body.
|
|
201
|
+
// We key off that explicit marker, never a content heuristic: a long string in
|
|
202
|
+
// the base64 alphabet can be ordinary text (e.g. "x".repeat(30000)) and must
|
|
203
|
+
// still truncate normally. Returns the text to keep intact, or null otherwise.
|
|
204
|
+
function base64PayloadText(result) {
|
|
205
|
+
if (result !== null &&
|
|
206
|
+
typeof result === 'object' &&
|
|
207
|
+
result.encoding === 'base64' &&
|
|
208
|
+
typeof result.data === 'string') {
|
|
209
|
+
return JSON.stringify(result);
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
185
213
|
function addExecuteTool(server, options) {
|
|
186
214
|
const { codeModeBaseUrl, codeModeFetch, authStrategy, visitor, toolDescriptions, maxResponseSize } = options;
|
|
187
215
|
const executeUrl = `${codeModeBaseUrl}/api/code-mode/execute`;
|
|
@@ -8,4 +8,5 @@
|
|
|
8
8
|
* single unknown enum member and rejected with HTTP 400.
|
|
9
9
|
*/
|
|
10
10
|
export declare const ARRAY_QUERY_PARAMS_RULE = "Repeat array query params once per value: `?fieldsets=URL&fieldsets=RICH_CONTENT`. Do NOT comma-join values (`fieldsets=URL,RICH_CONTENT`), do NOT leave a trailing comma, and do NOT use a value the method schema does not list, even if a sibling method accepts it (draft posts have no `SEO` fieldset).";
|
|
11
|
+
export declare const WQL_FILTER_RULE = "Query and search filters are written in WQL: each field takes a single operator, so combine conditions with the logical operators \u2014 `$and` and `$or` take an array of expressions, `$not` takes one, and they can nest.";
|
|
11
12
|
//# sourceMappingURL=api-request-rules.d.ts.map
|
|
@@ -8,4 +8,5 @@
|
|
|
8
8
|
* single unknown enum member and rejected with HTTP 400.
|
|
9
9
|
*/
|
|
10
10
|
export const ARRAY_QUERY_PARAMS_RULE = 'Repeat array query params once per value: `?fieldsets=URL&fieldsets=RICH_CONTENT`. Do NOT comma-join values (`fieldsets=URL,RICH_CONTENT`), do NOT leave a trailing comma, and do NOT use a value the method schema does not list, even if a sibling method accepts it (draft posts have no `SEO` fieldset).';
|
|
11
|
+
export const WQL_FILTER_RULE = 'Query and search filters are written in WQL: each field takes a single operator, so combine conditions with the logical operators — `$and` and `$or` take an array of expressions, `$not` takes one, and they can nest.';
|
|
11
12
|
//# sourceMappingURL=api-request-rules.js.map
|
|
@@ -80,7 +80,7 @@ export const SITE_CREATION_COPY = {
|
|
|
80
80
|
const SITE_GUIDANCE = dedent `
|
|
81
81
|
A new-site request has two things to settle: what the site is for, and how to build it. The how is AI generation vs. starting from a template, so naming templates, a template id or Wix Studio (Wix Studio is also the template side) already settles it.
|
|
82
82
|
|
|
83
|
-
One case skips all of this: the user ALREADY HAS the finished site as files — hand-written HTML/CSS/JS, a zip, or the export of an AI builder. There is nothing to build, so don't offer AI or templates and don't call any site-creation tool.
|
|
83
|
+
One case skips all of this: the user ALREADY HAS the finished site as files — hand-written HTML/CSS/JS, a zip, or the export of an AI builder. There is nothing to build, so don't offer AI or templates and don't call any site-creation tool. For a full agentic flow, read the recipe first and publish the files yourself: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md — it walks you through creating the site and uploading the files end to end. If you can't follow it, or it doesn't apply (you can't reach the file bytes, or something fails), fall back to sending the user to https://www.wix.com/headless/drop?utm_campaign=mcp — they drag the files in and get a live Wix-hosted site. Give that URL exactly as written, keeping the utm_campaign=mcp parameter.
|
|
84
84
|
|
|
85
85
|
Settle what the site is for first: its purpose, topic, business type, audience, or main visitor action. Naming a method is not a description of the site, and neither is the business or industry on its own.
|
|
86
86
|
If you can't tell yet, ask the preferred question below and nothing else — one question, no creation-method question in the same reply, and no internal tool names.
|
|
@@ -142,12 +142,25 @@ const ACCOUNT_SITE_EXECUTE_SCOPE = dedent `
|
|
|
142
142
|
siteId?: string;
|
|
143
143
|
method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
144
144
|
url: string;
|
|
145
|
+
// Plain object → JSON. A string is sent verbatim (set your own Content-Type,
|
|
146
|
+
// e.g. multipart/form-data). A Uint8Array/ArrayBuffer is sent as raw bytes
|
|
147
|
+
// for binary uploads (set your own Content-Type).
|
|
145
148
|
body?: unknown;
|
|
146
149
|
headers?: Record<string, string>;
|
|
150
|
+
// "base64" returns the response body as a base64 string (data), with
|
|
151
|
+
// encoding: "base64" on the result — use it for binary downloads (zip,
|
|
152
|
+
// image, pdf); decoding those as text corrupts them. Binary content types
|
|
153
|
+
// are returned as base64 automatically even without this. Decode, edit and
|
|
154
|
+
// re-upload binary WITHIN this script and return only a small summary. If you
|
|
155
|
+
// must return the bytes, return the wix.request response object itself (so
|
|
156
|
+
// encoding: "base64" is preserved and it isn't truncated); a bare base64
|
|
157
|
+
// string you return is treated as plain text and may be truncated.
|
|
158
|
+
responseType?: "base64";
|
|
147
159
|
}
|
|
148
160
|
interface WixResponse<T = unknown> {
|
|
149
161
|
status: number;
|
|
150
162
|
data: T;
|
|
163
|
+
encoding?: "base64";
|
|
151
164
|
json(): Promise<T>;
|
|
152
165
|
}
|
|
153
166
|
declare const wix: {
|
|
@@ -283,6 +296,7 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
|
|
|
283
296
|
ReadFullDocsArticle: dedent `
|
|
284
297
|
Fetches the full Wix docs article or method article with code examples for using the method.
|
|
285
298
|
Docs articles looks like this: https://dev.wix.com/docs/... and they can either be general docs articles or method articles.
|
|
299
|
+
Take the URL from the user's message or an earlier tool result. To reach an article you have no URL for, search or browse the docs first.
|
|
286
300
|
For REST docs, use the URL as-is. For SDK docs, the URL SHOULD include ?apiView=SDK.
|
|
287
301
|
${reminder}
|
|
288
302
|
`,
|
|
@@ -415,7 +429,7 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
|
|
|
415
429
|
`,
|
|
416
430
|
'import-claude-design-from-url': dedent `
|
|
417
431
|
Import a design created in Claude's design tool into Wix. Takes the signed claudeusercontent.com URL Claude Design produces for its self-contained HTML bundle (all images, fonts, and styles inlined); any other host is rejected. Creates a live Wix-hosted site and returns its URL.
|
|
418
|
-
NOT for site files the user provides themselves (their own HTML/CSS/JS, a zip, another builder's export) — for those, don't call this tool:
|
|
432
|
+
NOT for site files the user provides themselves (their own HTML/CSS/JS, a zip, another builder's export) — for those, don't call this tool. For a full agentic flow, read the recipe and publish the files yourself: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md — it covers creating the site and uploading the files end to end. If you can't follow it, or it doesn't apply, fall back to sending the user to https://www.wix.com/headless/drop?utm_campaign=mcp, where they upload the files and get a live hosted site. Give that URL exactly as written, keeping the utm_campaign=mcp parameter.
|
|
419
433
|
`,
|
|
420
434
|
RunWixCliCommand: dedent `
|
|
421
435
|
Executes Wix CLI commands for Wix app development and management.
|
|
@@ -842,7 +856,7 @@ export function buildToolDescriptions(includeReadme, includeImageUploadGuidance
|
|
|
842
856
|
CreateWixBusinessGuide: dedent `
|
|
843
857
|
Provides comprehensive documentation for creating a new Wix business (site, app, etc.).
|
|
844
858
|
Call this ONLY when the user explicitly asks for a headless site, a Wix Classic Editor site, or manual/advanced account/API-based site creation — these don't need the site's purpose first. For normal site-creation ${SITE_CREATION_COPY.normalCreationRoute.nonWidget}; for a specific template id, call \`CreateSiteFromTemplate\`.
|
|
845
|
-
Exception, even when the user says "headless": if they ALREADY HAVE the finished site as files (their own HTML/CSS/JS, a zip, an AI-builder export) and want those files hosted, don't call this tool —
|
|
859
|
+
Exception, even when the user says "headless": if they ALREADY HAVE the finished site as files (their own HTML/CSS/JS, a zip, an AI-builder export) and want those files hosted, don't call this tool. For a full agentic flow, read the recipe and publish the files yourself: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md — it covers creating the site and uploading the files end to end. If you can't follow it, or it doesn't apply, fall back to sending the user to https://www.wix.com/headless/drop?utm_campaign=mcp to upload the files and get a live hosted site (give that URL exactly, keeping the utm_campaign=mcp parameter). This guide's CLI flow is for BUILDING a headless frontend, not for hosting ready-made files.
|
|
846
860
|
|
|
847
861
|
## What This Tool Contains
|
|
848
862
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ALLOWED_HOSTNAMES } from '../api-call/validation.js';
|
|
2
|
-
import { ARRAY_QUERY_PARAMS_RULE } from './api-request-rules.js';
|
|
2
|
+
import { ARRAY_QUERY_PARAMS_RULE, WQL_FILTER_RULE } from './api-request-rules.js';
|
|
3
3
|
/**
|
|
4
4
|
* Centralized parameter descriptions organized by tool.
|
|
5
5
|
* Each tool has all its parameter descriptions in one place.
|
|
@@ -15,7 +15,10 @@ export const paramDescriptions = {
|
|
|
15
15
|
ARRAY_QUERY_PARAMS_RULE
|
|
16
16
|
].join('\n'),
|
|
17
17
|
method: 'The HTTP method to use for the API call (e.g. GET, POST, PUT, DELETE)',
|
|
18
|
-
body:
|
|
18
|
+
body: [
|
|
19
|
+
'The request body as a JSON object or array with all the required fields and values, including nested objects. Pass the actual value (object or array), NOT a JSON string. YOU MUST NEVER MAKE UP A BODY - the body should be based on the conversation context, i.e from the user prompt OR got into the conversation context by the "ReadFullDocsArticle" tool OR by the "SearchWixAPISpec" tool OR by the "ReadFullDocsMethodSchema" tool - i.e based on the API docs, a relevant recipe you read (preferably), a code example you found in the docs, a schema you read etc.. YOU MUST NEVER ASSUME YOU KNOW WHAT THE BODY SCHEMA IS WITHOUT CONCRETE EXAMPLES OR SCHEMA DEFINITIONS FROM THE CONVERSATION CONTEXT. Prefer reading relevant recipes if you have them in context for understand the body schema for API calls.',
|
|
20
|
+
WQL_FILTER_RULE
|
|
21
|
+
].join('\n'),
|
|
19
22
|
reason: 'One sentence explaining the original user request and why you are calling this API to complete it.',
|
|
20
23
|
sourceDocUrl: [
|
|
21
24
|
'The URL of the documentation or recipe where you found this API endpoint.',
|
package/build/dts/docs/docs.js
CHANGED
|
@@ -47,7 +47,9 @@ function getErrorMessage(error) {
|
|
|
47
47
|
}
|
|
48
48
|
function shouldTryMenuFallbackForDocsRead(error, articleUrl) {
|
|
49
49
|
const message = getErrorMessage(error);
|
|
50
|
-
|
|
50
|
+
const { pathname } = new URL(articleUrl);
|
|
51
|
+
const isMenuFallbackUrl = articleUrl.includes('/api-reference/') || pathname.startsWith('/docs/velo');
|
|
52
|
+
if (!message.includes('404') || !isMenuFallbackUrl) {
|
|
51
53
|
return false;
|
|
52
54
|
}
|
|
53
55
|
return !['This resource is deprecated', 'No schema found'].some((knownArticleError) => message.includes(knownArticleError));
|
|
@@ -671,7 +673,7 @@ export const addDocsTools = (server, allowedTools = [
|
|
|
671
673
|
content: [
|
|
672
674
|
{
|
|
673
675
|
type: 'text',
|
|
674
|
-
text: `Note: "${articleUrl}" is
|
|
676
|
+
text: `Note: "${articleUrl}" is not an article. Showing the closest documentation menu below; read the article you need from its links. For category URLs, use BrowseWixRESTDocsMenu directly next time.\n\n${text}`
|
|
675
677
|
}
|
|
676
678
|
]
|
|
677
679
|
};
|
|
@@ -746,7 +748,7 @@ export const addDocsTools = (server, allowedTools = [
|
|
|
746
748
|
content: [
|
|
747
749
|
{
|
|
748
750
|
type: 'text',
|
|
749
|
-
text: `Note: "${articleUrl}" is
|
|
751
|
+
text: `Note: "${articleUrl}" is not an article. Showing the closest documentation menu below; read the article you need from its links. For category URLs, use BrowseWixRESTDocsMenu directly next time.\n\n${text}`
|
|
750
752
|
}
|
|
751
753
|
]
|
|
752
754
|
};
|
|
@@ -169,17 +169,20 @@ export async function addGetToKnowTools({ server, disableTools = [], toolDescrip
|
|
|
169
169
|
// return '';
|
|
170
170
|
// });
|
|
171
171
|
const baseContentsPromise = Promise.all(baseDocsWithoutRecipes.map(fetchDoc));
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
172
|
+
// Filtered lists still come from the portal. The full management index is
|
|
173
|
+
// authored alongside the recipes and is fetched like any other article,
|
|
174
|
+
// using its commit version when a PR override is configured.
|
|
175
|
+
const skillsContentPromise = skillsTags?.length
|
|
176
|
+
? getSkillsByPortal(httpClient, {
|
|
177
|
+
portalId: REST_PORTAL_ID,
|
|
178
|
+
...(repo && { repo }),
|
|
179
|
+
...(commitHash && { commitHash }),
|
|
180
|
+
tags: skillsTags
|
|
181
|
+
}).catch((err) => {
|
|
182
|
+
console.error('[WixREADME] Failed to fetch skills by portal:', err);
|
|
183
|
+
return null;
|
|
184
|
+
})
|
|
185
|
+
: fetchDoc({ action: 'include', url: RECIPES_INDEX_URL }).then((doc) => doc?.text ?? null);
|
|
183
186
|
// Auto-inject site context. With no site targeted this is the account-wide
|
|
184
187
|
// view: single-site accounts "just work", and accounts with more than the
|
|
185
188
|
// cap show the first N sites plus a note to use GetSiteContext for the rest.
|
|
@@ -86,11 +86,11 @@ export function addImportClaudeDesignTool(server, authStrategy, options) {
|
|
|
86
86
|
designUrl = new URL(args.url);
|
|
87
87
|
}
|
|
88
88
|
catch {
|
|
89
|
-
return errorResult(
|
|
89
|
+
return errorResult("Invalid design URL — this tool only accepts the claudeusercontent.com URL produced by Claude Design. For site files the user provided themselves, do not retry this tool. For a full agentic flow, read the recipe and publish the files yourself: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md — it covers creating the site and uploading the files end to end. If you can't follow it, or it doesn't apply, send the user to https://www.wix.com/headless/drop?utm_campaign=mcp to upload them and get a live hosted site.");
|
|
90
90
|
}
|
|
91
91
|
if (designUrl.protocol !== 'https:' ||
|
|
92
92
|
!designUrl.hostname.endsWith(CLAUDE_USER_CONTENT_HOST_SUFFIX)) {
|
|
93
|
-
return errorResult(
|
|
93
|
+
return errorResult("Invalid design URL — this tool only accepts the claudeusercontent.com URL produced by Claude Design. For site files the user provided themselves, do not retry this tool. For a full agentic flow, read the recipe and publish the files yourself: https://dev.wix.com/docs/api-reference/account-level/sites/skills/upload-a-website-or-html-files.md — it covers creating the site and uploading the files end to end. If you can't follow it, or it doesn't apply, send the user to https://www.wix.com/headless/drop?utm_campaign=mcp to upload them and get a live hosted site.");
|
|
94
94
|
}
|
|
95
95
|
// Resolve the caller's account identity BEFORE the design fetch — the
|
|
96
96
|
// design URL is single-use, so an unauthenticated caller must fail
|
|
@@ -6,7 +6,11 @@ import { wixMcpRequestSrc39Evid1607, executeWixApiToolRequestSrc39Evid1612 } fro
|
|
|
6
6
|
* and concatenates text blocks with newlines.
|
|
7
7
|
*/
|
|
8
8
|
export declare function extractText(result: WixToolCallResult): string;
|
|
9
|
-
export
|
|
9
|
+
export interface ToolResultClient {
|
|
10
|
+
name?: string;
|
|
11
|
+
userAgent?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function formatToolResultAdditionalFields(result: WixToolCallResult, extras?: Record<string, unknown>, client?: ToolResultClient): string | undefined;
|
|
10
14
|
export declare function createToolBiEvent(params: Parameters<typeof wixMcpRequestSrc39Evid1607>[0]): import("@wix/bi-logger-api-infra-data/v2/types").LogObject<import("@wix/bi-logger-api-infra-data/v2/types").wixMcpRequestSrc39Evid1607Params>;
|
|
11
15
|
export declare function createExecuteWixAPICallToolBiEvent(params: Parameters<typeof executeWixApiToolRequestSrc39Evid1612>[0]): import("@wix/bi-logger-api-infra-data/v2/types").LogObject<import("@wix/bi-logger-api-infra-data/v2/types").executeWixApiToolRequestSrc39Evid1612Params>;
|
|
12
16
|
//# sourceMappingURL=tool-bi.d.ts.map
|
|
@@ -11,7 +11,27 @@ export function extractText(result) {
|
|
|
11
11
|
.join('\n');
|
|
12
12
|
}
|
|
13
13
|
const TOOL_RESULT_PREVIEW_MAX_CHARS = 4000;
|
|
14
|
-
|
|
14
|
+
// Codex keeps 10,000 tokens of a tool result by default, estimated at 4 bytes
|
|
15
|
+
// per token, and drops the middle of anything longer.
|
|
16
|
+
const CODEX_OUTPUT_BUDGET_BYTES = 40_000;
|
|
17
|
+
// Predicted from the default budget: a user who raised Codex's
|
|
18
|
+
// tool_output_token_limit is flagged here but not actually truncated.
|
|
19
|
+
function getCodexTruncation(resultText, client) {
|
|
20
|
+
const isCodex = [client?.name, client?.userAgent].some((identity) => identity?.toLowerCase().includes('codex'));
|
|
21
|
+
if (!isCodex) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
const resultBytes = new TextEncoder().encode(resultText).length;
|
|
25
|
+
if (resultBytes <= CODEX_OUTPUT_BUDGET_BYTES) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
resultBytes,
|
|
30
|
+
budgetBytes: CODEX_OUTPUT_BUDGET_BYTES,
|
|
31
|
+
truncatedBytes: resultBytes - CODEX_OUTPUT_BUDGET_BYTES
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function formatToolResultAdditionalFields(result, extras, client) {
|
|
15
35
|
const resultText = extractText(result);
|
|
16
36
|
const contentTypes = [
|
|
17
37
|
...new Set(result.content?.map((content) => content.type) ?? [])
|
|
@@ -24,6 +44,7 @@ export function formatToolResultAdditionalFields(result, extras) {
|
|
|
24
44
|
resultTextLength: resultText.length,
|
|
25
45
|
resultPreviewTruncated: resultText.length > TOOL_RESULT_PREVIEW_MAX_CHARS,
|
|
26
46
|
resultContentTypes: contentTypes,
|
|
47
|
+
codexTruncation: getCodexTruncation(resultText, client),
|
|
27
48
|
...extras
|
|
28
49
|
});
|
|
29
50
|
}
|
package/build/dts/tool-utils.js
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
const REST_METHOD_CONTRACT_RETRY_HINT = 'Before retrying, make sure you have everything needed for a correct call: the right endpoint URL, HTTP method, request body shape, and response shape. ' +
|
|
2
2
|
'Use the SearchWixRESTDocumentation tool or BrowseWixRESTDocsMenu tool to find the correct method or relevant related articles, the ReadFullDocsArticle tool to read the method description and all code examples, and the SearchWixAPISpec tool for precise field names and schema details.';
|
|
3
|
+
const SINGLE_OPERATOR_PER_FIELD_HINT = 'WQL takes one operator per field object. Split the conditions into separate expressions under the matching logical operator — `{"$and": [{"field": {"$gte": ...}}, {"field": {"$lte": ...}}]}` — and retry without re-searching the docs.';
|
|
4
|
+
const FIELD_CAPABILITIES_HINT = 'This endpoint declares its filterable and sortable fields with their operators. Read the map with SearchWixAPISpec (`queryMethodData.queryFieldsCapabilitiesMap`, or `searchMethodData.searchFieldsCapabilitiesMap` on search methods) and use only what it lists; otherwise fetch a bounded page and filter or sort the results yourself.';
|
|
5
|
+
const getRetryHint = (errorDetails) => {
|
|
6
|
+
if (!/Filter contains unsupported operator/i.test(errorDetails)) {
|
|
7
|
+
return REST_METHOD_CONTRACT_RETRY_HINT;
|
|
8
|
+
}
|
|
9
|
+
// The API reports a field object with several operators as one unknown operator whose name is that object.
|
|
10
|
+
return /"operator":"\{/.test(errorDetails)
|
|
11
|
+
? SINGLE_OPERATOR_PER_FIELD_HINT
|
|
12
|
+
: FIELD_CAPABILITIES_HINT;
|
|
13
|
+
};
|
|
3
14
|
export const DEFAULT_OPENAI_SECURITY_SCHEMES = [
|
|
4
15
|
{ type: 'oauth2', scopes: ['offline_access'] }
|
|
5
16
|
];
|
|
@@ -28,7 +39,7 @@ export const buildErrorMessage = ({ status, statusText, requestId, responseData
|
|
|
28
39
|
]
|
|
29
40
|
.filter((str) => !!str)
|
|
30
41
|
.join('\n');
|
|
31
|
-
return `${baseMessage}\n\n${
|
|
42
|
+
return `${baseMessage}\n\n${getRetryHint(String(errorDetails))}`;
|
|
32
43
|
};
|
|
33
44
|
export const handleWixAPIResponse = async (response) => {
|
|
34
45
|
const requestId = response.headers?.['x-wix-request-id'];
|
|
@@ -175,7 +175,11 @@ export type CodeModeFetch = (url: string, init: RequestInit) => Promise<Response
|
|
|
175
175
|
* and concatenates text blocks with newlines.
|
|
176
176
|
*/
|
|
177
177
|
export declare function extractText(result: WixToolCallResult): string;
|
|
178
|
-
export
|
|
178
|
+
export interface ToolResultClient {
|
|
179
|
+
name?: string;
|
|
180
|
+
userAgent?: string;
|
|
181
|
+
}
|
|
182
|
+
export declare function formatToolResultAdditionalFields(result: WixToolCallResult, extras?: Record<string, unknown>, client?: ToolResultClient): string | undefined;
|
|
179
183
|
/** A single tool extracted from @wix/mcp, ready to execute. */
|
|
180
184
|
export interface WixTool {
|
|
181
185
|
/** Tool name (e.g. 'CallWixSiteAPI', 'SearchWixRESTDocumentation') */
|
|
@@ -175,7 +175,11 @@ export type CodeModeFetch = (url: string, init: RequestInit) => Promise<Response
|
|
|
175
175
|
* and concatenates text blocks with newlines.
|
|
176
176
|
*/
|
|
177
177
|
export declare function extractText(result: WixToolCallResult): string;
|
|
178
|
-
export
|
|
178
|
+
export interface ToolResultClient {
|
|
179
|
+
name?: string;
|
|
180
|
+
userAgent?: string;
|
|
181
|
+
}
|
|
182
|
+
export declare function formatToolResultAdditionalFields(result: WixToolCallResult, extras?: Record<string, unknown>, client?: ToolResultClient): string | undefined;
|
|
179
183
|
/** A single tool extracted from @wix/mcp, ready to execute. */
|
|
180
184
|
export interface WixTool {
|
|
181
185
|
/** Tool name (e.g. 'CallWixSiteAPI', 'SearchWixRESTDocumentation') */
|