@wix/mcp 1.0.97 → 1.0.98
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 +2 -8
- package/build/cjs/index.cjs +444 -11
- package/build/dts/api-call/availability-tool/index.js +1 -4
- package/build/dts/api-call/suggested-domain-tool/index.js +1 -4
- package/build/dts/auth/index.d.ts +14 -3
- package/build/dts/toolkit-entry.d.cts +14 -3
- package/build/dts/toolkit-entry.d.ts +14 -3
- package/build/dts/wixel/api.d.ts +23 -0
- package/build/dts/wixel/api.js +47 -0
- package/build/dts/wixel/app-icon-files.d.ts +19 -0
- package/build/dts/wixel/app-icon-files.js +77 -0
- package/build/dts/wixel/index.d.ts +1 -0
- package/build/dts/wixel/index.js +4 -0
- package/build/dts/wixel/wixel-asset-files-tool.d.ts +29 -0
- package/build/dts/wixel/wixel-asset-files-tool.js +327 -0
- package/build/dts/wixel/wixel-session-entries.d.ts +28 -0
- package/build/dts/wixel/wixel-session-entries.js +34 -1
- package/build/dts/wixel/wixel-session-tools.js +13 -2
- package/build/esm/index.js +443 -11
- package/package.json +2 -2
|
@@ -442,14 +442,25 @@ Call this when get_wixel_task flags a question, or its reply asks the user somet
|
|
|
442
442
|
? ` (${question.customCanvas.componentName})`
|
|
443
443
|
: ''}`)
|
|
444
444
|
.join('\n');
|
|
445
|
+
// The sample the question is about ("make the rest in this look?"):
|
|
446
|
+
// a text client cannot see the stage, so it gets the pictures too.
|
|
447
|
+
const previewUrls = pending.previewUrls ?? [];
|
|
448
|
+
const previewText = previewUrls.length
|
|
449
|
+
? `\n\nSample made so far (what the user is approving):\n${previewUrls
|
|
450
|
+
.map((url) => `- ${url}`)
|
|
451
|
+
.join('\n')}`
|
|
452
|
+
: '';
|
|
453
|
+
const previewInstruction = previewUrls.length
|
|
454
|
+
? 'Show the user the sample first: give the link(s) above and, when you can write files, save them under assets/_preview/ (for example assets/_preview/sample-1.png) and name the path, so the user sees the look before answering. '
|
|
455
|
+
: '';
|
|
445
456
|
const responseText = widgetShown
|
|
446
457
|
? `An interactive question widget is displayed to the user (questionId: ${pending.toolCallId}):\n${summary}
|
|
447
458
|
<agent_instruction>Do NOT repeat the question content as text — the user answers inside the widget, which sends the answer to Wixel itself. This is NOT a turn-taking situation: the result of the user's widget answer reaches YOU only through get_wixel_task — nothing wakes you if you stop, and ending your turn BREAKS the flow (the result would never be delivered).
|
|
448
459
|
Therefore: briefly tell the user to answer in the widget, then keep calling get_wixel_task (taskId: "${args.taskId}", metaSiteId: "${args.metaSiteId}", waitSeconds: 25) in a continuous loop, and do NOT end your turn until it reports the turn done, however long it takes (the user may think for a while, then generation takes minutes). If it names a newer taskId (the widget's answer opened a new turn), follow that one.
|
|
449
460
|
- Polls that flag questionId ${pending.toolCallId} mean the user has not answered THIS question yet — do NOT call get_wixel_pending_question again for it, just keep polling.
|
|
450
461
|
- When the turn is done: if the response flags a NEW interactive question, call get_wixel_pending_question again (it renders the next widget). Otherwise present the reply to the user as a normal chat message, always including the "Edit in Wixel" link when provided.</agent_instruction>`
|
|
451
|
-
: `The Wixel agent asked:\n${summary}
|
|
452
|
-
<agent_instruction
|
|
462
|
+
: `The Wixel agent asked:\n${summary}${previewText}
|
|
463
|
+
<agent_instruction>${previewInstruction}Present the question(s) with their options/content to the user, then send their answer as plain text via send_wixel_message (contextId: "${args.contextId}", metaSiteId: "${args.metaSiteId}") and poll get_wixel_task for the taskId it returns.</agent_instruction>`;
|
|
453
464
|
return {
|
|
454
465
|
isError: false,
|
|
455
466
|
content: [{ type: 'text', text: responseText }],
|
package/build/esm/index.js
CHANGED
|
@@ -42816,10 +42816,7 @@ function addSuggestedDomainTool(server, authStrategy) {
|
|
|
42816
42816
|
openWorldHint: false
|
|
42817
42817
|
},
|
|
42818
42818
|
_meta: {
|
|
42819
|
-
securitySchemes: [
|
|
42820
|
-
{ type: "noauth" },
|
|
42821
|
-
{ type: "oauth2", scopes: ["offline_access"] }
|
|
42822
|
-
],
|
|
42819
|
+
securitySchemes: [{ type: "oauth2", scopes: ["offline_access"] }],
|
|
42823
42820
|
// Model-visible so hosts (e.g. Microsoft 365 Copilot) can call it directly and show it
|
|
42824
42821
|
// in their debug info; 'app' keeps it callable from the site-builder widget.
|
|
42825
42822
|
ui: { visibility: ["app", "model"] },
|
|
@@ -43042,10 +43039,7 @@ function addDomainAvailabilityTool(server) {
|
|
|
43042
43039
|
openWorldHint: false
|
|
43043
43040
|
},
|
|
43044
43041
|
_meta: {
|
|
43045
|
-
securitySchemes: [
|
|
43046
|
-
{ type: "noauth" },
|
|
43047
|
-
{ type: "oauth2", scopes: ["offline_access"] }
|
|
43048
|
-
],
|
|
43042
|
+
securitySchemes: [{ type: "oauth2", scopes: ["offline_access"] }],
|
|
43049
43043
|
ui: { visibility: ["app", "model"] },
|
|
43050
43044
|
"openai/toolInvocation/invoking": "Checking domain availability",
|
|
43051
43045
|
"openai/toolInvocation/invoked": "Domain availability checked"
|
|
@@ -45667,6 +45661,59 @@ async function getStylePresetsByIds2({
|
|
|
45667
45661
|
};
|
|
45668
45662
|
});
|
|
45669
45663
|
}
|
|
45664
|
+
function wixelHostClient({
|
|
45665
|
+
siteAuthHeaders,
|
|
45666
|
+
apiBaseURL = WWW_ASSET_V1_ASSET_API_BASE_URL,
|
|
45667
|
+
conductorOverride
|
|
45668
|
+
}) {
|
|
45669
|
+
return createHttpClient2({
|
|
45670
|
+
headers: {
|
|
45671
|
+
...COMMON_HEADERS,
|
|
45672
|
+
...siteAuthHeaders,
|
|
45673
|
+
...conductorOverride ? { ...CONDUCTOR_OVERRIDE_HEADERS } : {}
|
|
45674
|
+
},
|
|
45675
|
+
baseURL: apiBaseURL
|
|
45676
|
+
});
|
|
45677
|
+
}
|
|
45678
|
+
var WIXEL_HOST_EXPORT_ASSET_PATH = "/_api/wixel/v1/export/asset";
|
|
45679
|
+
var WIXEL_HOST_EXPORT_STATE_PATH = "/_api/wixel/v1/export/state";
|
|
45680
|
+
async function startAssetExport({
|
|
45681
|
+
assetId,
|
|
45682
|
+
params,
|
|
45683
|
+
...options
|
|
45684
|
+
}) {
|
|
45685
|
+
const { data } = await wixelHostClient(options).post(
|
|
45686
|
+
WIXEL_HOST_EXPORT_ASSET_PATH,
|
|
45687
|
+
{
|
|
45688
|
+
projectId: options.projectId,
|
|
45689
|
+
assetId,
|
|
45690
|
+
assetType: options.assetType,
|
|
45691
|
+
...params ? { params } : {}
|
|
45692
|
+
}
|
|
45693
|
+
);
|
|
45694
|
+
if (!data?.jobId) {
|
|
45695
|
+
throw new Error("Wixel did not start an export job for this asset");
|
|
45696
|
+
}
|
|
45697
|
+
return data.jobId;
|
|
45698
|
+
}
|
|
45699
|
+
async function getAssetExportState({
|
|
45700
|
+
jobId,
|
|
45701
|
+
...options
|
|
45702
|
+
}) {
|
|
45703
|
+
const { data } = await wixelHostClient(options).post(WIXEL_HOST_EXPORT_STATE_PATH, {
|
|
45704
|
+
jobId,
|
|
45705
|
+
projectId: options.projectId,
|
|
45706
|
+
assetType: options.assetType
|
|
45707
|
+
});
|
|
45708
|
+
const state = data?.state ?? {};
|
|
45709
|
+
return {
|
|
45710
|
+
complete: state.complete === true,
|
|
45711
|
+
progress: state.progress ?? null,
|
|
45712
|
+
url: state.url ?? null,
|
|
45713
|
+
errorDescription: state.errorDescription ?? null,
|
|
45714
|
+
title: state.title ?? null
|
|
45715
|
+
};
|
|
45716
|
+
}
|
|
45670
45717
|
|
|
45671
45718
|
// src/wixel/get-wixel-job-status-tool.ts
|
|
45672
45719
|
var TOOL_NAME = "getWixelJobStatus";
|
|
@@ -46319,6 +46366,29 @@ async function getWixelSessionProjectId({
|
|
|
46319
46366
|
const session = data?.sessions?.find((entry) => entry.id === sessionId);
|
|
46320
46367
|
return session?.projectId ?? null;
|
|
46321
46368
|
}
|
|
46369
|
+
var PREVIEW_TOOLS = /* @__PURE__ */ new Set([
|
|
46370
|
+
"generate_image",
|
|
46371
|
+
"edit_image",
|
|
46372
|
+
"remove_image_background"
|
|
46373
|
+
]);
|
|
46374
|
+
var MAX_PREVIEW_URLS = 4;
|
|
46375
|
+
function collectPreviewUrls(messages, askIndex, turnId) {
|
|
46376
|
+
const urls = [];
|
|
46377
|
+
for (let i = 0; i <= askIndex; i++) {
|
|
46378
|
+
const message = messages[i];
|
|
46379
|
+
if (turnId && message.turnId !== turnId) continue;
|
|
46380
|
+
for (const part of message.parts ?? []) {
|
|
46381
|
+
const response = part.functionResponse;
|
|
46382
|
+
if (!response?.name || !PREVIEW_TOOLS.has(response.name)) continue;
|
|
46383
|
+
for (const url of extractMediaUrls(
|
|
46384
|
+
JSON.stringify(response.response ?? "")
|
|
46385
|
+
)) {
|
|
46386
|
+
if (!urls.includes(url)) urls.push(url);
|
|
46387
|
+
}
|
|
46388
|
+
}
|
|
46389
|
+
}
|
|
46390
|
+
return urls.slice(-MAX_PREVIEW_URLS);
|
|
46391
|
+
}
|
|
46322
46392
|
async function getWixelSessionActivity({
|
|
46323
46393
|
sessionId,
|
|
46324
46394
|
...base
|
|
@@ -46410,10 +46480,12 @@ async function getWixelPendingQuestion({
|
|
|
46410
46480
|
const questions = call.args?.questions ?? [];
|
|
46411
46481
|
if (!questions.length) continue;
|
|
46412
46482
|
if (ownTurn && message.turnId !== ownTurn) return null;
|
|
46483
|
+
const previewUrls = collectPreviewUrls(messages, i, message.turnId);
|
|
46413
46484
|
return {
|
|
46414
46485
|
toolCallId: call.id,
|
|
46415
46486
|
turnId: message.turnId,
|
|
46416
|
-
questions
|
|
46487
|
+
questions,
|
|
46488
|
+
...previewUrls.length ? { previewUrls } : {}
|
|
46417
46489
|
};
|
|
46418
46490
|
}
|
|
46419
46491
|
if (part.text || part.functionCall || part.functionResponse) {
|
|
@@ -46774,14 +46846,20 @@ Call this when get_wixel_task flags a question, or its reply asks the user somet
|
|
|
46774
46846
|
const summary = pending.questions.map(
|
|
46775
46847
|
(question, index) => `${index + 1}. [${question.kind}] ${question.title ?? ""}${question.customCanvas?.componentName ? ` (${question.customCanvas.componentName})` : ""}`
|
|
46776
46848
|
).join("\n");
|
|
46849
|
+
const previewUrls = pending.previewUrls ?? [];
|
|
46850
|
+
const previewText = previewUrls.length ? `
|
|
46851
|
+
|
|
46852
|
+
Sample made so far (what the user is approving):
|
|
46853
|
+
${previewUrls.map((url) => `- ${url}`).join("\n")}` : "";
|
|
46854
|
+
const previewInstruction = previewUrls.length ? "Show the user the sample first: give the link(s) above and, when you can write files, save them under assets/_preview/ (for example assets/_preview/sample-1.png) and name the path, so the user sees the look before answering. " : "";
|
|
46777
46855
|
const responseText = widgetShown ? `An interactive question widget is displayed to the user (questionId: ${pending.toolCallId}):
|
|
46778
46856
|
${summary}
|
|
46779
46857
|
<agent_instruction>Do NOT repeat the question content as text \u2014 the user answers inside the widget, which sends the answer to Wixel itself. This is NOT a turn-taking situation: the result of the user's widget answer reaches YOU only through get_wixel_task \u2014 nothing wakes you if you stop, and ending your turn BREAKS the flow (the result would never be delivered).
|
|
46780
46858
|
Therefore: briefly tell the user to answer in the widget, then keep calling get_wixel_task (taskId: "${args.taskId}", metaSiteId: "${args.metaSiteId}", waitSeconds: 25) in a continuous loop, and do NOT end your turn until it reports the turn done, however long it takes (the user may think for a while, then generation takes minutes). If it names a newer taskId (the widget's answer opened a new turn), follow that one.
|
|
46781
46859
|
- Polls that flag questionId ${pending.toolCallId} mean the user has not answered THIS question yet \u2014 do NOT call get_wixel_pending_question again for it, just keep polling.
|
|
46782
46860
|
- When the turn is done: if the response flags a NEW interactive question, call get_wixel_pending_question again (it renders the next widget). Otherwise present the reply to the user as a normal chat message, always including the "Edit in Wixel" link when provided.</agent_instruction>` : `The Wixel agent asked:
|
|
46783
|
-
${summary}
|
|
46784
|
-
<agent_instruction
|
|
46861
|
+
${summary}${previewText}
|
|
46862
|
+
<agent_instruction>${previewInstruction}Present the question(s) with their options/content to the user, then send their answer as plain text via send_wixel_message (contextId: "${args.contextId}", metaSiteId: "${args.metaSiteId}") and poll get_wixel_task for the taskId it returns.</agent_instruction>`;
|
|
46785
46863
|
return {
|
|
46786
46864
|
isError: false,
|
|
46787
46865
|
content: [{ type: "text", text: responseText }],
|
|
@@ -47744,6 +47822,358 @@ function addWixelSessionTools(server, authStrategy, options = {}) {
|
|
|
47744
47822
|
addGetWixelTurnResultTool(server, authStrategy, shared);
|
|
47745
47823
|
}
|
|
47746
47824
|
|
|
47825
|
+
// src/wixel/app-icon-files.ts
|
|
47826
|
+
var WIX_MEDIA_FILE = /^(https:\/\/static\.wixstatic\.com\/media\/[^/?#]+)/;
|
|
47827
|
+
var ANDROID_MIPMAPS = [
|
|
47828
|
+
["mdpi", 48],
|
|
47829
|
+
["hdpi", 72],
|
|
47830
|
+
["xhdpi", 96],
|
|
47831
|
+
["xxhdpi", 144],
|
|
47832
|
+
["xxxhdpi", 192]
|
|
47833
|
+
];
|
|
47834
|
+
function wixMediaFileUrl(url) {
|
|
47835
|
+
return WIX_MEDIA_FILE.exec(url)?.[1] ?? null;
|
|
47836
|
+
}
|
|
47837
|
+
function wixMediaSquarePng(fileUrl, size) {
|
|
47838
|
+
return `${fileUrl}/v1/fill/w_${size},h_${size},al_c,q_95/icon-${size}.png`;
|
|
47839
|
+
}
|
|
47840
|
+
function buildAppIconFiles(exportedPngUrl, appName) {
|
|
47841
|
+
const file = wixMediaFileUrl(exportedPngUrl);
|
|
47842
|
+
if (!file) {
|
|
47843
|
+
return null;
|
|
47844
|
+
}
|
|
47845
|
+
const png = (path, size) => ({
|
|
47846
|
+
path,
|
|
47847
|
+
url: wixMediaSquarePng(file, size)
|
|
47848
|
+
});
|
|
47849
|
+
return [
|
|
47850
|
+
png("ios/AppIcon.appiconset/icon-1024.png", 1024),
|
|
47851
|
+
{
|
|
47852
|
+
path: "ios/AppIcon.appiconset/Contents.json",
|
|
47853
|
+
content: JSON.stringify(
|
|
47854
|
+
{
|
|
47855
|
+
images: [
|
|
47856
|
+
{
|
|
47857
|
+
filename: "icon-1024.png",
|
|
47858
|
+
idiom: "universal",
|
|
47859
|
+
platform: "ios",
|
|
47860
|
+
size: "1024x1024"
|
|
47861
|
+
}
|
|
47862
|
+
],
|
|
47863
|
+
info: { author: "wixel", version: 1 }
|
|
47864
|
+
},
|
|
47865
|
+
null,
|
|
47866
|
+
2
|
|
47867
|
+
)
|
|
47868
|
+
},
|
|
47869
|
+
...ANDROID_MIPMAPS.map(
|
|
47870
|
+
([density, size]) => png(`android/res/mipmap-${density}/ic_launcher.png`, size)
|
|
47871
|
+
),
|
|
47872
|
+
png("android/play-store-512.png", 512),
|
|
47873
|
+
png("web/favicon-16.png", 16),
|
|
47874
|
+
png("web/favicon-32.png", 32),
|
|
47875
|
+
png("web/favicon-48.png", 48),
|
|
47876
|
+
png("web/apple-touch-icon.png", 180),
|
|
47877
|
+
png("web/icon-192.png", 192),
|
|
47878
|
+
png("web/icon-512.png", 512),
|
|
47879
|
+
{
|
|
47880
|
+
path: "web/site.webmanifest",
|
|
47881
|
+
content: JSON.stringify(
|
|
47882
|
+
{
|
|
47883
|
+
name: appName,
|
|
47884
|
+
short_name: appName.slice(0, 12),
|
|
47885
|
+
icons: [
|
|
47886
|
+
{ src: "icon-192.png", sizes: "192x192", type: "image/png" },
|
|
47887
|
+
{ src: "icon-512.png", sizes: "512x512", type: "image/png" }
|
|
47888
|
+
],
|
|
47889
|
+
display: "standalone"
|
|
47890
|
+
},
|
|
47891
|
+
null,
|
|
47892
|
+
2
|
|
47893
|
+
)
|
|
47894
|
+
}
|
|
47895
|
+
];
|
|
47896
|
+
}
|
|
47897
|
+
|
|
47898
|
+
// src/wixel/wixel-asset-files-tool.ts
|
|
47899
|
+
var ASSET_FILE_LAYOUTS = ["files", "spriteSheet", "appIcons"];
|
|
47900
|
+
var ICONS_FILE_TYPES = [
|
|
47901
|
+
"png",
|
|
47902
|
+
"pngTransparentBackground",
|
|
47903
|
+
"svg",
|
|
47904
|
+
"svgTransparentBackground"
|
|
47905
|
+
];
|
|
47906
|
+
var ICONS_SIZES = [
|
|
47907
|
+
"16px",
|
|
47908
|
+
"24px",
|
|
47909
|
+
"32px",
|
|
47910
|
+
"64px",
|
|
47911
|
+
"128px",
|
|
47912
|
+
"256px",
|
|
47913
|
+
"512px"
|
|
47914
|
+
];
|
|
47915
|
+
var POLL_INTERVAL_MS = 2e3;
|
|
47916
|
+
var MAX_WAIT_SECONDS = 25;
|
|
47917
|
+
function buildExportParams(assetType, args) {
|
|
47918
|
+
const layout = args.layout ?? "files";
|
|
47919
|
+
if (assetType === "IMAGE" && layout === "appIcons") {
|
|
47920
|
+
return { format: "png", quality: 100, scale: 1 };
|
|
47921
|
+
}
|
|
47922
|
+
if (assetType !== "ICONS") {
|
|
47923
|
+
if (layout !== "files") {
|
|
47924
|
+
throw new Error(
|
|
47925
|
+
layout === "appIcons" ? `layout "appIcons" needs an ICONS or IMAGE asset (the app icon); this asset is ${assetType}` : `layout "${layout}" needs an ICONS asset (an icon / sprite set); this asset is ${assetType}`
|
|
47926
|
+
);
|
|
47927
|
+
}
|
|
47928
|
+
return void 0;
|
|
47929
|
+
}
|
|
47930
|
+
if (layout === "files") {
|
|
47931
|
+
return {
|
|
47932
|
+
// Engines and app projects want plain transparent PNGs, not the
|
|
47933
|
+
// editor's default SVG-wrapped PNG.
|
|
47934
|
+
fileType: args.fileType ?? "pngTransparentBackground",
|
|
47935
|
+
...args.size ? { size: args.size } : { size: "512px" }
|
|
47936
|
+
};
|
|
47937
|
+
}
|
|
47938
|
+
return {
|
|
47939
|
+
layout,
|
|
47940
|
+
...layout === "spriteSheet" && args.size ? { size: args.size } : {}
|
|
47941
|
+
};
|
|
47942
|
+
}
|
|
47943
|
+
async function waitForExport(read, waitSeconds) {
|
|
47944
|
+
const deadline = Date.now() + waitSeconds * 1e3;
|
|
47945
|
+
let state = await read();
|
|
47946
|
+
while (!state.complete && !state.errorDescription && Date.now() < deadline) {
|
|
47947
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
47948
|
+
state = await read();
|
|
47949
|
+
}
|
|
47950
|
+
return state;
|
|
47951
|
+
}
|
|
47952
|
+
function describeContents(layout, assetType) {
|
|
47953
|
+
if (assetType !== "ICONS") {
|
|
47954
|
+
return "The file is the asset as the editor downloads it.";
|
|
47955
|
+
}
|
|
47956
|
+
switch (layout) {
|
|
47957
|
+
case "spriteSheet":
|
|
47958
|
+
return 'The zip holds <set>-sheet.png (frames on one grid, feet on one baseline), <set>-sheet.json (Aseprite array frame map: frames "0","1"\u2026, one frameTags entry per pose \u2014 load with Phaser this.load.aseprite + this.anims.createFromAseprite) and frames/<n>-<pose>.png.';
|
|
47959
|
+
case "appIcons":
|
|
47960
|
+
return "The zip holds ios/AppIcon.appiconset (1024 px + Contents.json, drop into Assets.xcassets), android/res/mipmap-*/ic_launcher.png + android/play-store-512.png, and web/ favicons, apple-touch-icon.png, icon-192/512.png and site.webmanifest.";
|
|
47961
|
+
default:
|
|
47962
|
+
return "The zip holds one transparent PNG per tile, named after the tile.";
|
|
47963
|
+
}
|
|
47964
|
+
}
|
|
47965
|
+
function addGetWixelAssetFilesTool(server, authStrategy, options = {}) {
|
|
47966
|
+
const { getSiteAuthHeaders } = authStrategy;
|
|
47967
|
+
server.registerTool(
|
|
47968
|
+
"get_wixel_asset_files",
|
|
47969
|
+
{
|
|
47970
|
+
title: "Get Wixel asset files",
|
|
47971
|
+
description: `Download the files of an asset made in a Wixel conversation, to use them in code (a game, an app, a site). Pass the metaSiteId and sessionId that send_wixel_message returned.
|
|
47972
|
+
- Without assetId: lists the project's assets (id, name, type) \u2014 pick one.
|
|
47973
|
+
- With assetId: exports it and returns a zip URL. For an ICONS asset choose layout:
|
|
47974
|
+
"files" (default) one transparent PNG per tile;
|
|
47975
|
+
"spriteSheet" for a character's poses / animation frames: one aligned sheet + Aseprite JSON frame map (Phaser createFromAseprite) + aligned frames;
|
|
47976
|
+
"appIcons" for an app or game icon: iOS app icon set, Android mipmaps + Play Store 512, web favicons + manifest.
|
|
47977
|
+
An IMAGE asset (an app icon tile) also takes layout "appIcons": you get one URL per file instead of a zip.
|
|
47978
|
+
- If the export is still running, call again with the returned jobId, assetType, layout and assetId.
|
|
47979
|
+
Download the URL and unzip it into the project; never re-draw or re-generate assets the user already approved.`,
|
|
47980
|
+
inputSchema: {
|
|
47981
|
+
metaSiteId: external_exports.string().describe("metaSiteId returned by send_wixel_message"),
|
|
47982
|
+
sessionId: external_exports.string().describe("sessionId returned by send_wixel_message"),
|
|
47983
|
+
assetId: external_exports.string().optional().describe("Asset to export; omit to list the project assets"),
|
|
47984
|
+
layout: external_exports.enum(ASSET_FILE_LAYOUTS).optional().describe(
|
|
47985
|
+
"ICONS assets only: files (default), spriteSheet, appIcons"
|
|
47986
|
+
),
|
|
47987
|
+
fileType: external_exports.enum(ICONS_FILE_TYPES).optional().describe(
|
|
47988
|
+
'ICONS layout "files" only; default pngTransparentBackground'
|
|
47989
|
+
),
|
|
47990
|
+
size: external_exports.enum(ICONS_SIZES).optional().describe(
|
|
47991
|
+
'ICONS: tile size for "files" (default 512px), frame cell size for "spriteSheet" (default 256px)'
|
|
47992
|
+
),
|
|
47993
|
+
jobId: external_exports.string().optional().describe("Continue waiting on an export this tool started"),
|
|
47994
|
+
assetType: external_exports.string().optional().describe("With jobId: the assetType this tool returned"),
|
|
47995
|
+
waitSeconds: external_exports.number().min(0).max(MAX_WAIT_SECONDS).optional().describe("How long to wait for the export (default 25)")
|
|
47996
|
+
},
|
|
47997
|
+
annotations: {
|
|
47998
|
+
readOnlyHint: true,
|
|
47999
|
+
destructiveHint: false,
|
|
48000
|
+
openWorldHint: false
|
|
48001
|
+
}
|
|
48002
|
+
},
|
|
48003
|
+
async (args, extra) => {
|
|
48004
|
+
setWixelToolBi(extra, { siteId: args.metaSiteId });
|
|
48005
|
+
const siteAuthHeaders = await getSiteAuthHeaders(args.metaSiteId);
|
|
48006
|
+
try {
|
|
48007
|
+
const projectId = await getWixelSessionProjectId({
|
|
48008
|
+
authHeaders: siteAuthHeaders,
|
|
48009
|
+
conductorOverride: options.conductorOverride,
|
|
48010
|
+
sessionId: args.sessionId
|
|
48011
|
+
});
|
|
48012
|
+
if (!projectId) {
|
|
48013
|
+
throw new Error(
|
|
48014
|
+
"This Wixel conversation has no project yet \u2014 make something first"
|
|
48015
|
+
);
|
|
48016
|
+
}
|
|
48017
|
+
const base = {
|
|
48018
|
+
siteAuthHeaders,
|
|
48019
|
+
projectId,
|
|
48020
|
+
conductorOverride: options.conductorOverride
|
|
48021
|
+
};
|
|
48022
|
+
const waitSeconds = args.waitSeconds ?? MAX_WAIT_SECONDS;
|
|
48023
|
+
if (args.jobId) {
|
|
48024
|
+
if (!args.assetType) {
|
|
48025
|
+
throw new Error("Pass the assetType returned with the jobId");
|
|
48026
|
+
}
|
|
48027
|
+
const assetType2 = args.assetType;
|
|
48028
|
+
const state2 = await waitForExport(
|
|
48029
|
+
() => getAssetExportState({ ...base, assetType: assetType2, jobId: args.jobId }),
|
|
48030
|
+
waitSeconds
|
|
48031
|
+
);
|
|
48032
|
+
const assetName = state2.complete && args.assetId && assetType2 === "IMAGE" && args.layout === "appIcons" ? await getAsset({ ...base, assetId: args.assetId }).then((asset2) => asset2?.name).catch(() => void 0) : void 0;
|
|
48033
|
+
return exportResult(state2, args, assetType2, args.jobId, assetName);
|
|
48034
|
+
}
|
|
48035
|
+
if (!args.assetId) {
|
|
48036
|
+
const { assets } = await queryAssets(base);
|
|
48037
|
+
const list = (assets ?? []).filter((asset2) => !asset2.archived).map((asset2) => ({
|
|
48038
|
+
id: asset2.id,
|
|
48039
|
+
name: asset2.name ?? null,
|
|
48040
|
+
type: asset2.type ?? null
|
|
48041
|
+
}));
|
|
48042
|
+
return {
|
|
48043
|
+
isError: false,
|
|
48044
|
+
content: [
|
|
48045
|
+
{
|
|
48046
|
+
type: "text",
|
|
48047
|
+
text: `${list.length} assets in this Wixel project:
|
|
48048
|
+
${list.map(
|
|
48049
|
+
(a2) => `- ${a2.name ?? "(unnamed)"} [${a2.type}] id=${a2.id}`
|
|
48050
|
+
).join(
|
|
48051
|
+
"\n"
|
|
48052
|
+
)}
|
|
48053
|
+
Call get_wixel_asset_files again with the assetId to download one.`
|
|
48054
|
+
}
|
|
48055
|
+
],
|
|
48056
|
+
structuredContent: { success: true, projectId, assets: list }
|
|
48057
|
+
};
|
|
48058
|
+
}
|
|
48059
|
+
const asset = await getAsset({ ...base, assetId: args.assetId });
|
|
48060
|
+
const assetType = asset?.type;
|
|
48061
|
+
if (!assetType) {
|
|
48062
|
+
throw new Error(
|
|
48063
|
+
`Asset ${args.assetId} was not found in this project`
|
|
48064
|
+
);
|
|
48065
|
+
}
|
|
48066
|
+
const params = buildExportParams(assetType, args);
|
|
48067
|
+
const jobId = await startAssetExport({
|
|
48068
|
+
...base,
|
|
48069
|
+
assetType,
|
|
48070
|
+
assetId: args.assetId,
|
|
48071
|
+
params
|
|
48072
|
+
});
|
|
48073
|
+
const state = await waitForExport(
|
|
48074
|
+
() => getAssetExportState({ ...base, assetType, jobId }),
|
|
48075
|
+
waitSeconds
|
|
48076
|
+
);
|
|
48077
|
+
return exportResult(state, args, assetType, jobId, asset?.name);
|
|
48078
|
+
} catch (error2) {
|
|
48079
|
+
rethrowIfUnauthorized(error2);
|
|
48080
|
+
console.error("\u274C Error getting Wixel asset files:", {
|
|
48081
|
+
message: error2.message,
|
|
48082
|
+
requestId: error2.requestId
|
|
48083
|
+
});
|
|
48084
|
+
const errorMessage = error2.message;
|
|
48085
|
+
return {
|
|
48086
|
+
isError: true,
|
|
48087
|
+
content: [{ type: "text", text: errorMessage }],
|
|
48088
|
+
structuredContent: { success: false, error: errorMessage }
|
|
48089
|
+
};
|
|
48090
|
+
}
|
|
48091
|
+
}
|
|
48092
|
+
);
|
|
48093
|
+
}
|
|
48094
|
+
function exportResult(state, args, assetType, jobId, assetName) {
|
|
48095
|
+
if (state.errorDescription) {
|
|
48096
|
+
return {
|
|
48097
|
+
isError: true,
|
|
48098
|
+
content: [
|
|
48099
|
+
{
|
|
48100
|
+
type: "text",
|
|
48101
|
+
text: `Export failed: ${state.errorDescription}`
|
|
48102
|
+
}
|
|
48103
|
+
],
|
|
48104
|
+
structuredContent: {
|
|
48105
|
+
success: false,
|
|
48106
|
+
error: state.errorDescription,
|
|
48107
|
+
jobId,
|
|
48108
|
+
assetType
|
|
48109
|
+
}
|
|
48110
|
+
};
|
|
48111
|
+
}
|
|
48112
|
+
const layout = args.layout ?? "files";
|
|
48113
|
+
if (!state.complete || !state.url) {
|
|
48114
|
+
return {
|
|
48115
|
+
isError: false,
|
|
48116
|
+
content: [
|
|
48117
|
+
{
|
|
48118
|
+
type: "text",
|
|
48119
|
+
text: `Export still running${state.progress != null ? ` (${state.progress}%)` : ""}. Call get_wixel_asset_files again with jobId "${jobId}", assetType "${assetType}", layout "${layout}"${args.assetId ? ` and assetId "${args.assetId}"` : ""}.`
|
|
48120
|
+
}
|
|
48121
|
+
],
|
|
48122
|
+
structuredContent: {
|
|
48123
|
+
success: true,
|
|
48124
|
+
status: "running",
|
|
48125
|
+
progress: state.progress,
|
|
48126
|
+
jobId,
|
|
48127
|
+
assetType,
|
|
48128
|
+
layout,
|
|
48129
|
+
...args.assetId ? { assetId: args.assetId } : {}
|
|
48130
|
+
}
|
|
48131
|
+
};
|
|
48132
|
+
}
|
|
48133
|
+
if (assetType === "IMAGE" && layout === "appIcons") {
|
|
48134
|
+
const files = buildAppIconFiles(state.url, assetName || "App");
|
|
48135
|
+
if (files) {
|
|
48136
|
+
return {
|
|
48137
|
+
isError: false,
|
|
48138
|
+
content: [
|
|
48139
|
+
{
|
|
48140
|
+
type: "text",
|
|
48141
|
+
text: `App icon files ready \u2014 download each URL to its path, write the JSON files as given:
|
|
48142
|
+
${files.map((f2) => `- ${f2.path}: ${f2.url ?? f2.content}`).join("\n")}`
|
|
48143
|
+
}
|
|
48144
|
+
],
|
|
48145
|
+
structuredContent: {
|
|
48146
|
+
success: true,
|
|
48147
|
+
status: "ready",
|
|
48148
|
+
url: state.url,
|
|
48149
|
+
files,
|
|
48150
|
+
jobId,
|
|
48151
|
+
assetType,
|
|
48152
|
+
layout
|
|
48153
|
+
}
|
|
48154
|
+
};
|
|
48155
|
+
}
|
|
48156
|
+
}
|
|
48157
|
+
return {
|
|
48158
|
+
isError: false,
|
|
48159
|
+
content: [
|
|
48160
|
+
{
|
|
48161
|
+
type: "text",
|
|
48162
|
+
text: `Files ready: ${state.url}
|
|
48163
|
+
${describeContents(args.layout, assetType)}`
|
|
48164
|
+
}
|
|
48165
|
+
],
|
|
48166
|
+
structuredContent: {
|
|
48167
|
+
success: true,
|
|
48168
|
+
status: "ready",
|
|
48169
|
+
url: state.url,
|
|
48170
|
+
jobId,
|
|
48171
|
+
assetType,
|
|
48172
|
+
layout: args.layout ?? "files"
|
|
48173
|
+
}
|
|
48174
|
+
};
|
|
48175
|
+
}
|
|
48176
|
+
|
|
47747
48177
|
// src/wixel/index.ts
|
|
47748
48178
|
function addWixelTools(server, authStrategy, options = {}) {
|
|
47749
48179
|
addGetWixelJobStatusTool(server, authStrategy, options);
|
|
@@ -47751,6 +48181,7 @@ function addWixelTools(server, authStrategy, options = {}) {
|
|
|
47751
48181
|
addGetWixelAssetTool(server, authStrategy, options);
|
|
47752
48182
|
addGetWixelProjectAssetsTool(server, authStrategy, options);
|
|
47753
48183
|
addWixelSessionTools(server, authStrategy, options);
|
|
48184
|
+
addGetWixelAssetFilesTool(server, authStrategy, options);
|
|
47754
48185
|
}
|
|
47755
48186
|
|
|
47756
48187
|
// src/import-claude-design-tool/constants.ts
|
|
@@ -50733,6 +51164,7 @@ export {
|
|
|
50733
51164
|
addEditSiteWidgetTool,
|
|
50734
51165
|
addExecuteCodeModeTool,
|
|
50735
51166
|
addGetProfileTool,
|
|
51167
|
+
addGetWixelAssetFilesTool,
|
|
50736
51168
|
addGetWixelAssetTool,
|
|
50737
51169
|
addGetWixelJobStatusTool,
|
|
50738
51170
|
addGetWixelProjectAssetsTool,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.98",
|
|
4
4
|
"description": "A Model Context Protocol server for Wix AI tools",
|
|
5
5
|
"bin": "./build/bin-standalone.js",
|
|
6
6
|
"dependencies": {
|
|
@@ -133,5 +133,5 @@
|
|
|
133
133
|
]
|
|
134
134
|
}
|
|
135
135
|
},
|
|
136
|
-
"falconPackageHash": "
|
|
136
|
+
"falconPackageHash": "695cea78d0bf1f8b2763cdc2be91b0bb2679eaaf1f2da5e545ddb019"
|
|
137
137
|
}
|