@productmaker/mcp 6.8.2 → 6.9.0

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.
Files changed (3) hide show
  1. package/dist/http.js +116 -4
  2. package/dist/stdio.js +116 -4
  3. package/package.json +1 -1
package/dist/http.js CHANGED
@@ -2677,6 +2677,7 @@ function getCurrencyForCountry(country) {
2677
2677
  }
2678
2678
  __name(getCurrencyForCountry, "getCurrencyForCountry");
2679
2679
  var MAX_PLAUSIBLE_AMOUNT = 1e10;
2680
+ var KNOWN_CURRENCY_CODES = new Set(Object.values(CURRENCY_BY_COUNTRY).map((c) => c.code));
2680
2681
 
2681
2682
  // ../../packages/types/src/shopify-publish-recovery.ts
2682
2683
  var MAX_MONEY_LENGTH = 40;
@@ -5083,6 +5084,42 @@ var ShopifyPublishErrorResponse = z27.discriminatedUnion("code", [
5083
5084
  code: z27.literal("SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR"),
5084
5085
  action: z27.literal("open_editor"),
5085
5086
  message: z27.string().optional()
5087
+ }),
5088
+ z27.object({
5089
+ /**
5090
+ * Code-landing publish refused BEFORE any product mutation: the shared
5091
+ * cl-landing template exists but the merchant has not saved the
5092
+ * code-landing-renderer app block, so the product page would render blank.
5093
+ * `themeEditorDeepLink` is the one-click fix — mandatory because without it
5094
+ * the client has nothing actionable to show.
5095
+ */
5096
+ code: z27.literal("SHOPIFY_THEME_SETUP_REQUIRED"),
5097
+ message: z27.string().optional(),
5098
+ shopDomain: z27.string().optional(),
5099
+ themeEditorDeepLink: z27.string().url(),
5100
+ /**
5101
+ * Present when the request also carried a commerceCorrection that was
5102
+ * committed before the gate refused — the client must drop the stale
5103
+ * correction before retrying or it dies on CONTEXT_CHANGED.
5104
+ */
5105
+ commerceCorrectionSaved: z27.literal(true).optional()
5106
+ })
5107
+ ]);
5108
+ var CodeLandingThemeSetupResponse = z27.discriminatedUnion("status", [
5109
+ z27.object({
5110
+ /** The app block is saved (ready) or still missing (setup_required). */
5111
+ status: z27.enum([
5112
+ "ready",
5113
+ "setup_required"
5114
+ ]),
5115
+ /** One-click theme-editor link that pre-adds the Code Landing block. */
5116
+ themeEditorDeepLink: z27.string().url(),
5117
+ shopDomain: z27.string()
5118
+ }),
5119
+ z27.object({
5120
+ /** Not a code-landing task — the gate does not apply. */
5121
+ status: z27.literal("not_applicable"),
5122
+ shopDomain: z27.string().optional()
5086
5123
  })
5087
5124
  ]);
5088
5125
  var ShopifyAuthUrlResponse = z27.object({
@@ -13047,6 +13084,8 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
13047
13084
  ], [
13048
13085
  "product_reference"
13049
13086
  ], [
13087
+ "task",
13088
+ "product",
13050
13089
  "landing"
13051
13090
  ], SHARED_CHANNEL_EXPOSURE, [
13052
13091
  {
@@ -13144,6 +13183,8 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
13144
13183
  ], [
13145
13184
  "product_reference"
13146
13185
  ], [
13186
+ "task",
13187
+ "creative",
13147
13188
  "video"
13148
13189
  ], SHARED_CHANNEL_EXPOSURE, [
13149
13190
  {
@@ -14198,6 +14239,60 @@ import { z as z65 } from "zod";
14198
14239
 
14199
14240
  // ../../packages/types/src/whatsapp-presentation-finalizer.ts
14200
14241
  import { z as z64 } from "zod";
14242
+ var WhatsAppQuoteRecoveryChoiceSchema = z64.enum([
14243
+ "without_winner",
14244
+ "retry",
14245
+ "cancel"
14246
+ ]);
14247
+ var recoveryBase = {
14248
+ version: z64.literal(1),
14249
+ requestHash: z64.string().min(1).max(256),
14250
+ issuedAt: z64.string().datetime(),
14251
+ expiresAt: z64.string().datetime(),
14252
+ offeredAlternative: z64.literal("without_winner").nullable()
14253
+ };
14254
+ var WhatsAppQuoteRecoveryReceiptSchema = z64.discriminatedUnion("state", [
14255
+ z64.object({
14256
+ ...recoveryBase,
14257
+ state: z64.literal("open"),
14258
+ successorCommandId: z64.null(),
14259
+ choice: z64.null()
14260
+ }).strict(),
14261
+ z64.object({
14262
+ ...recoveryBase,
14263
+ state: z64.literal("consumed"),
14264
+ successorCommandId: z64.string().uuid(),
14265
+ choice: z64.enum([
14266
+ "without_winner",
14267
+ "retry"
14268
+ ])
14269
+ }).strict(),
14270
+ z64.object({
14271
+ ...recoveryBase,
14272
+ state: z64.literal("awaiting_media"),
14273
+ successorCommandId: z64.null(),
14274
+ successorCollectionId: z64.string().uuid(),
14275
+ choice: z64.enum([
14276
+ "without_winner",
14277
+ "retry"
14278
+ ])
14279
+ }).strict(),
14280
+ z64.object({
14281
+ ...recoveryBase,
14282
+ state: z64.literal("cancelled"),
14283
+ successorCommandId: z64.null(),
14284
+ choice: z64.literal("cancel")
14285
+ }).strict()
14286
+ ]).refine((receipt) => {
14287
+ const duration = Date.parse(receipt.expiresAt) - Date.parse(receipt.issuedAt);
14288
+ return duration > 0 && duration <= 24 * 60 * 60 * 1e3;
14289
+ }, "Recovery must expire within twenty-four hours");
14290
+ var WhatsAppQuoteBudgetResultSchema = z64.object({
14291
+ estimatedMinCredits: z64.number().finite().nonnegative(),
14292
+ estimatedMaxCredits: z64.number().finite().positive(),
14293
+ balanceCredits: z64.number().finite(),
14294
+ budgetRecovery: WhatsAppQuoteRecoveryReceiptSchema.optional()
14295
+ }).strict().refine((result) => result.estimatedMinCredits <= result.estimatedMaxCredits, "Minimum budget cannot exceed maximum");
14201
14296
  var json = z64.lazy(() => z64.union([
14202
14297
  z64.null(),
14203
14298
  z64.boolean(),
@@ -14228,6 +14323,12 @@ function sameJson(a, b) {
14228
14323
  __name(sameJson, "sameJson");
14229
14324
  var WhatsAppPresentationFinalizerSchema = z64.object({
14230
14325
  command: z64.discriminatedUnion("kind", [
14326
+ z64.object({
14327
+ ...proposedSnapshot,
14328
+ kind: z64.literal("reject_quote"),
14329
+ errorCode: z64.literal("insufficient_balance"),
14330
+ result: WhatsAppQuoteBudgetResultSchema
14331
+ }).strict(),
14231
14332
  z64.object({
14232
14333
  ...proposedSnapshot,
14233
14334
  kind: z64.literal("reject_command"),
@@ -14287,7 +14388,7 @@ var WhatsAppPresentationFinalizerSchema = z64.object({
14287
14388
  expiresAt: z64.string().datetime()
14288
14389
  }).strict().optional()
14289
14390
  }).strict().superRefine((value, ctx) => {
14290
- if (value.command?.kind === "reject_command" || value.command?.kind === "complete_cancel") {
14391
+ if (value.command?.kind === "reject_command" || value.command?.kind === "reject_quote" || value.command?.kind === "complete_cancel") {
14291
14392
  if (value.plan || value.collection) ctx.addIssue({
14292
14393
  code: z64.ZodIssueCode.custom,
14293
14394
  message: "Proposed command finalizers cannot bind plans or collections"
@@ -14296,6 +14397,13 @@ var WhatsAppPresentationFinalizerSchema = z64.object({
14296
14397
  code: z64.ZodIssueCode.custom,
14297
14398
  message: "Rejection must preserve the original result"
14298
14399
  });
14400
+ if (value.command.kind === "reject_quote") {
14401
+ const receipt = value.command.result.budgetRecovery;
14402
+ if (receipt && (receipt.requestHash !== value.command.requestHash || receipt.state !== "open")) ctx.addIssue({
14403
+ code: z64.ZodIssueCode.custom,
14404
+ message: "Budget recovery must be new and bound to the rejected request"
14405
+ });
14406
+ }
14299
14407
  }
14300
14408
  if (!value.command && !value.plan && !value.collection) ctx.addIssue({
14301
14409
  code: z64.ZodIssueCode.custom,
@@ -16081,7 +16189,7 @@ var ApiClient = class {
16081
16189
  };
16082
16190
 
16083
16191
  // src/index.ts
16084
- var MCP_VERSION = "6.8.2";
16192
+ var MCP_VERSION = "6.9.0";
16085
16193
 
16086
16194
  // src/meta-upload-app.ts
16087
16195
  import { readFileSync } from "fs";
@@ -17199,6 +17307,7 @@ var McpErrorCode = {
17199
17307
  SHOPIFY_PUBLISH_INPUT_REQUIRED: "SHOPIFY_PUBLISH_INPUT_REQUIRED",
17200
17308
  SHOPIFY_PUBLISH_CONTEXT_CHANGED: "SHOPIFY_PUBLISH_CONTEXT_CHANGED",
17201
17309
  SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR: "SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR",
17310
+ SHOPIFY_THEME_SETUP_REQUIRED: "SHOPIFY_THEME_SETUP_REQUIRED",
17202
17311
  // Meta / Facebook Ads
17203
17312
  META_NO_FB_PAGE: "META_NO_FB_PAGE",
17204
17313
  META_PORTFOLIO_MISMATCH: "META_PORTFOLIO_MISMATCH",
@@ -17269,6 +17378,7 @@ var CODE_MESSAGES = {
17269
17378
  SHOPIFY_PUBLISH_INPUT_REQUIRED: "Review every recovery issue with the seller. Ask for missing prices and confirm the exact currency, unit price and pack totals before resending commerceCorrection with confirmed:true. Never invent amounts, convert currency or remove an offer.",
17270
17379
  SHOPIFY_PUBLISH_CONTEXT_CHANGED: "The publication context changed. Reopen the task in ProductMaker, review the current store, product and landing revision, and obtain fresh confirmation. Do not reuse this correction.",
17271
17380
  SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR: "Open the landing editor in ProductMaker to reconcile the visible offer with checkout prices. The correction was not saved; do not retry it automatically or regenerate the landing without approval.",
17381
+ SHOPIFY_THEME_SETUP_REQUIRED: "The store's Shopify theme is missing the ProductMaker 'Code Landing' app block \u2014 publishing now would create a product whose page renders blank. Send the user the themeEditorDeepLink (in error data) to open the Shopify theme editor, have them confirm the Code Landing block is added and press Save, then retry publishing. This is a one-time step per store; nothing was published.",
17272
17382
  SHOPIFY_NO_PUBLISHED_THEME: "The Shopify store has no published theme, so there is nowhere to place the landing. Ask the user to publish a theme (Online Store > Themes > Publish) and retry.",
17273
17383
  SHOPIFY_THEME_STRUCTURE_UNSUPPORTED: "The active Shopify theme contains an HTML structure ProductMaker cannot process safely. Ask the user to restore a valid layout/theme.liquid or contact ProductMaker support to correct the theme, then retry.",
17274
17384
  // Meta — high-frequency
@@ -17462,6 +17572,7 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
17462
17572
  McpErrorCode.SHOPIFY_PUBLISH_INPUT_REQUIRED,
17463
17573
  McpErrorCode.SHOPIFY_PUBLISH_CONTEXT_CHANGED,
17464
17574
  McpErrorCode.SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR,
17575
+ McpErrorCode.SHOPIFY_THEME_SETUP_REQUIRED,
17465
17576
  McpErrorCode.TIKTOK_NOT_CONNECTED,
17466
17577
  McpErrorCode.NO_SHOPIFY_SHOPS,
17467
17578
  McpErrorCode.NO_META_AD_ACCOUNTS
@@ -17609,13 +17720,14 @@ function translateError(err) {
17609
17720
  }
17610
17721
  if (err instanceof ApiHttpError) {
17611
17722
  const body = isRecord2(err.body) ? err.body : {};
17612
- if (body.code === McpErrorCode.SHOPIFY_PUBLISH_INPUT_REQUIRED || body.code === McpErrorCode.SHOPIFY_PUBLISH_CONTEXT_CHANGED || body.code === McpErrorCode.SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR || body.code === McpErrorCode.SHOPIFY_PUBLISH_FAILED) {
17723
+ if (body.code === McpErrorCode.SHOPIFY_PUBLISH_INPUT_REQUIRED || body.code === McpErrorCode.SHOPIFY_PUBLISH_CONTEXT_CHANGED || body.code === McpErrorCode.SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR || body.code === McpErrorCode.SHOPIFY_THEME_SETUP_REQUIRED || body.code === McpErrorCode.SHOPIFY_PUBLISH_FAILED) {
17613
17724
  const parsed = ShopifyPublishErrorResponse.safeParse(body);
17614
17725
  if (!parsed.success) return mcp(McpErrorCode.INTERNAL, resolveMessage(McpErrorCode.INTERNAL, void 0));
17615
17726
  const details = parsed.data;
17616
17727
  const extra = {};
17617
17728
  if (details.code === "SHOPIFY_PUBLISH_INPUT_REQUIRED") extra.recovery = details.recovery;
17618
17729
  if (details.code === "SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR") extra.action = details.action;
17730
+ if (details.code === "SHOPIFY_THEME_SETUP_REQUIRED") extra.themeEditorDeepLink = details.themeEditorDeepLink;
17619
17731
  const saved = "commerceCorrectionSaved" in details && details.commerceCorrectionSaved === true;
17620
17732
  if (saved) extra.commerceCorrectionSaved = true;
17621
17733
  const message = resolveMessage(body.code, void 0);
@@ -20889,7 +21001,7 @@ var TikTokInput = z76.object({
20889
21001
  });
20890
21002
  function registerPublishTools(server, api, opts = {}) {
20891
21003
  const transport = opts.transport ?? "http";
20892
- server.tool("publish_to_shopify", publishToShopifyGuidance.summary + " Precondici\xF3n: la tarea debe estar en status='complete' \u2014 usa `get_task_status` antes. Por defecto `publicationMode=auto`: si esta tarea ya fue publicada en esa tienda, actualiza ESE MISMO producto y conserva URL, precio, variantes, inventario y SKU; si no, crea uno. Usa `existing` + `targetProductId` cuando el usuario elija otro producto y `new` s\xF3lo si pide crear uno nuevo. ANTES de llamar preg\xFAntale \"\xBFTu producto tiene variantes (talla, color, etc.)?\" y pasa `showVariantPicker: true` si responde que s\xED. Por defecto publica TODAS las im\xE1genes generadas; pasa `numLandingImages` s\xF3lo si el usuario pide menos. En code landings con una oferta expl\xEDcita de packs, ProductMaker s\xF3lo publica el selector si variantes reales de Shopify cobran los totales exactos; la respuesta explica si las cre\xF3, verific\xF3 o descart\xF3 por no existir ahorro. Si devuelve SHOPIFY_PUBLISH_INPUT_REQUIRED, muestra todos los problemas de recovery y solicita los importes faltantes. Solo env\xEDa commerceCorrection con su revisi\xF3n/huella y confirmed:true despu\xE9s de confirmar los valores exactos; nunca inventes importes, conviertas moneda ni elimines ofertas. Un contexto obsoleto requiere volver a revisar; open_editor requiere el editor de ProductMaker. Ante resultado incierto, verifica la tarea/tienda antes de volver a publicar, especialmente con new; commerceCorrectionSaved no prueba publicaci\xF3n. La respuesta es markdown con el link a la landing y al producto.", ShopifyInput.shape, OVERWRITES_EXTERNAL, async (input, extra) => {
21004
+ server.tool("publish_to_shopify", publishToShopifyGuidance.summary + " Precondici\xF3n: la tarea debe estar en status='complete' \u2014 usa `get_task_status` antes. Por defecto `publicationMode=auto`: si esta tarea ya fue publicada en esa tienda, actualiza ESE MISMO producto y conserva URL, precio, variantes, inventario y SKU; si no, crea uno. Usa `existing` + `targetProductId` cuando el usuario elija otro producto y `new` s\xF3lo si pide crear uno nuevo. ANTES de llamar preg\xFAntale \"\xBFTu producto tiene variantes (talla, color, etc.)?\" y pasa `showVariantPicker: true` si responde que s\xED. Por defecto publica TODAS las im\xE1genes generadas; pasa `numLandingImages` s\xF3lo si el usuario pide menos. En code landings con una oferta expl\xEDcita de packs, ProductMaker s\xF3lo publica el selector si variantes reales de Shopify cobran los totales exactos; la respuesta explica si las cre\xF3, verific\xF3 o descart\xF3 por no existir ahorro. Si devuelve SHOPIFY_PUBLISH_INPUT_REQUIRED, muestra todos los problemas de recovery y solicita los importes faltantes. Solo env\xEDa commerceCorrection con su revisi\xF3n/huella y confirmed:true despu\xE9s de confirmar los valores exactos; nunca inventes importes, conviertas moneda ni elimines ofertas. Un contexto obsoleto requiere volver a revisar; open_editor requiere el editor de ProductMaker. Si devuelve SHOPIFY_THEME_SETUP_REQUIRED, el tema de la tienda no tiene el bloque Code Landing y nada se public\xF3: env\xEDa al usuario el themeEditorDeepLink del error para abrir el editor del tema de Shopify, p\xEDdele que agregue el bloque y pulse Guardar (una sola vez por tienda), y reintenta s\xF3lo despu\xE9s. Ante resultado incierto, verifica la tarea/tienda antes de volver a publicar, especialmente con new; commerceCorrectionSaved no prueba publicaci\xF3n. La respuesta es markdown con el link a la landing y al producto.", ShopifyInput.shape, OVERWRITES_EXTERNAL, async (input, extra) => {
20893
21005
  try {
20894
21006
  const parsed = parseShopifyInput(input);
20895
21007
  if (parsed.publicationMode === "existing" && !parsed.targetProductId) {
package/dist/stdio.js CHANGED
@@ -2699,6 +2699,7 @@ function getCurrencyForCountry(country) {
2699
2699
  }
2700
2700
  __name(getCurrencyForCountry, "getCurrencyForCountry");
2701
2701
  var MAX_PLAUSIBLE_AMOUNT = 1e10;
2702
+ var KNOWN_CURRENCY_CODES = new Set(Object.values(CURRENCY_BY_COUNTRY).map((c) => c.code));
2702
2703
 
2703
2704
  // ../../packages/types/src/shopify-publish-recovery.ts
2704
2705
  var MAX_MONEY_LENGTH = 40;
@@ -5105,6 +5106,42 @@ var ShopifyPublishErrorResponse = z27.discriminatedUnion("code", [
5105
5106
  code: z27.literal("SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR"),
5106
5107
  action: z27.literal("open_editor"),
5107
5108
  message: z27.string().optional()
5109
+ }),
5110
+ z27.object({
5111
+ /**
5112
+ * Code-landing publish refused BEFORE any product mutation: the shared
5113
+ * cl-landing template exists but the merchant has not saved the
5114
+ * code-landing-renderer app block, so the product page would render blank.
5115
+ * `themeEditorDeepLink` is the one-click fix — mandatory because without it
5116
+ * the client has nothing actionable to show.
5117
+ */
5118
+ code: z27.literal("SHOPIFY_THEME_SETUP_REQUIRED"),
5119
+ message: z27.string().optional(),
5120
+ shopDomain: z27.string().optional(),
5121
+ themeEditorDeepLink: z27.string().url(),
5122
+ /**
5123
+ * Present when the request also carried a commerceCorrection that was
5124
+ * committed before the gate refused — the client must drop the stale
5125
+ * correction before retrying or it dies on CONTEXT_CHANGED.
5126
+ */
5127
+ commerceCorrectionSaved: z27.literal(true).optional()
5128
+ })
5129
+ ]);
5130
+ var CodeLandingThemeSetupResponse = z27.discriminatedUnion("status", [
5131
+ z27.object({
5132
+ /** The app block is saved (ready) or still missing (setup_required). */
5133
+ status: z27.enum([
5134
+ "ready",
5135
+ "setup_required"
5136
+ ]),
5137
+ /** One-click theme-editor link that pre-adds the Code Landing block. */
5138
+ themeEditorDeepLink: z27.string().url(),
5139
+ shopDomain: z27.string()
5140
+ }),
5141
+ z27.object({
5142
+ /** Not a code-landing task — the gate does not apply. */
5143
+ status: z27.literal("not_applicable"),
5144
+ shopDomain: z27.string().optional()
5108
5145
  })
5109
5146
  ]);
5110
5147
  var ShopifyAuthUrlResponse = z27.object({
@@ -13069,6 +13106,8 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
13069
13106
  ], [
13070
13107
  "product_reference"
13071
13108
  ], [
13109
+ "task",
13110
+ "product",
13072
13111
  "landing"
13073
13112
  ], SHARED_CHANNEL_EXPOSURE, [
13074
13113
  {
@@ -13166,6 +13205,8 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
13166
13205
  ], [
13167
13206
  "product_reference"
13168
13207
  ], [
13208
+ "task",
13209
+ "creative",
13169
13210
  "video"
13170
13211
  ], SHARED_CHANNEL_EXPOSURE, [
13171
13212
  {
@@ -13966,6 +14007,60 @@ import { z as z65 } from "zod";
13966
14007
 
13967
14008
  // ../../packages/types/src/whatsapp-presentation-finalizer.ts
13968
14009
  import { z as z64 } from "zod";
14010
+ var WhatsAppQuoteRecoveryChoiceSchema = z64.enum([
14011
+ "without_winner",
14012
+ "retry",
14013
+ "cancel"
14014
+ ]);
14015
+ var recoveryBase = {
14016
+ version: z64.literal(1),
14017
+ requestHash: z64.string().min(1).max(256),
14018
+ issuedAt: z64.string().datetime(),
14019
+ expiresAt: z64.string().datetime(),
14020
+ offeredAlternative: z64.literal("without_winner").nullable()
14021
+ };
14022
+ var WhatsAppQuoteRecoveryReceiptSchema = z64.discriminatedUnion("state", [
14023
+ z64.object({
14024
+ ...recoveryBase,
14025
+ state: z64.literal("open"),
14026
+ successorCommandId: z64.null(),
14027
+ choice: z64.null()
14028
+ }).strict(),
14029
+ z64.object({
14030
+ ...recoveryBase,
14031
+ state: z64.literal("consumed"),
14032
+ successorCommandId: z64.string().uuid(),
14033
+ choice: z64.enum([
14034
+ "without_winner",
14035
+ "retry"
14036
+ ])
14037
+ }).strict(),
14038
+ z64.object({
14039
+ ...recoveryBase,
14040
+ state: z64.literal("awaiting_media"),
14041
+ successorCommandId: z64.null(),
14042
+ successorCollectionId: z64.string().uuid(),
14043
+ choice: z64.enum([
14044
+ "without_winner",
14045
+ "retry"
14046
+ ])
14047
+ }).strict(),
14048
+ z64.object({
14049
+ ...recoveryBase,
14050
+ state: z64.literal("cancelled"),
14051
+ successorCommandId: z64.null(),
14052
+ choice: z64.literal("cancel")
14053
+ }).strict()
14054
+ ]).refine((receipt) => {
14055
+ const duration = Date.parse(receipt.expiresAt) - Date.parse(receipt.issuedAt);
14056
+ return duration > 0 && duration <= 24 * 60 * 60 * 1e3;
14057
+ }, "Recovery must expire within twenty-four hours");
14058
+ var WhatsAppQuoteBudgetResultSchema = z64.object({
14059
+ estimatedMinCredits: z64.number().finite().nonnegative(),
14060
+ estimatedMaxCredits: z64.number().finite().positive(),
14061
+ balanceCredits: z64.number().finite(),
14062
+ budgetRecovery: WhatsAppQuoteRecoveryReceiptSchema.optional()
14063
+ }).strict().refine((result) => result.estimatedMinCredits <= result.estimatedMaxCredits, "Minimum budget cannot exceed maximum");
13969
14064
  var json = z64.lazy(() => z64.union([
13970
14065
  z64.null(),
13971
14066
  z64.boolean(),
@@ -13996,6 +14091,12 @@ function sameJson(a, b) {
13996
14091
  __name(sameJson, "sameJson");
13997
14092
  var WhatsAppPresentationFinalizerSchema = z64.object({
13998
14093
  command: z64.discriminatedUnion("kind", [
14094
+ z64.object({
14095
+ ...proposedSnapshot,
14096
+ kind: z64.literal("reject_quote"),
14097
+ errorCode: z64.literal("insufficient_balance"),
14098
+ result: WhatsAppQuoteBudgetResultSchema
14099
+ }).strict(),
13999
14100
  z64.object({
14000
14101
  ...proposedSnapshot,
14001
14102
  kind: z64.literal("reject_command"),
@@ -14055,7 +14156,7 @@ var WhatsAppPresentationFinalizerSchema = z64.object({
14055
14156
  expiresAt: z64.string().datetime()
14056
14157
  }).strict().optional()
14057
14158
  }).strict().superRefine((value, ctx) => {
14058
- if (value.command?.kind === "reject_command" || value.command?.kind === "complete_cancel") {
14159
+ if (value.command?.kind === "reject_command" || value.command?.kind === "reject_quote" || value.command?.kind === "complete_cancel") {
14059
14160
  if (value.plan || value.collection) ctx.addIssue({
14060
14161
  code: z64.ZodIssueCode.custom,
14061
14162
  message: "Proposed command finalizers cannot bind plans or collections"
@@ -14064,6 +14165,13 @@ var WhatsAppPresentationFinalizerSchema = z64.object({
14064
14165
  code: z64.ZodIssueCode.custom,
14065
14166
  message: "Rejection must preserve the original result"
14066
14167
  });
14168
+ if (value.command.kind === "reject_quote") {
14169
+ const receipt = value.command.result.budgetRecovery;
14170
+ if (receipt && (receipt.requestHash !== value.command.requestHash || receipt.state !== "open")) ctx.addIssue({
14171
+ code: z64.ZodIssueCode.custom,
14172
+ message: "Budget recovery must be new and bound to the rejected request"
14173
+ });
14174
+ }
14067
14175
  }
14068
14176
  if (!value.command && !value.plan && !value.collection) ctx.addIssue({
14069
14177
  code: z64.ZodIssueCode.custom,
@@ -14460,7 +14568,7 @@ var ApiClient = class {
14460
14568
  };
14461
14569
 
14462
14570
  // src/index.ts
14463
- var MCP_VERSION = "6.8.2";
14571
+ var MCP_VERSION = "6.9.0";
14464
14572
 
14465
14573
  // src/meta-upload-app.ts
14466
14574
  import { readFileSync } from "fs";
@@ -15578,6 +15686,7 @@ var McpErrorCode = {
15578
15686
  SHOPIFY_PUBLISH_INPUT_REQUIRED: "SHOPIFY_PUBLISH_INPUT_REQUIRED",
15579
15687
  SHOPIFY_PUBLISH_CONTEXT_CHANGED: "SHOPIFY_PUBLISH_CONTEXT_CHANGED",
15580
15688
  SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR: "SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR",
15689
+ SHOPIFY_THEME_SETUP_REQUIRED: "SHOPIFY_THEME_SETUP_REQUIRED",
15581
15690
  // Meta / Facebook Ads
15582
15691
  META_NO_FB_PAGE: "META_NO_FB_PAGE",
15583
15692
  META_PORTFOLIO_MISMATCH: "META_PORTFOLIO_MISMATCH",
@@ -15648,6 +15757,7 @@ var CODE_MESSAGES = {
15648
15757
  SHOPIFY_PUBLISH_INPUT_REQUIRED: "Review every recovery issue with the seller. Ask for missing prices and confirm the exact currency, unit price and pack totals before resending commerceCorrection with confirmed:true. Never invent amounts, convert currency or remove an offer.",
15649
15758
  SHOPIFY_PUBLISH_CONTEXT_CHANGED: "The publication context changed. Reopen the task in ProductMaker, review the current store, product and landing revision, and obtain fresh confirmation. Do not reuse this correction.",
15650
15759
  SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR: "Open the landing editor in ProductMaker to reconcile the visible offer with checkout prices. The correction was not saved; do not retry it automatically or regenerate the landing without approval.",
15760
+ SHOPIFY_THEME_SETUP_REQUIRED: "The store's Shopify theme is missing the ProductMaker 'Code Landing' app block \u2014 publishing now would create a product whose page renders blank. Send the user the themeEditorDeepLink (in error data) to open the Shopify theme editor, have them confirm the Code Landing block is added and press Save, then retry publishing. This is a one-time step per store; nothing was published.",
15651
15761
  SHOPIFY_NO_PUBLISHED_THEME: "The Shopify store has no published theme, so there is nowhere to place the landing. Ask the user to publish a theme (Online Store > Themes > Publish) and retry.",
15652
15762
  SHOPIFY_THEME_STRUCTURE_UNSUPPORTED: "The active Shopify theme contains an HTML structure ProductMaker cannot process safely. Ask the user to restore a valid layout/theme.liquid or contact ProductMaker support to correct the theme, then retry.",
15653
15763
  // Meta — high-frequency
@@ -15841,6 +15951,7 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
15841
15951
  McpErrorCode.SHOPIFY_PUBLISH_INPUT_REQUIRED,
15842
15952
  McpErrorCode.SHOPIFY_PUBLISH_CONTEXT_CHANGED,
15843
15953
  McpErrorCode.SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR,
15954
+ McpErrorCode.SHOPIFY_THEME_SETUP_REQUIRED,
15844
15955
  McpErrorCode.TIKTOK_NOT_CONNECTED,
15845
15956
  McpErrorCode.NO_SHOPIFY_SHOPS,
15846
15957
  McpErrorCode.NO_META_AD_ACCOUNTS
@@ -15988,13 +16099,14 @@ function translateError(err) {
15988
16099
  }
15989
16100
  if (err instanceof ApiHttpError) {
15990
16101
  const body = isRecord2(err.body) ? err.body : {};
15991
- if (body.code === McpErrorCode.SHOPIFY_PUBLISH_INPUT_REQUIRED || body.code === McpErrorCode.SHOPIFY_PUBLISH_CONTEXT_CHANGED || body.code === McpErrorCode.SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR || body.code === McpErrorCode.SHOPIFY_PUBLISH_FAILED) {
16102
+ if (body.code === McpErrorCode.SHOPIFY_PUBLISH_INPUT_REQUIRED || body.code === McpErrorCode.SHOPIFY_PUBLISH_CONTEXT_CHANGED || body.code === McpErrorCode.SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR || body.code === McpErrorCode.SHOPIFY_THEME_SETUP_REQUIRED || body.code === McpErrorCode.SHOPIFY_PUBLISH_FAILED) {
15992
16103
  const parsed = ShopifyPublishErrorResponse.safeParse(body);
15993
16104
  if (!parsed.success) return mcp(McpErrorCode.INTERNAL, resolveMessage(McpErrorCode.INTERNAL, void 0));
15994
16105
  const details = parsed.data;
15995
16106
  const extra = {};
15996
16107
  if (details.code === "SHOPIFY_PUBLISH_INPUT_REQUIRED") extra.recovery = details.recovery;
15997
16108
  if (details.code === "SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR") extra.action = details.action;
16109
+ if (details.code === "SHOPIFY_THEME_SETUP_REQUIRED") extra.themeEditorDeepLink = details.themeEditorDeepLink;
15998
16110
  const saved = "commerceCorrectionSaved" in details && details.commerceCorrectionSaved === true;
15999
16111
  if (saved) extra.commerceCorrectionSaved = true;
16000
16112
  const message = resolveMessage(body.code, void 0);
@@ -19321,7 +19433,7 @@ var TikTokInput = z75.object({
19321
19433
  });
19322
19434
  function registerPublishTools(server, api, opts = {}) {
19323
19435
  const transport = opts.transport ?? "http";
19324
- server.tool("publish_to_shopify", publishToShopifyGuidance.summary + " Precondici\xF3n: la tarea debe estar en status='complete' \u2014 usa `get_task_status` antes. Por defecto `publicationMode=auto`: si esta tarea ya fue publicada en esa tienda, actualiza ESE MISMO producto y conserva URL, precio, variantes, inventario y SKU; si no, crea uno. Usa `existing` + `targetProductId` cuando el usuario elija otro producto y `new` s\xF3lo si pide crear uno nuevo. ANTES de llamar preg\xFAntale \"\xBFTu producto tiene variantes (talla, color, etc.)?\" y pasa `showVariantPicker: true` si responde que s\xED. Por defecto publica TODAS las im\xE1genes generadas; pasa `numLandingImages` s\xF3lo si el usuario pide menos. En code landings con una oferta expl\xEDcita de packs, ProductMaker s\xF3lo publica el selector si variantes reales de Shopify cobran los totales exactos; la respuesta explica si las cre\xF3, verific\xF3 o descart\xF3 por no existir ahorro. Si devuelve SHOPIFY_PUBLISH_INPUT_REQUIRED, muestra todos los problemas de recovery y solicita los importes faltantes. Solo env\xEDa commerceCorrection con su revisi\xF3n/huella y confirmed:true despu\xE9s de confirmar los valores exactos; nunca inventes importes, conviertas moneda ni elimines ofertas. Un contexto obsoleto requiere volver a revisar; open_editor requiere el editor de ProductMaker. Ante resultado incierto, verifica la tarea/tienda antes de volver a publicar, especialmente con new; commerceCorrectionSaved no prueba publicaci\xF3n. La respuesta es markdown con el link a la landing y al producto.", ShopifyInput.shape, OVERWRITES_EXTERNAL, async (input, extra) => {
19436
+ server.tool("publish_to_shopify", publishToShopifyGuidance.summary + " Precondici\xF3n: la tarea debe estar en status='complete' \u2014 usa `get_task_status` antes. Por defecto `publicationMode=auto`: si esta tarea ya fue publicada en esa tienda, actualiza ESE MISMO producto y conserva URL, precio, variantes, inventario y SKU; si no, crea uno. Usa `existing` + `targetProductId` cuando el usuario elija otro producto y `new` s\xF3lo si pide crear uno nuevo. ANTES de llamar preg\xFAntale \"\xBFTu producto tiene variantes (talla, color, etc.)?\" y pasa `showVariantPicker: true` si responde que s\xED. Por defecto publica TODAS las im\xE1genes generadas; pasa `numLandingImages` s\xF3lo si el usuario pide menos. En code landings con una oferta expl\xEDcita de packs, ProductMaker s\xF3lo publica el selector si variantes reales de Shopify cobran los totales exactos; la respuesta explica si las cre\xF3, verific\xF3 o descart\xF3 por no existir ahorro. Si devuelve SHOPIFY_PUBLISH_INPUT_REQUIRED, muestra todos los problemas de recovery y solicita los importes faltantes. Solo env\xEDa commerceCorrection con su revisi\xF3n/huella y confirmed:true despu\xE9s de confirmar los valores exactos; nunca inventes importes, conviertas moneda ni elimines ofertas. Un contexto obsoleto requiere volver a revisar; open_editor requiere el editor de ProductMaker. Si devuelve SHOPIFY_THEME_SETUP_REQUIRED, el tema de la tienda no tiene el bloque Code Landing y nada se public\xF3: env\xEDa al usuario el themeEditorDeepLink del error para abrir el editor del tema de Shopify, p\xEDdele que agregue el bloque y pulse Guardar (una sola vez por tienda), y reintenta s\xF3lo despu\xE9s. Ante resultado incierto, verifica la tarea/tienda antes de volver a publicar, especialmente con new; commerceCorrectionSaved no prueba publicaci\xF3n. La respuesta es markdown con el link a la landing y al producto.", ShopifyInput.shape, OVERWRITES_EXTERNAL, async (input, extra) => {
19325
19437
  try {
19326
19438
  const parsed = parseShopifyInput(input);
19327
19439
  if (parsed.publicationMode === "existing" && !parsed.targetProductId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@productmaker/mcp",
3
- "version": "6.8.2",
3
+ "version": "6.9.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "Product Maker MCP server — cost products, generate dropshipping landing pages, UGC videos, and ad creatives, and publish to Shopify and Meta Ads from Claude, ChatGPT, or Gemini.",