@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.
@@ -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>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>`;
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 }],
@@ -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>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>`;
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.97",
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": "d908b53faa7258b99da6339c5ee2a4e113a4902ee8df410025c57048"
136
+ "falconPackageHash": "695cea78d0bf1f8b2763cdc2be91b0bb2679eaaf1f2da5e545ddb019"
137
137
  }