@productmaker/mcp 1.9.0 → 1.11.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.
package/dist/http.js CHANGED
@@ -113,7 +113,7 @@ var ApiClient = class {
113
113
  };
114
114
 
115
115
  // src/index.ts
116
- var MCP_VERSION = "1.9.0";
116
+ var MCP_VERSION = "1.11.0";
117
117
 
118
118
  // src/tools/tasks.ts
119
119
  import { z as z2 } from "zod";
@@ -405,6 +405,11 @@ var LandingStrategyEnum = z.enum([
405
405
  "offer_value",
406
406
  "lifestyle_identity"
407
407
  ]);
408
+ var LandingImageFormatEnum = z.enum([
409
+ "9:16",
410
+ "4:5",
411
+ "1:1"
412
+ ]);
408
413
  var VideoPresetEnum = z.enum([
409
414
  "auto",
410
415
  "ugc_dynamic",
@@ -1207,7 +1212,8 @@ var CreateProductTaskInput = z2.object({
1207
1212
  competitorScanEnabled: z2.boolean().optional().describe("Opcional. Si true (default), el sistema analiza ads de competencia en Meta Ad Library y enriquece los \xE1ngulos generados con hooks y painPoints validados por el mercado (\u2248$0.20-0.48 extra por task \u2014 var\xEDa con cu\xE1ntas transcripciones de video se capturen). Si false, el pipeline genera \xE1ngulos sin esa se\xF1al \u2014 usa esto SOLO si el usuario ya tiene su propia investigaci\xF3n de mercado o quiere ahorrar costos en productos muy nicho sin presencia en Meta. Default true."),
1208
1213
  brandId: z2.string().uuid().optional().describe("Opcional. ID de una marca del usuario; si se env\xEDa, el sistema usa su voz, colores y persona para enriquecer los outputs generados."),
1209
1214
  creativeDirection: z2.string().max(500).optional().describe('Opcional. Direcci\xF3n creativa en texto libre (colores, mood, estilo) que aplica a los outputs de IMAGEN y LANDING (no al video \u2014 el estilo/ambiente del video se dirige con scriptNotes). M\xC1XIMA PRIORIDAD: anula marca, colores y estilo por defecto. Ej. "amarillo y azul, minimalista". M\xE1x 500 caracteres.'),
1210
- landingStrategy: LandingStrategyEnum.default("classic").describe('Opcional. Estructura de la landing cl\xE1sica cuando landing est\xE1 entre outputs: "classic" (estructura original probada y recomendada; default del primer rollout), "auto" (la IA decide; experimental), "pain_solution", "social_proof", "benefits_clarity", "offer_value" o "lifestyle_identity". Default "classic".')
1215
+ landingStrategy: LandingStrategyEnum.default("classic").describe('Opcional. Estructura de la landing cl\xE1sica cuando landing est\xE1 entre outputs: "classic" (estructura original probada y recomendada; default del primer rollout), "auto" (la IA decide; experimental), "pain_solution", "social_proof", "benefits_clarity", "offer_value" o "lifestyle_identity". Default "classic".'),
1216
+ landingImageFormat: LandingImageFormatEnum.default("9:16").describe('Opcional. Formato/relaci\xF3n de aspecto de las im\xE1genes de la landing cl\xE1sica cuando landing est\xE1 entre outputs: "9:16" (vertical, por defecto), "4:5" (vertical compacto para feed) o "1:1" (cuadrado). Default "9:16".')
1211
1217
  }).strict();
1212
1218
  var GetTaskStatusInput = z2.object({
1213
1219
  taskId: z2.string().min(1),
@@ -1337,6 +1343,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
1337
1343
  "options.brandId": input.brandId,
1338
1344
  "options.creativeDirection": input.creativeDirection,
1339
1345
  "options.landingStrategy": input.landingStrategy,
1346
+ "options.landingImageFormat": input.landingImageFormat,
1340
1347
  url: input.productUrl
1341
1348
  });
1342
1349
  const r = await api.postMultipart("/v1/ingest", {
@@ -1571,7 +1578,8 @@ var LandingInput = z3.object({
1571
1578
  competitorScanEnabled: CreativeRefs.competitorScanEnabled,
1572
1579
  brandId: CreativeRefs.brandId,
1573
1580
  creativeDirection: CreativeRefs.creativeDirection,
1574
- landingStrategy: LandingStrategyEnum.default("classic").describe('Opcional. Estructura de la landing cl\xE1sica: "classic" (estructura original probada y recomendada; default del primer rollout), "auto" (la IA decide; experimental), "pain_solution" (problema\u2192soluci\xF3n), "social_proof" (confianza y evidencia), "benefits_clarity" (beneficios claros), "offer_value" (promoci\xF3n/valor) o "lifestyle_identity" (estilo de vida). Default "classic".')
1581
+ landingStrategy: LandingStrategyEnum.default("classic").describe('Opcional. Estructura de la landing cl\xE1sica: "classic" (estructura original probada y recomendada; default del primer rollout), "auto" (la IA decide; experimental), "pain_solution" (problema\u2192soluci\xF3n), "social_proof" (confianza y evidencia), "benefits_clarity" (beneficios claros), "offer_value" (promoci\xF3n/valor) o "lifestyle_identity" (estilo de vida). Default "classic".'),
1582
+ landingImageFormat: LandingImageFormatEnum.default("9:16").describe('Opcional. Formato/relaci\xF3n de aspecto de las im\xE1genes de la landing cl\xE1sica: "9:16" (vertical, por defecto), "4:5" (vertical compacto para feed) o "1:1" (cuadrado). Default "9:16".')
1575
1583
  });
1576
1584
  function registerCreativeTools(server, api, { transport = "http" } = {}) {
1577
1585
  server.tool("generate_video_creative", 'Genera UN video creativo independiente. Devuelve un taskId.\n\nREQUERIDOS: images (array de 1-5 fotos), title, primaryAngle, language, country, aspectRatio.\n\nANTES de invocar, PREG\xDANTALE al usuario por price y offer si no los mencion\xF3 \u2014 son los inputs de mayor impacto en conversi\xF3n y nunca debes asumirlos. NO defaultes country/language desde el contexto: preg\xFAntale expl\xEDcitamente. Si el usuario te dice "t\xFA elige" para price/offer, om\xEDtelos.\n\nOTROS OPCIONALES (puedes invocar sin ellos y dejar que la IA elija defaults sensatos): durationSeconds (m\xE1x 15), videoPreset (tipo/narrativa del video), features, cta, scriptNotes (dirige el guion Y el ambiente/escenario/est\xE9tica del video, ej. "en una playa, opulento").\n\nESTILO: v\xEDa MCP todos los videos se generan en estilo "Realista" (render \xFAnico de 15s con audio nativo). El estilo "Multiescena" (30s, multi-toma) NO est\xE1 disponible por MCP por su costo \u2014 si el usuario lo pide, dile que lo genere desde la web.\n\nGUION: el usuario NO escribe el guion desde cero \u2014 la IA lo redacta. Si quiere REVISARLO/EDITARLO antes de renderizar (igual que en la web), pasa `pauseForVideoScriptReview: true`; cuando la tarea quede pausada, usa get_video_script para leer las escenas, ofr\xE9celas al usuario, y llama approve_video_script con las ediciones. Para dirigir el guion ANTES de generar (sin pausar), usa scriptNotes.', VideoInput.shape, async (input) => {
@@ -1727,7 +1735,18 @@ var ShopifyInput = z6.object({
1727
1735
  /** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
1728
1736
  * When true, the landing renders variant-picker pills; when false, it ships
1729
1737
  * a single-option product page. Defaults to false. */
1730
- showVariantPicker: z6.boolean().optional()
1738
+ showVariantPicker: z6.boolean().optional(),
1739
+ /** Default `auto` updates the product previously published for this task and
1740
+ * shop. Use `existing` with targetProductId to choose another product, or
1741
+ * `new` only when the user explicitly wants a duplicate/new listing. */
1742
+ publicationMode: z6.enum([
1743
+ "auto",
1744
+ "existing",
1745
+ "new"
1746
+ ]).optional(),
1747
+ /** Shopify product GID, numeric id, or admin product URL. Required when
1748
+ * publicationMode is `existing`. */
1749
+ targetProductId: z6.string().trim().min(1).max(300).optional()
1731
1750
  });
1732
1751
  var McpCampaignConfig = z6.object({
1733
1752
  dailyBudgetUsd: z6.number().positive().optional(),
@@ -1765,8 +1784,19 @@ var TikTokInput = z6.object({
1765
1784
  primaryText: z6.string().trim().min(1).max(500).optional()
1766
1785
  });
1767
1786
  function registerPublishTools(server, api) {
1768
- server.tool("publish_to_shopify", "Publica la landing y producto generados en una tienda Shopify conectada. Precondici\xF3n: la tarea debe estar en status='complete' \u2014 usa `get_task_status` antes. ANTES de llamar preg\xFAntale al usuario \"\xBFTu producto tiene variantes (talla, color, etc.)?\" y pasa `showVariantPicker: true` si responde que s\xED \u2014 eso renderiza los pills de variantes en la landing. Por defecto publica TODAS las im\xE1genes que la tarea gener\xF3 para la landing; pasa `numLandingImages` s\xF3lo si el usuario te pide menos. La respuesta es markdown con el link a la landing y al producto.", ShopifyInput.shape, async (input) => {
1787
+ server.tool("publish_to_shopify", "Publica o actualiza la landing en una tienda Shopify conectada. 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. La respuesta es markdown con el link a la landing y al producto.", ShopifyInput.shape, async (input) => {
1769
1788
  try {
1789
+ if (input.publicationMode === "existing" && !input.targetProductId) {
1790
+ return {
1791
+ isError: true,
1792
+ content: [
1793
+ {
1794
+ type: "text",
1795
+ text: "targetProductId is required when publicationMode is existing"
1796
+ }
1797
+ ]
1798
+ };
1799
+ }
1770
1800
  const variantIndex = input.variantIndex ?? 0;
1771
1801
  const availableImages = await fetchLandingImageCount(api, input.taskId);
1772
1802
  const effectiveAvailable = availableImages > 0 ? availableImages : 3;
@@ -1780,7 +1810,9 @@ function registerPublishTools(server, api) {
1780
1810
  const r = await api.postJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/publish/shopify`, {
1781
1811
  shopDomain: input.shopDomain,
1782
1812
  selectedLandingImages,
1783
- showVariantPicker: input.showVariantPicker
1813
+ showVariantPicker: input.showVariantPicker,
1814
+ publicationMode: input.publicationMode,
1815
+ targetProductId: input.targetProductId
1784
1816
  });
1785
1817
  return renderShopifyPublishMarkdown(r);
1786
1818
  } catch (e) {
package/dist/stdio.js CHANGED
@@ -110,7 +110,7 @@ var ApiClient = class {
110
110
  };
111
111
 
112
112
  // src/index.ts
113
- var MCP_VERSION = "1.9.0";
113
+ var MCP_VERSION = "1.11.0";
114
114
 
115
115
  // src/tools/tasks.ts
116
116
  import { z as z2 } from "zod";
@@ -402,6 +402,11 @@ var LandingStrategyEnum = z.enum([
402
402
  "offer_value",
403
403
  "lifestyle_identity"
404
404
  ]);
405
+ var LandingImageFormatEnum = z.enum([
406
+ "9:16",
407
+ "4:5",
408
+ "1:1"
409
+ ]);
405
410
  var VideoPresetEnum = z.enum([
406
411
  "auto",
407
412
  "ugc_dynamic",
@@ -1163,7 +1168,8 @@ var CreateProductTaskInput = z2.object({
1163
1168
  competitorScanEnabled: z2.boolean().optional().describe("Opcional. Si true (default), el sistema analiza ads de competencia en Meta Ad Library y enriquece los \xE1ngulos generados con hooks y painPoints validados por el mercado (\u2248$0.20-0.48 extra por task \u2014 var\xEDa con cu\xE1ntas transcripciones de video se capturen). Si false, el pipeline genera \xE1ngulos sin esa se\xF1al \u2014 usa esto SOLO si el usuario ya tiene su propia investigaci\xF3n de mercado o quiere ahorrar costos en productos muy nicho sin presencia en Meta. Default true."),
1164
1169
  brandId: z2.string().uuid().optional().describe("Opcional. ID de una marca del usuario; si se env\xEDa, el sistema usa su voz, colores y persona para enriquecer los outputs generados."),
1165
1170
  creativeDirection: z2.string().max(500).optional().describe('Opcional. Direcci\xF3n creativa en texto libre (colores, mood, estilo) que aplica a los outputs de IMAGEN y LANDING (no al video \u2014 el estilo/ambiente del video se dirige con scriptNotes). M\xC1XIMA PRIORIDAD: anula marca, colores y estilo por defecto. Ej. "amarillo y azul, minimalista". M\xE1x 500 caracteres.'),
1166
- landingStrategy: LandingStrategyEnum.default("classic").describe('Opcional. Estructura de la landing cl\xE1sica cuando landing est\xE1 entre outputs: "classic" (estructura original probada y recomendada; default del primer rollout), "auto" (la IA decide; experimental), "pain_solution", "social_proof", "benefits_clarity", "offer_value" o "lifestyle_identity". Default "classic".')
1171
+ landingStrategy: LandingStrategyEnum.default("classic").describe('Opcional. Estructura de la landing cl\xE1sica cuando landing est\xE1 entre outputs: "classic" (estructura original probada y recomendada; default del primer rollout), "auto" (la IA decide; experimental), "pain_solution", "social_proof", "benefits_clarity", "offer_value" o "lifestyle_identity". Default "classic".'),
1172
+ landingImageFormat: LandingImageFormatEnum.default("9:16").describe('Opcional. Formato/relaci\xF3n de aspecto de las im\xE1genes de la landing cl\xE1sica cuando landing est\xE1 entre outputs: "9:16" (vertical, por defecto), "4:5" (vertical compacto para feed) o "1:1" (cuadrado). Default "9:16".')
1167
1173
  }).strict();
1168
1174
  var GetTaskStatusInput = z2.object({
1169
1175
  taskId: z2.string().min(1),
@@ -1293,6 +1299,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
1293
1299
  "options.brandId": input.brandId,
1294
1300
  "options.creativeDirection": input.creativeDirection,
1295
1301
  "options.landingStrategy": input.landingStrategy,
1302
+ "options.landingImageFormat": input.landingImageFormat,
1296
1303
  url: input.productUrl
1297
1304
  });
1298
1305
  const r = await api.postMultipart("/v1/ingest", {
@@ -1527,7 +1534,8 @@ var LandingInput = z3.object({
1527
1534
  competitorScanEnabled: CreativeRefs.competitorScanEnabled,
1528
1535
  brandId: CreativeRefs.brandId,
1529
1536
  creativeDirection: CreativeRefs.creativeDirection,
1530
- landingStrategy: LandingStrategyEnum.default("classic").describe('Opcional. Estructura de la landing cl\xE1sica: "classic" (estructura original probada y recomendada; default del primer rollout), "auto" (la IA decide; experimental), "pain_solution" (problema\u2192soluci\xF3n), "social_proof" (confianza y evidencia), "benefits_clarity" (beneficios claros), "offer_value" (promoci\xF3n/valor) o "lifestyle_identity" (estilo de vida). Default "classic".')
1537
+ landingStrategy: LandingStrategyEnum.default("classic").describe('Opcional. Estructura de la landing cl\xE1sica: "classic" (estructura original probada y recomendada; default del primer rollout), "auto" (la IA decide; experimental), "pain_solution" (problema\u2192soluci\xF3n), "social_proof" (confianza y evidencia), "benefits_clarity" (beneficios claros), "offer_value" (promoci\xF3n/valor) o "lifestyle_identity" (estilo de vida). Default "classic".'),
1538
+ landingImageFormat: LandingImageFormatEnum.default("9:16").describe('Opcional. Formato/relaci\xF3n de aspecto de las im\xE1genes de la landing cl\xE1sica: "9:16" (vertical, por defecto), "4:5" (vertical compacto para feed) o "1:1" (cuadrado). Default "9:16".')
1531
1539
  });
1532
1540
  function registerCreativeTools(server, api, { transport = "http" } = {}) {
1533
1541
  server.tool("generate_video_creative", 'Genera UN video creativo independiente. Devuelve un taskId.\n\nREQUERIDOS: images (array de 1-5 fotos), title, primaryAngle, language, country, aspectRatio.\n\nANTES de invocar, PREG\xDANTALE al usuario por price y offer si no los mencion\xF3 \u2014 son los inputs de mayor impacto en conversi\xF3n y nunca debes asumirlos. NO defaultes country/language desde el contexto: preg\xFAntale expl\xEDcitamente. Si el usuario te dice "t\xFA elige" para price/offer, om\xEDtelos.\n\nOTROS OPCIONALES (puedes invocar sin ellos y dejar que la IA elija defaults sensatos): durationSeconds (m\xE1x 15), videoPreset (tipo/narrativa del video), features, cta, scriptNotes (dirige el guion Y el ambiente/escenario/est\xE9tica del video, ej. "en una playa, opulento").\n\nESTILO: v\xEDa MCP todos los videos se generan en estilo "Realista" (render \xFAnico de 15s con audio nativo). El estilo "Multiescena" (30s, multi-toma) NO est\xE1 disponible por MCP por su costo \u2014 si el usuario lo pide, dile que lo genere desde la web.\n\nGUION: el usuario NO escribe el guion desde cero \u2014 la IA lo redacta. Si quiere REVISARLO/EDITARLO antes de renderizar (igual que en la web), pasa `pauseForVideoScriptReview: true`; cuando la tarea quede pausada, usa get_video_script para leer las escenas, ofr\xE9celas al usuario, y llama approve_video_script con las ediciones. Para dirigir el guion ANTES de generar (sin pausar), usa scriptNotes.', VideoInput.shape, async (input) => {
@@ -1683,7 +1691,18 @@ var ShopifyInput = z6.object({
1683
1691
  /** ASK THE USER before setting: "¿Tu producto tiene variantes (talla, color, etc.)?".
1684
1692
  * When true, the landing renders variant-picker pills; when false, it ships
1685
1693
  * a single-option product page. Defaults to false. */
1686
- showVariantPicker: z6.boolean().optional()
1694
+ showVariantPicker: z6.boolean().optional(),
1695
+ /** Default `auto` updates the product previously published for this task and
1696
+ * shop. Use `existing` with targetProductId to choose another product, or
1697
+ * `new` only when the user explicitly wants a duplicate/new listing. */
1698
+ publicationMode: z6.enum([
1699
+ "auto",
1700
+ "existing",
1701
+ "new"
1702
+ ]).optional(),
1703
+ /** Shopify product GID, numeric id, or admin product URL. Required when
1704
+ * publicationMode is `existing`. */
1705
+ targetProductId: z6.string().trim().min(1).max(300).optional()
1687
1706
  });
1688
1707
  var McpCampaignConfig = z6.object({
1689
1708
  dailyBudgetUsd: z6.number().positive().optional(),
@@ -1721,8 +1740,19 @@ var TikTokInput = z6.object({
1721
1740
  primaryText: z6.string().trim().min(1).max(500).optional()
1722
1741
  });
1723
1742
  function registerPublishTools(server, api) {
1724
- server.tool("publish_to_shopify", "Publica la landing y producto generados en una tienda Shopify conectada. Precondici\xF3n: la tarea debe estar en status='complete' \u2014 usa `get_task_status` antes. ANTES de llamar preg\xFAntale al usuario \"\xBFTu producto tiene variantes (talla, color, etc.)?\" y pasa `showVariantPicker: true` si responde que s\xED \u2014 eso renderiza los pills de variantes en la landing. Por defecto publica TODAS las im\xE1genes que la tarea gener\xF3 para la landing; pasa `numLandingImages` s\xF3lo si el usuario te pide menos. La respuesta es markdown con el link a la landing y al producto.", ShopifyInput.shape, async (input) => {
1743
+ server.tool("publish_to_shopify", "Publica o actualiza la landing en una tienda Shopify conectada. 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. La respuesta es markdown con el link a la landing y al producto.", ShopifyInput.shape, async (input) => {
1725
1744
  try {
1745
+ if (input.publicationMode === "existing" && !input.targetProductId) {
1746
+ return {
1747
+ isError: true,
1748
+ content: [
1749
+ {
1750
+ type: "text",
1751
+ text: "targetProductId is required when publicationMode is existing"
1752
+ }
1753
+ ]
1754
+ };
1755
+ }
1726
1756
  const variantIndex = input.variantIndex ?? 0;
1727
1757
  const availableImages = await fetchLandingImageCount(api, input.taskId);
1728
1758
  const effectiveAvailable = availableImages > 0 ? availableImages : 3;
@@ -1736,7 +1766,9 @@ function registerPublishTools(server, api) {
1736
1766
  const r = await api.postJson(`/v1/tasks/${encodeURIComponent(input.taskId)}/publish/shopify`, {
1737
1767
  shopDomain: input.shopDomain,
1738
1768
  selectedLandingImages,
1739
- showVariantPicker: input.showVariantPicker
1769
+ showVariantPicker: input.showVariantPicker,
1770
+ publicationMode: input.publicationMode,
1771
+ targetProductId: input.targetProductId
1740
1772
  });
1741
1773
  return renderShopifyPublishMarkdown(r);
1742
1774
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@productmaker/mcp",
3
- "version": "1.9.0",
3
+ "version": "1.11.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "Product Maker MCP server — connect Claude Desktop, Claude Code, ChatGPT, or Gemini to Product Maker AI to generate dropshipping landing pages, UGC videos, and ad creatives, and publish to Shopify and Meta Ads from your AI assistant.",