@productmaker/mcp 6.8.2 → 6.9.1

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 +131 -5
  2. package/dist/stdio.js +131 -5
  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,52 @@ 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
+ * The chosen existing product cannot express the confirmed offer's
5091
+ * quantity/price options. `message` carries the merchant-facing reason;
5092
+ * the client offers a one-click "create a new product and retry" path.
5093
+ */
5094
+ code: z27.literal("SHOPIFY_PUBLISH_TARGET_INCOMPATIBLE"),
5095
+ message: z27.string().optional(),
5096
+ commerceCorrectionSaved: z27.literal(true).optional()
5097
+ }),
5098
+ z27.object({
5099
+ /**
5100
+ * Code-landing publish refused BEFORE any product mutation: the shared
5101
+ * cl-landing template exists but the merchant has not saved the
5102
+ * code-landing-renderer app block, so the product page would render blank.
5103
+ * `themeEditorDeepLink` is the one-click fix — mandatory because without it
5104
+ * the client has nothing actionable to show.
5105
+ */
5106
+ code: z27.literal("SHOPIFY_THEME_SETUP_REQUIRED"),
5107
+ message: z27.string().optional(),
5108
+ shopDomain: z27.string().optional(),
5109
+ themeEditorDeepLink: z27.string().url(),
5110
+ /**
5111
+ * Present when the request also carried a commerceCorrection that was
5112
+ * committed before the gate refused — the client must drop the stale
5113
+ * correction before retrying or it dies on CONTEXT_CHANGED.
5114
+ */
5115
+ commerceCorrectionSaved: z27.literal(true).optional()
5116
+ })
5117
+ ]);
5118
+ var CodeLandingThemeSetupResponse = z27.discriminatedUnion("status", [
5119
+ z27.object({
5120
+ /** The app block is saved (ready) or still missing (setup_required). */
5121
+ status: z27.enum([
5122
+ "ready",
5123
+ "setup_required"
5124
+ ]),
5125
+ /** One-click theme-editor link that pre-adds the Code Landing block. */
5126
+ themeEditorDeepLink: z27.string().url(),
5127
+ shopDomain: z27.string()
5128
+ }),
5129
+ z27.object({
5130
+ /** Not a code-landing task — the gate does not apply. */
5131
+ status: z27.literal("not_applicable"),
5132
+ shopDomain: z27.string().optional()
5086
5133
  })
5087
5134
  ]);
5088
5135
  var ShopifyAuthUrlResponse = z27.object({
@@ -6292,7 +6339,7 @@ var SharedOutputEstimationFields = {
6292
6339
  * The lightweight competitor scan must never imply this add-on. */
6293
6340
  competitorCommercialStudyRequested: z33.boolean().optional(),
6294
6341
  /** Declining the winner replica makes `gap_replication` unreachable, so its
6295
- * forced-STANDARD ceiling must not be quoted. See
6342
+ * tier-matched replication ceiling must not be quoted. See
6296
6343
  * `.agent/specs/winner-replication-consent.spec.test.ts`. */
6297
6344
  replicateWinnerWhenFound: z33.boolean().optional()
6298
6345
  };
@@ -13047,6 +13094,8 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
13047
13094
  ], [
13048
13095
  "product_reference"
13049
13096
  ], [
13097
+ "task",
13098
+ "product",
13050
13099
  "landing"
13051
13100
  ], SHARED_CHANNEL_EXPOSURE, [
13052
13101
  {
@@ -13144,6 +13193,8 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
13144
13193
  ], [
13145
13194
  "product_reference"
13146
13195
  ], [
13196
+ "task",
13197
+ "creative",
13147
13198
  "video"
13148
13199
  ], SHARED_CHANNEL_EXPOSURE, [
13149
13200
  {
@@ -14198,6 +14249,60 @@ import { z as z65 } from "zod";
14198
14249
 
14199
14250
  // ../../packages/types/src/whatsapp-presentation-finalizer.ts
14200
14251
  import { z as z64 } from "zod";
14252
+ var WhatsAppQuoteRecoveryChoiceSchema = z64.enum([
14253
+ "without_winner",
14254
+ "retry",
14255
+ "cancel"
14256
+ ]);
14257
+ var recoveryBase = {
14258
+ version: z64.literal(1),
14259
+ requestHash: z64.string().min(1).max(256),
14260
+ issuedAt: z64.string().datetime(),
14261
+ expiresAt: z64.string().datetime(),
14262
+ offeredAlternative: z64.literal("without_winner").nullable()
14263
+ };
14264
+ var WhatsAppQuoteRecoveryReceiptSchema = z64.discriminatedUnion("state", [
14265
+ z64.object({
14266
+ ...recoveryBase,
14267
+ state: z64.literal("open"),
14268
+ successorCommandId: z64.null(),
14269
+ choice: z64.null()
14270
+ }).strict(),
14271
+ z64.object({
14272
+ ...recoveryBase,
14273
+ state: z64.literal("consumed"),
14274
+ successorCommandId: z64.string().uuid(),
14275
+ choice: z64.enum([
14276
+ "without_winner",
14277
+ "retry"
14278
+ ])
14279
+ }).strict(),
14280
+ z64.object({
14281
+ ...recoveryBase,
14282
+ state: z64.literal("awaiting_media"),
14283
+ successorCommandId: z64.null(),
14284
+ successorCollectionId: z64.string().uuid(),
14285
+ choice: z64.enum([
14286
+ "without_winner",
14287
+ "retry"
14288
+ ])
14289
+ }).strict(),
14290
+ z64.object({
14291
+ ...recoveryBase,
14292
+ state: z64.literal("cancelled"),
14293
+ successorCommandId: z64.null(),
14294
+ choice: z64.literal("cancel")
14295
+ }).strict()
14296
+ ]).refine((receipt) => {
14297
+ const duration = Date.parse(receipt.expiresAt) - Date.parse(receipt.issuedAt);
14298
+ return duration > 0 && duration <= 24 * 60 * 60 * 1e3;
14299
+ }, "Recovery must expire within twenty-four hours");
14300
+ var WhatsAppQuoteBudgetResultSchema = z64.object({
14301
+ estimatedMinCredits: z64.number().finite().nonnegative(),
14302
+ estimatedMaxCredits: z64.number().finite().positive(),
14303
+ balanceCredits: z64.number().finite(),
14304
+ budgetRecovery: WhatsAppQuoteRecoveryReceiptSchema.optional()
14305
+ }).strict().refine((result) => result.estimatedMinCredits <= result.estimatedMaxCredits, "Minimum budget cannot exceed maximum");
14201
14306
  var json = z64.lazy(() => z64.union([
14202
14307
  z64.null(),
14203
14308
  z64.boolean(),
@@ -14228,6 +14333,12 @@ function sameJson(a, b) {
14228
14333
  __name(sameJson, "sameJson");
14229
14334
  var WhatsAppPresentationFinalizerSchema = z64.object({
14230
14335
  command: z64.discriminatedUnion("kind", [
14336
+ z64.object({
14337
+ ...proposedSnapshot,
14338
+ kind: z64.literal("reject_quote"),
14339
+ errorCode: z64.literal("insufficient_balance"),
14340
+ result: WhatsAppQuoteBudgetResultSchema
14341
+ }).strict(),
14231
14342
  z64.object({
14232
14343
  ...proposedSnapshot,
14233
14344
  kind: z64.literal("reject_command"),
@@ -14287,7 +14398,7 @@ var WhatsAppPresentationFinalizerSchema = z64.object({
14287
14398
  expiresAt: z64.string().datetime()
14288
14399
  }).strict().optional()
14289
14400
  }).strict().superRefine((value, ctx) => {
14290
- if (value.command?.kind === "reject_command" || value.command?.kind === "complete_cancel") {
14401
+ if (value.command?.kind === "reject_command" || value.command?.kind === "reject_quote" || value.command?.kind === "complete_cancel") {
14291
14402
  if (value.plan || value.collection) ctx.addIssue({
14292
14403
  code: z64.ZodIssueCode.custom,
14293
14404
  message: "Proposed command finalizers cannot bind plans or collections"
@@ -14296,6 +14407,13 @@ var WhatsAppPresentationFinalizerSchema = z64.object({
14296
14407
  code: z64.ZodIssueCode.custom,
14297
14408
  message: "Rejection must preserve the original result"
14298
14409
  });
14410
+ if (value.command.kind === "reject_quote") {
14411
+ const receipt = value.command.result.budgetRecovery;
14412
+ if (receipt && (receipt.requestHash !== value.command.requestHash || receipt.state !== "open")) ctx.addIssue({
14413
+ code: z64.ZodIssueCode.custom,
14414
+ message: "Budget recovery must be new and bound to the rejected request"
14415
+ });
14416
+ }
14299
14417
  }
14300
14418
  if (!value.command && !value.plan && !value.collection) ctx.addIssue({
14301
14419
  code: z64.ZodIssueCode.custom,
@@ -16081,7 +16199,7 @@ var ApiClient = class {
16081
16199
  };
16082
16200
 
16083
16201
  // src/index.ts
16084
- var MCP_VERSION = "6.8.2";
16202
+ var MCP_VERSION = "6.9.1";
16085
16203
 
16086
16204
  // src/meta-upload-app.ts
16087
16205
  import { readFileSync } from "fs";
@@ -17199,6 +17317,8 @@ var McpErrorCode = {
17199
17317
  SHOPIFY_PUBLISH_INPUT_REQUIRED: "SHOPIFY_PUBLISH_INPUT_REQUIRED",
17200
17318
  SHOPIFY_PUBLISH_CONTEXT_CHANGED: "SHOPIFY_PUBLISH_CONTEXT_CHANGED",
17201
17319
  SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR: "SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR",
17320
+ SHOPIFY_PUBLISH_TARGET_INCOMPATIBLE: "SHOPIFY_PUBLISH_TARGET_INCOMPATIBLE",
17321
+ SHOPIFY_THEME_SETUP_REQUIRED: "SHOPIFY_THEME_SETUP_REQUIRED",
17202
17322
  // Meta / Facebook Ads
17203
17323
  META_NO_FB_PAGE: "META_NO_FB_PAGE",
17204
17324
  META_PORTFOLIO_MISMATCH: "META_PORTFOLIO_MISMATCH",
@@ -17269,6 +17389,8 @@ var CODE_MESSAGES = {
17269
17389
  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
17390
  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
17391
  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.",
17392
+ SHOPIFY_PUBLISH_TARGET_INCOMPATIBLE: "The selected Shopify product cannot carry the confirmed offer (missing quantity options/prices) or no longer exists. Read the error's message to the user, then retry with publicationMode:'new' to create a fresh product, or ask the user to pick a different existing product.",
17393
+ 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
17394
  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
17395
  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
17396
  // Meta — high-frequency
@@ -17462,6 +17584,8 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
17462
17584
  McpErrorCode.SHOPIFY_PUBLISH_INPUT_REQUIRED,
17463
17585
  McpErrorCode.SHOPIFY_PUBLISH_CONTEXT_CHANGED,
17464
17586
  McpErrorCode.SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR,
17587
+ McpErrorCode.SHOPIFY_PUBLISH_TARGET_INCOMPATIBLE,
17588
+ McpErrorCode.SHOPIFY_THEME_SETUP_REQUIRED,
17465
17589
  McpErrorCode.TIKTOK_NOT_CONNECTED,
17466
17590
  McpErrorCode.NO_SHOPIFY_SHOPS,
17467
17591
  McpErrorCode.NO_META_AD_ACCOUNTS
@@ -17487,6 +17611,7 @@ var PASSTHROUGH_CODES = /* @__PURE__ */ new Set([
17487
17611
  McpErrorCode.SHOPIFY_PUBLISH_INPUT_REQUIRED,
17488
17612
  McpErrorCode.SHOPIFY_PUBLISH_CONTEXT_CHANGED,
17489
17613
  McpErrorCode.SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR,
17614
+ McpErrorCode.SHOPIFY_PUBLISH_TARGET_INCOMPATIBLE,
17490
17615
  McpErrorCode.SHOPIFY_NOT_CONNECTED,
17491
17616
  McpErrorCode.SHOPIFY_TOKEN_EXPIRED,
17492
17617
  McpErrorCode.SHOPIFY_SCOPE_UPGRADE_REQUIRED,
@@ -17609,13 +17734,14 @@ function translateError(err) {
17609
17734
  }
17610
17735
  if (err instanceof ApiHttpError) {
17611
17736
  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) {
17737
+ 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
17738
  const parsed = ShopifyPublishErrorResponse.safeParse(body);
17614
17739
  if (!parsed.success) return mcp(McpErrorCode.INTERNAL, resolveMessage(McpErrorCode.INTERNAL, void 0));
17615
17740
  const details = parsed.data;
17616
17741
  const extra = {};
17617
17742
  if (details.code === "SHOPIFY_PUBLISH_INPUT_REQUIRED") extra.recovery = details.recovery;
17618
17743
  if (details.code === "SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR") extra.action = details.action;
17744
+ if (details.code === "SHOPIFY_THEME_SETUP_REQUIRED") extra.themeEditorDeepLink = details.themeEditorDeepLink;
17619
17745
  const saved = "commerceCorrectionSaved" in details && details.commerceCorrectionSaved === true;
17620
17746
  if (saved) extra.commerceCorrectionSaved = true;
17621
17747
  const message = resolveMessage(body.code, void 0);
@@ -20889,7 +21015,7 @@ var TikTokInput = z76.object({
20889
21015
  });
20890
21016
  function registerPublishTools(server, api, opts = {}) {
20891
21017
  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) => {
21018
+ 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
21019
  try {
20894
21020
  const parsed = parseShopifyInput(input);
20895
21021
  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,52 @@ 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
+ * The chosen existing product cannot express the confirmed offer's
5113
+ * quantity/price options. `message` carries the merchant-facing reason;
5114
+ * the client offers a one-click "create a new product and retry" path.
5115
+ */
5116
+ code: z27.literal("SHOPIFY_PUBLISH_TARGET_INCOMPATIBLE"),
5117
+ message: z27.string().optional(),
5118
+ commerceCorrectionSaved: z27.literal(true).optional()
5119
+ }),
5120
+ z27.object({
5121
+ /**
5122
+ * Code-landing publish refused BEFORE any product mutation: the shared
5123
+ * cl-landing template exists but the merchant has not saved the
5124
+ * code-landing-renderer app block, so the product page would render blank.
5125
+ * `themeEditorDeepLink` is the one-click fix — mandatory because without it
5126
+ * the client has nothing actionable to show.
5127
+ */
5128
+ code: z27.literal("SHOPIFY_THEME_SETUP_REQUIRED"),
5129
+ message: z27.string().optional(),
5130
+ shopDomain: z27.string().optional(),
5131
+ themeEditorDeepLink: z27.string().url(),
5132
+ /**
5133
+ * Present when the request also carried a commerceCorrection that was
5134
+ * committed before the gate refused — the client must drop the stale
5135
+ * correction before retrying or it dies on CONTEXT_CHANGED.
5136
+ */
5137
+ commerceCorrectionSaved: z27.literal(true).optional()
5138
+ })
5139
+ ]);
5140
+ var CodeLandingThemeSetupResponse = z27.discriminatedUnion("status", [
5141
+ z27.object({
5142
+ /** The app block is saved (ready) or still missing (setup_required). */
5143
+ status: z27.enum([
5144
+ "ready",
5145
+ "setup_required"
5146
+ ]),
5147
+ /** One-click theme-editor link that pre-adds the Code Landing block. */
5148
+ themeEditorDeepLink: z27.string().url(),
5149
+ shopDomain: z27.string()
5150
+ }),
5151
+ z27.object({
5152
+ /** Not a code-landing task — the gate does not apply. */
5153
+ status: z27.literal("not_applicable"),
5154
+ shopDomain: z27.string().optional()
5108
5155
  })
5109
5156
  ]);
5110
5157
  var ShopifyAuthUrlResponse = z27.object({
@@ -6314,7 +6361,7 @@ var SharedOutputEstimationFields = {
6314
6361
  * The lightweight competitor scan must never imply this add-on. */
6315
6362
  competitorCommercialStudyRequested: z33.boolean().optional(),
6316
6363
  /** Declining the winner replica makes `gap_replication` unreachable, so its
6317
- * forced-STANDARD ceiling must not be quoted. See
6364
+ * tier-matched replication ceiling must not be quoted. See
6318
6365
  * `.agent/specs/winner-replication-consent.spec.test.ts`. */
6319
6366
  replicateWinnerWhenFound: z33.boolean().optional()
6320
6367
  };
@@ -13069,6 +13116,8 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
13069
13116
  ], [
13070
13117
  "product_reference"
13071
13118
  ], [
13119
+ "task",
13120
+ "product",
13072
13121
  "landing"
13073
13122
  ], SHARED_CHANNEL_EXPOSURE, [
13074
13123
  {
@@ -13166,6 +13215,8 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
13166
13215
  ], [
13167
13216
  "product_reference"
13168
13217
  ], [
13218
+ "task",
13219
+ "creative",
13169
13220
  "video"
13170
13221
  ], SHARED_CHANNEL_EXPOSURE, [
13171
13222
  {
@@ -13966,6 +14017,60 @@ import { z as z65 } from "zod";
13966
14017
 
13967
14018
  // ../../packages/types/src/whatsapp-presentation-finalizer.ts
13968
14019
  import { z as z64 } from "zod";
14020
+ var WhatsAppQuoteRecoveryChoiceSchema = z64.enum([
14021
+ "without_winner",
14022
+ "retry",
14023
+ "cancel"
14024
+ ]);
14025
+ var recoveryBase = {
14026
+ version: z64.literal(1),
14027
+ requestHash: z64.string().min(1).max(256),
14028
+ issuedAt: z64.string().datetime(),
14029
+ expiresAt: z64.string().datetime(),
14030
+ offeredAlternative: z64.literal("without_winner").nullable()
14031
+ };
14032
+ var WhatsAppQuoteRecoveryReceiptSchema = z64.discriminatedUnion("state", [
14033
+ z64.object({
14034
+ ...recoveryBase,
14035
+ state: z64.literal("open"),
14036
+ successorCommandId: z64.null(),
14037
+ choice: z64.null()
14038
+ }).strict(),
14039
+ z64.object({
14040
+ ...recoveryBase,
14041
+ state: z64.literal("consumed"),
14042
+ successorCommandId: z64.string().uuid(),
14043
+ choice: z64.enum([
14044
+ "without_winner",
14045
+ "retry"
14046
+ ])
14047
+ }).strict(),
14048
+ z64.object({
14049
+ ...recoveryBase,
14050
+ state: z64.literal("awaiting_media"),
14051
+ successorCommandId: z64.null(),
14052
+ successorCollectionId: z64.string().uuid(),
14053
+ choice: z64.enum([
14054
+ "without_winner",
14055
+ "retry"
14056
+ ])
14057
+ }).strict(),
14058
+ z64.object({
14059
+ ...recoveryBase,
14060
+ state: z64.literal("cancelled"),
14061
+ successorCommandId: z64.null(),
14062
+ choice: z64.literal("cancel")
14063
+ }).strict()
14064
+ ]).refine((receipt) => {
14065
+ const duration = Date.parse(receipt.expiresAt) - Date.parse(receipt.issuedAt);
14066
+ return duration > 0 && duration <= 24 * 60 * 60 * 1e3;
14067
+ }, "Recovery must expire within twenty-four hours");
14068
+ var WhatsAppQuoteBudgetResultSchema = z64.object({
14069
+ estimatedMinCredits: z64.number().finite().nonnegative(),
14070
+ estimatedMaxCredits: z64.number().finite().positive(),
14071
+ balanceCredits: z64.number().finite(),
14072
+ budgetRecovery: WhatsAppQuoteRecoveryReceiptSchema.optional()
14073
+ }).strict().refine((result) => result.estimatedMinCredits <= result.estimatedMaxCredits, "Minimum budget cannot exceed maximum");
13969
14074
  var json = z64.lazy(() => z64.union([
13970
14075
  z64.null(),
13971
14076
  z64.boolean(),
@@ -13996,6 +14101,12 @@ function sameJson(a, b) {
13996
14101
  __name(sameJson, "sameJson");
13997
14102
  var WhatsAppPresentationFinalizerSchema = z64.object({
13998
14103
  command: z64.discriminatedUnion("kind", [
14104
+ z64.object({
14105
+ ...proposedSnapshot,
14106
+ kind: z64.literal("reject_quote"),
14107
+ errorCode: z64.literal("insufficient_balance"),
14108
+ result: WhatsAppQuoteBudgetResultSchema
14109
+ }).strict(),
13999
14110
  z64.object({
14000
14111
  ...proposedSnapshot,
14001
14112
  kind: z64.literal("reject_command"),
@@ -14055,7 +14166,7 @@ var WhatsAppPresentationFinalizerSchema = z64.object({
14055
14166
  expiresAt: z64.string().datetime()
14056
14167
  }).strict().optional()
14057
14168
  }).strict().superRefine((value, ctx) => {
14058
- if (value.command?.kind === "reject_command" || value.command?.kind === "complete_cancel") {
14169
+ if (value.command?.kind === "reject_command" || value.command?.kind === "reject_quote" || value.command?.kind === "complete_cancel") {
14059
14170
  if (value.plan || value.collection) ctx.addIssue({
14060
14171
  code: z64.ZodIssueCode.custom,
14061
14172
  message: "Proposed command finalizers cannot bind plans or collections"
@@ -14064,6 +14175,13 @@ var WhatsAppPresentationFinalizerSchema = z64.object({
14064
14175
  code: z64.ZodIssueCode.custom,
14065
14176
  message: "Rejection must preserve the original result"
14066
14177
  });
14178
+ if (value.command.kind === "reject_quote") {
14179
+ const receipt = value.command.result.budgetRecovery;
14180
+ if (receipt && (receipt.requestHash !== value.command.requestHash || receipt.state !== "open")) ctx.addIssue({
14181
+ code: z64.ZodIssueCode.custom,
14182
+ message: "Budget recovery must be new and bound to the rejected request"
14183
+ });
14184
+ }
14067
14185
  }
14068
14186
  if (!value.command && !value.plan && !value.collection) ctx.addIssue({
14069
14187
  code: z64.ZodIssueCode.custom,
@@ -14460,7 +14578,7 @@ var ApiClient = class {
14460
14578
  };
14461
14579
 
14462
14580
  // src/index.ts
14463
- var MCP_VERSION = "6.8.2";
14581
+ var MCP_VERSION = "6.9.1";
14464
14582
 
14465
14583
  // src/meta-upload-app.ts
14466
14584
  import { readFileSync } from "fs";
@@ -15578,6 +15696,8 @@ var McpErrorCode = {
15578
15696
  SHOPIFY_PUBLISH_INPUT_REQUIRED: "SHOPIFY_PUBLISH_INPUT_REQUIRED",
15579
15697
  SHOPIFY_PUBLISH_CONTEXT_CHANGED: "SHOPIFY_PUBLISH_CONTEXT_CHANGED",
15580
15698
  SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR: "SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR",
15699
+ SHOPIFY_PUBLISH_TARGET_INCOMPATIBLE: "SHOPIFY_PUBLISH_TARGET_INCOMPATIBLE",
15700
+ SHOPIFY_THEME_SETUP_REQUIRED: "SHOPIFY_THEME_SETUP_REQUIRED",
15581
15701
  // Meta / Facebook Ads
15582
15702
  META_NO_FB_PAGE: "META_NO_FB_PAGE",
15583
15703
  META_PORTFOLIO_MISMATCH: "META_PORTFOLIO_MISMATCH",
@@ -15648,6 +15768,8 @@ var CODE_MESSAGES = {
15648
15768
  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
15769
  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
15770
  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.",
15771
+ SHOPIFY_PUBLISH_TARGET_INCOMPATIBLE: "The selected Shopify product cannot carry the confirmed offer (missing quantity options/prices) or no longer exists. Read the error's message to the user, then retry with publicationMode:'new' to create a fresh product, or ask the user to pick a different existing product.",
15772
+ 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
15773
  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
15774
  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
15775
  // Meta — high-frequency
@@ -15841,6 +15963,8 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
15841
15963
  McpErrorCode.SHOPIFY_PUBLISH_INPUT_REQUIRED,
15842
15964
  McpErrorCode.SHOPIFY_PUBLISH_CONTEXT_CHANGED,
15843
15965
  McpErrorCode.SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR,
15966
+ McpErrorCode.SHOPIFY_PUBLISH_TARGET_INCOMPATIBLE,
15967
+ McpErrorCode.SHOPIFY_THEME_SETUP_REQUIRED,
15844
15968
  McpErrorCode.TIKTOK_NOT_CONNECTED,
15845
15969
  McpErrorCode.NO_SHOPIFY_SHOPS,
15846
15970
  McpErrorCode.NO_META_AD_ACCOUNTS
@@ -15866,6 +15990,7 @@ var PASSTHROUGH_CODES = /* @__PURE__ */ new Set([
15866
15990
  McpErrorCode.SHOPIFY_PUBLISH_INPUT_REQUIRED,
15867
15991
  McpErrorCode.SHOPIFY_PUBLISH_CONTEXT_CHANGED,
15868
15992
  McpErrorCode.SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR,
15993
+ McpErrorCode.SHOPIFY_PUBLISH_TARGET_INCOMPATIBLE,
15869
15994
  McpErrorCode.SHOPIFY_NOT_CONNECTED,
15870
15995
  McpErrorCode.SHOPIFY_TOKEN_EXPIRED,
15871
15996
  McpErrorCode.SHOPIFY_SCOPE_UPGRADE_REQUIRED,
@@ -15988,13 +16113,14 @@ function translateError(err) {
15988
16113
  }
15989
16114
  if (err instanceof ApiHttpError) {
15990
16115
  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) {
16116
+ 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
16117
  const parsed = ShopifyPublishErrorResponse.safeParse(body);
15993
16118
  if (!parsed.success) return mcp(McpErrorCode.INTERNAL, resolveMessage(McpErrorCode.INTERNAL, void 0));
15994
16119
  const details = parsed.data;
15995
16120
  const extra = {};
15996
16121
  if (details.code === "SHOPIFY_PUBLISH_INPUT_REQUIRED") extra.recovery = details.recovery;
15997
16122
  if (details.code === "SHOPIFY_PUBLISH_CORRECTION_REQUIRES_EDITOR") extra.action = details.action;
16123
+ if (details.code === "SHOPIFY_THEME_SETUP_REQUIRED") extra.themeEditorDeepLink = details.themeEditorDeepLink;
15998
16124
  const saved = "commerceCorrectionSaved" in details && details.commerceCorrectionSaved === true;
15999
16125
  if (saved) extra.commerceCorrectionSaved = true;
16000
16126
  const message = resolveMessage(body.code, void 0);
@@ -19321,7 +19447,7 @@ var TikTokInput = z75.object({
19321
19447
  });
19322
19448
  function registerPublishTools(server, api, opts = {}) {
19323
19449
  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) => {
19450
+ 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
19451
  try {
19326
19452
  const parsed = parseShopifyInput(input);
19327
19453
  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.1",
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.",