@productmaker/mcp 1.12.0 → 1.14.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/README.md CHANGED
@@ -78,6 +78,7 @@ Sign up at [productmaker.app](https://productmaker.app) and create a key (prefix
78
78
  | `regenerate_video_script` | Re-roll the pending video script (optionally with a different narrative style) |
79
79
  | `resolve_pending_action` | Resolve a gate the pipeline is waiting on (product-reference confirmation or angle-review approval) when `get_task_status` returns `pendingUserAction` |
80
80
  | `list_shopify_shops` | List connected Shopify stores |
81
+ | `list_shopify_products` | Search products in a connected Shopify store before choosing `targetProductId` for `publish_to_shopify` |
81
82
  | `list_meta_ad_accounts` | List connected Meta ad accounts |
82
83
  | `list_tiktok_advertisers` | List connected TikTok Ads advertiser accounts |
83
84
  | `publish_to_shopify` | Publish a completed task to a Shopify store (returns markdown with landing + product URLs) |
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.12.0";
116
+ var MCP_VERSION = "1.14.0";
117
117
 
118
118
  // src/tools/tasks.ts
119
119
  import { z as z2 } from "zod";
@@ -414,15 +414,12 @@ var VideoPresetEnum = z.enum([
414
414
  "auto",
415
415
  "ugc_dynamic",
416
416
  "ugc_unboxing_chaos",
417
- "ugc_curiosity_reveal",
418
- "ugc_listicle_3",
419
- "ugc_skeptic_confession",
420
- "ugc_demo_review",
421
417
  "problem_solution",
422
- "pixar_problem",
423
- "pixar_sassy",
424
418
  "product_showcase",
425
- "wearable_showcase"
419
+ "wearable_showcase",
420
+ "cinematic",
421
+ "pixar_problem",
422
+ "pixar_sassy"
426
423
  ]);
427
424
  var VIDEO_PRESET_RESOLUTION = {
428
425
  auto: null,
@@ -436,26 +433,6 @@ var VIDEO_PRESET_RESOLUTION = {
436
433
  narrativeStyle: "ugc_unboxing_chaos",
437
434
  videoModel: "veo"
438
435
  },
439
- ugc_curiosity_reveal: {
440
- videoStyle: "ai_dynamic",
441
- narrativeStyle: "ugc_curiosity_reveal",
442
- videoModel: "veo"
443
- },
444
- ugc_listicle_3: {
445
- videoStyle: "ai_dynamic",
446
- narrativeStyle: "ugc_listicle_3",
447
- videoModel: "kling"
448
- },
449
- ugc_skeptic_confession: {
450
- videoStyle: "ai_dynamic",
451
- narrativeStyle: "ugc_skeptic_confession",
452
- videoModel: "veo"
453
- },
454
- ugc_demo_review: {
455
- videoStyle: "ai_dynamic",
456
- narrativeStyle: "ugc_demo_review",
457
- videoModel: "kling"
458
- },
459
436
  problem_solution: {
460
437
  videoStyle: "ai_dynamic",
461
438
  narrativeStyle: "problem_product_cta",
@@ -480,6 +457,11 @@ var VIDEO_PRESET_RESOLUTION = {
480
457
  videoStyle: "ugc",
481
458
  narrativeStyle: "wearable_showcase",
482
459
  videoModel: "veo"
460
+ },
461
+ cinematic: {
462
+ videoStyle: "product_only",
463
+ narrativeStyle: "cinematic",
464
+ videoModel: "grok"
483
465
  }
484
466
  };
485
467
  function resolveVideoPreset(preset) {
@@ -508,6 +490,7 @@ var NarrativeStyleEnum = z.enum([
508
490
  "review",
509
491
  "problem_product_cta",
510
492
  "product_showcase",
493
+ "cinematic",
511
494
  "metaphor_product_cta",
512
495
  "wearable_showcase",
513
496
  "animated_problem_pixar",
@@ -518,6 +501,16 @@ var NarrativeStyleEnum = z.enum([
518
501
  "ugc_skeptic_confession",
519
502
  "ugc_demo_review"
520
503
  ]);
504
+ var PublicNarrativeStyleEnum = z.enum([
505
+ "review",
506
+ "ugc_unboxing_chaos",
507
+ "problem_product_cta",
508
+ "product_showcase",
509
+ "wearable_showcase",
510
+ "cinematic",
511
+ "animated_problem_pixar",
512
+ "sassy_product_pixar"
513
+ ]);
521
514
  var VideoModelEnum = z.enum([
522
515
  "veo",
523
516
  "wan",
@@ -1199,11 +1192,11 @@ var CreateProductTaskInput = z2.object({
1199
1192
  paymentMethods: z2.string().min(1).max(240).optional().describe('Opcional. M\xE9todos de pago que puede mostrar la landing. Ej. "Tarjeta, PSE, Nequi, Contraentrega". Si se omite, la landing no muestra m\xE9todos de pago.'),
1200
1193
  productContext: z2.string().max(3e3).optional().describe("Opcional. Informacion adicional del producto que la IA debe usar como contexto prioritario: uso real, comprador, restricciones, diferenciadores o direccion creativa. No reemplaza la investigacion de competencia; si hay competencia, guia el primer angulo y los outputs no deben contradecirlo."),
1201
1194
  productLabel: z2.string().trim().max(80).optional().describe('Opcional. Qu\xE9 es el producto, en pocas palabras (ej. "quita pelusas para ropa"). Es AUTORITATIVO: si lo env\xEDas, la IA usa esta identidad y no la reemplaza por otra categor\xEDa. Si lo omites, la IA detecta el producto desde la imagen.'),
1202
- videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla (en la web es el \xFAnico dropdown). Default "auto" (la IA escoge tras analizar el producto). V\xEDa MCP todo video renderiza a 15s (single_call), as\xED que ning\xFAn preset requiere 30s aqu\xED. Opciones: "auto", "ugc_dynamic" (UGC testimonial), "ugc_unboxing_chaos" (unboxing agresivo), "ugc_curiosity_reveal" (hook con producto oculto), "ugc_listicle_3" (3 razones, comprimido a 15s v\xEDa MCP \u2014 para el ritmo completo de 30s el usuario debe usar la web), "ugc_skeptic_confession" (esc\xE9ptico\u2192conversi\xF3n, comprimido a 15s v\xEDa MCP \u2014 para el ritmo completo de 30s el usuario debe usar la web), "ugc_demo_review" (demo en uso), "problem_solution" (problema\u2192producto\u2192CTA), "pixar_problem" (problema animado 3D), "pixar_sassy" (producto que habla), "product_showcase" (solo producto, sin actor), "wearable_showcase" (ropa/accesorios puestos). Si el usuario pide algo fuera de esta lista (ej. "estilo anime"), p\xEDdele una opci\xF3n v\xE1lida.'),
1195
+ videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo de video. Default "auto" (la IA escoge tras analizar el producto). Opciones actuales: "auto", "ugc_dynamic" (UGC Review), "ugc_unboxing_chaos" (Unboxing), "problem_solution" (problema\u2192soluci\xF3n), "product_showcase" (Producto en acci\xF3n: una prueba funcional), "wearable_showcase" (modelaje con la prenda/accesorio puesto), "cinematic" (historia visual premium), "pixar_problem" (problema animado Pixar), "pixar_sassy" (producto que habla Pixar). V\xEDa MCP todo video renderiza a 15s single_call. Si el usuario pide algo fuera de esta lista, p\xEDdele una opci\xF3n v\xE1lida.'),
1203
1196
  videoTier: VideoTierEnum.optional().describe('Opcional. Estilo del video. \xDAnico valor disponible v\xEDa MCP: "basic" = "Realista": render de 15s con audio nativo, el m\xE1s natural. El estilo "Multiescena" (multi-toma, 30s) NO est\xE1 disponible v\xEDa MCP por su costo (se factura por escena) \u2014 si el usuario lo pide, dile que lo genere desde la web. Omitir el campo equivale a "basic".'),
1204
1197
  videoResolution: VideoResolutionEnum.optional().describe('Opcional. Resoluci\xF3n del render. En la web se pregunta "\xBFResoluci\xF3n del video?". "480p" = excelente calidad y m\xE1s econ\xF3mico (RECOMENDADO, default si se omite); "720p" = m\xE1xima nitidez (~el doble de precio). Si el usuario no menciona resoluci\xF3n, OMITE este campo (el backend usa 480p).'),
1205
1198
  multiAngleVideos: z2.boolean().optional().describe("Opcional. Si true, genera m\xFAltiples \xE1ngulos de venta en paralelo (3-5 videos). Si false/omitido, genera 1 video del \xE1ngulo principal. Default false."),
1206
- angleNarratives: NarrativeStyleEnum.array().max(3).optional().describe('Opcional. Narrativa POR \xC1NGULO (posicional, override avanzado). Usa los mismos identificadores de narrativa que videoPreset (ej. "animated_problem_pixar", "review", "ugc_skeptic_confession"). angleNarratives[i] fuerza la narrativa del \xE1ngulo en la posici\xF3n i; si la lista es m\xE1s corta que la cantidad de \xE1ngulos, las posiciones restantes las elige la IA. Requiere multiAngleVideos:true para que haya varios \xE1ngulos. Si se omite, todas las narrativas se auto-seleccionan seg\xFAn el producto.'),
1199
+ angleNarratives: PublicNarrativeStyleEnum.array().max(3).optional().describe('Opcional. Narrativa POR \xC1NGULO (posicional). Valores actuales: "review", "ugc_unboxing_chaos", "problem_product_cta", "product_showcase", "wearable_showcase", "cinematic", "animated_problem_pixar", "sassy_product_pixar". angleNarratives[i] fuerza la narrativa del \xE1ngulo i; las posiciones restantes las elige la IA. Requiere multiAngleVideos:true para varios \xE1ngulos.'),
1207
1200
  outputs: z2.array(z2.enum([
1208
1201
  "landing",
1209
1202
  "video",
@@ -1212,7 +1205,7 @@ var CreateProductTaskInput = z2.object({
1212
1205
  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."),
1213
1206
  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."),
1214
1207
  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.'),
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".'),
1208
+ 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" (Landing Viral: UGC, WhatsApp, Instagram y evidencia social), "benefits_clarity", "offer_value" o "lifestyle_identity". Default "classic".'),
1216
1209
  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".')
1217
1210
  }).strict();
1218
1211
  var GetTaskStatusInput = z2.object({
@@ -1516,7 +1509,7 @@ var VideoInput = z3.object({
1516
1509
  "Auto"
1517
1510
  ]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
1518
1511
  durationSeconds: z3.number().int().min(5).max(15).optional().describe("Opcional. Duraci\xF3n del video en segundos (5-15). Default 15s. Para hooks cortos usa 8. Duraciones mayores a 15s (video Multiescena de 30s) NO est\xE1n disponibles v\xEDa MCP \u2014 ind\xEDcale al usuario que las genere desde la web."),
1519
- videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla (en la web es el \xFAnico dropdown). Default "auto" (la IA escoge tras analizar el producto). V\xEDa MCP todo video renderiza a m\xE1x. 15s (ver durationSeconds), as\xED que ning\xFAn preset requiere 30s aqu\xED. Opciones: "auto", "ugc_dynamic" (UGC cl\xE1sico testimonial multi-hook), "ugc_unboxing_chaos" (unboxing agresivo), "ugc_curiosity_reveal" (hook con producto oculto), "ugc_listicle_3" (3 razones numeradas, comprimido a 15s v\xEDa MCP \u2014 para el ritmo completo de 30s el usuario debe usar la web), "ugc_skeptic_confession" (esc\xE9ptico\u2192conversi\xF3n, comprimido a 15s v\xEDa MCP \u2014 para el ritmo completo de 30s el usuario debe usar la web), "ugc_demo_review" (demo en uso a lo largo de d\xEDas), "problem_solution" (problema\u2192producto\u2192CTA), "pixar_problem" (problema animado 3D estilo Pixar), "pixar_sassy" (producto que habla con personalidad), "product_showcase" (sin actor, solo producto), "wearable_showcase" (ropa/accesorios puestos).'),
1512
+ videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo de video. Default "auto". Opciones actuales: "auto", "ugc_dynamic" (UGC Review), "ugc_unboxing_chaos" (Unboxing), "problem_solution" (problema\u2192soluci\xF3n), "product_showcase" (Producto en acci\xF3n: una prueba funcional), "wearable_showcase" (modelaje con la prenda/accesorio puesto), "cinematic" (historia visual premium), "pixar_problem" (problema animado Pixar), "pixar_sassy" (producto que habla Pixar). V\xEDa MCP todo video renderiza a m\xE1x. 15s.'),
1520
1513
  videoTier: VideoTierEnum.optional().describe('Opcional. Estilo del video. \xDAnico valor disponible v\xEDa MCP: "basic" = "Realista": render de 15s con audio nativo, el m\xE1s natural. El estilo "Multiescena" (multi-toma, 30s) NO est\xE1 disponible v\xEDa MCP por su costo (se factura por escena) \u2014 si el usuario lo pide, dile que lo genere desde la web. Omitir el campo equivale a "basic".'),
1521
1514
  videoResolution: VideoResolutionEnum.optional().describe('Opcional. Resoluci\xF3n del render. En la web se pregunta "\xBFResoluci\xF3n del video?". "720p" = m\xE1xima nitidez (RECOMENDADO, default si se omite); "480p" = render m\xE1s econ\xF3mico. Si el usuario no menciona resoluci\xF3n, OMITE este campo (el backend usa 720p).'),
1522
1515
  features: z3.array(z3.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
@@ -1578,7 +1571,7 @@ var LandingInput = z3.object({
1578
1571
  competitorScanEnabled: CreativeRefs.competitorScanEnabled,
1579
1572
  brandId: CreativeRefs.brandId,
1580
1573
  creativeDirection: CreativeRefs.creativeDirection,
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".'),
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" (Landing Viral: UGC, WhatsApp, Instagram y evidencia social), "benefits_clarity" (beneficios claros), "offer_value" (promoci\xF3n/valor) o "lifestyle_identity" (estilo de vida). Default "classic".'),
1582
1575
  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".')
1583
1576
  });
1584
1577
  function registerCreativeTools(server, api, { transport = "http" } = {}) {
@@ -1653,7 +1646,7 @@ var ApproveVideoScriptInput = z4.object({
1653
1646
  var RegenerateVideoScriptInput = z4.object({
1654
1647
  taskId: z4.string().min(1).describe("ID de la tarea."),
1655
1648
  angleKey: z4.string().min(1).describe('Clave del \xE1ngulo a regenerar. Single-angle: pasa "angle_0". Multi-angle: el mismo que devuelve get_video_script. NO se puede omitir \u2014 el backend lo exige siempre.'),
1656
- narrativeStyle: NarrativeStyleEnum.describe('Narrativa para el nuevo guion (mismos valores que angleNarratives). REQUERIDO \u2014 el backend NO acepta "mantener la actual"; siempre tienes que elegir una. Si el usuario quiere re-rolear con la misma narrativa, pasa la que ya tiene el \xE1ngulo (la ves en get_video_script o en get_task_status).')
1649
+ narrativeStyle: NarrativeStyleEnum.describe('Narrativa para el nuevo guion. Incluye valores hist\xF3ricos porque esta herramienta tambi\xE9n regenera tareas antiguas. REQUERIDO \u2014 el backend NO acepta "mantener la actual"; siempre tienes que elegir una. Si el usuario quiere re-rolear con la misma narrativa, pasa la que ya tiene el \xE1ngulo (la ves en get_video_script o en get_task_status).')
1657
1650
  }).strict();
1658
1651
  function registerScriptReviewTools(server, api) {
1659
1652
  server.tool("get_video_script", 'Lee el guion del video pendiente de revisi\xF3n. S\xF3lo funciona cuando la tarea fue creada con `pauseForVideoScriptReview: true` (via generate_video_creative). Devuelve { status, angleKey, perClipSeconds?, scenes: [{ index, dialogue, maxWords, minWords?, beatId? }], error? }. El campo `error` (cuando viene con status="ready") es el motivo legible por el que un intento previo de aprobaci\xF3n fue rechazado (p.ej. el guion no cab\xEDa en el tiempo y el \xE1ngulo volvi\xF3 a "ready"); mu\xE9straselo al usuario.\n\nCU\xC1NDO LLAMAR: no existe un status top-level "awaiting_script_review" en get_task_status. Si la tarea fue creada con pauseForVideoScriptReview=true, polea get_task_status hasta que `status === "processing"` y a partir de ah\xED llama este tool peri\xF3dicamente \u2014 la respuesta de get_video_script trae su propio campo `status` que es la se\xF1al real:\n- "ready" \u2192 el guion est\xE1 listo; mu\xE9strale al usuario cada `scenes[].dialogue` por `index` con su tope `maxWords`, recoge ediciones, llama approve_video_script.\n- "not_ready" \u2192 el guion a\xFAn se est\xE1 generando; espera 5\u201310s y reintenta este tool.\n- "approved" \u2192 ya fue aprobado; en multi-angle puede ser que ese \xE1ngulo ya est\xE9 hecho \u2014 vuelve a llamar con otro angleKey.\n- "skipped" \u2192 el usuario opt\xF3 por no revisar en una pausa previa.\n- "error" \u2192 el backend no pudo generar el guion; usa `regenerate_video_script` para re-intentar (es la ruta de recuperaci\xF3n v\xE1lida \u2014 el guion no se aprueba en estado "error").\n\nMULTI-ANGLE: si la tarea tiene varios \xE1ngulos, omitir angleKey siempre devuelve "angle_0" literal \u2014 itera expl\xEDcitamente por "angle_0", "angle_1", ... hasta encontrar uno con status="ready".', GetVideoScriptInput.shape, async (input) => {
@@ -1702,6 +1695,23 @@ function registerConnectionTools(server, api) {
1702
1695
  throw translateError(e);
1703
1696
  }
1704
1697
  });
1698
+ server.tool("list_shopify_products", 'Busca productos en una tienda Shopify conectada para elegir el target de una republicaci\xF3n. Usa el `id` devuelto junto con `publicationMode="existing"` en `publish_to_shopify`. Si `taskId` se proporciona, la respuesta tambi\xE9n indica el producto publicado actualmente para esa tarea y tienda. No modifica Shopify.', z5.object({
1699
+ shopDomain: z5.string().min(3).max(120),
1700
+ query: z5.string().trim().max(120).optional(),
1701
+ taskId: z5.string().uuid().optional()
1702
+ }).shape, async ({ shopDomain, query, taskId }) => {
1703
+ try {
1704
+ const params = new URLSearchParams({
1705
+ shopDomain
1706
+ });
1707
+ if (query) params.set("query", query);
1708
+ if (taskId) params.set("taskId", taskId);
1709
+ const r = await api.getJson(`/v1/shopify/products?${params.toString()}`);
1710
+ return asText(r);
1711
+ } catch (e) {
1712
+ throw translateError(e);
1713
+ }
1714
+ });
1705
1715
  server.tool("list_meta_ad_accounts", "Lista las cuentas publicitarias de Meta conectadas. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar Meta en productmaker.app/integrations.", z5.object({}).shape, async () => {
1706
1716
  try {
1707
1717
  const r = await api.getJson("/v1/facebook/ad-accounts");
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.12.0";
113
+ var MCP_VERSION = "1.14.0";
114
114
 
115
115
  // src/tools/tasks.ts
116
116
  import { z as z2 } from "zod";
@@ -411,15 +411,12 @@ var VideoPresetEnum = z.enum([
411
411
  "auto",
412
412
  "ugc_dynamic",
413
413
  "ugc_unboxing_chaos",
414
- "ugc_curiosity_reveal",
415
- "ugc_listicle_3",
416
- "ugc_skeptic_confession",
417
- "ugc_demo_review",
418
414
  "problem_solution",
419
- "pixar_problem",
420
- "pixar_sassy",
421
415
  "product_showcase",
422
- "wearable_showcase"
416
+ "wearable_showcase",
417
+ "cinematic",
418
+ "pixar_problem",
419
+ "pixar_sassy"
423
420
  ]);
424
421
  var VIDEO_PRESET_RESOLUTION = {
425
422
  auto: null,
@@ -433,26 +430,6 @@ var VIDEO_PRESET_RESOLUTION = {
433
430
  narrativeStyle: "ugc_unboxing_chaos",
434
431
  videoModel: "veo"
435
432
  },
436
- ugc_curiosity_reveal: {
437
- videoStyle: "ai_dynamic",
438
- narrativeStyle: "ugc_curiosity_reveal",
439
- videoModel: "veo"
440
- },
441
- ugc_listicle_3: {
442
- videoStyle: "ai_dynamic",
443
- narrativeStyle: "ugc_listicle_3",
444
- videoModel: "kling"
445
- },
446
- ugc_skeptic_confession: {
447
- videoStyle: "ai_dynamic",
448
- narrativeStyle: "ugc_skeptic_confession",
449
- videoModel: "veo"
450
- },
451
- ugc_demo_review: {
452
- videoStyle: "ai_dynamic",
453
- narrativeStyle: "ugc_demo_review",
454
- videoModel: "kling"
455
- },
456
433
  problem_solution: {
457
434
  videoStyle: "ai_dynamic",
458
435
  narrativeStyle: "problem_product_cta",
@@ -477,6 +454,11 @@ var VIDEO_PRESET_RESOLUTION = {
477
454
  videoStyle: "ugc",
478
455
  narrativeStyle: "wearable_showcase",
479
456
  videoModel: "veo"
457
+ },
458
+ cinematic: {
459
+ videoStyle: "product_only",
460
+ narrativeStyle: "cinematic",
461
+ videoModel: "grok"
480
462
  }
481
463
  };
482
464
  function resolveVideoPreset(preset) {
@@ -505,6 +487,7 @@ var NarrativeStyleEnum = z.enum([
505
487
  "review",
506
488
  "problem_product_cta",
507
489
  "product_showcase",
490
+ "cinematic",
508
491
  "metaphor_product_cta",
509
492
  "wearable_showcase",
510
493
  "animated_problem_pixar",
@@ -515,6 +498,16 @@ var NarrativeStyleEnum = z.enum([
515
498
  "ugc_skeptic_confession",
516
499
  "ugc_demo_review"
517
500
  ]);
501
+ var PublicNarrativeStyleEnum = z.enum([
502
+ "review",
503
+ "ugc_unboxing_chaos",
504
+ "problem_product_cta",
505
+ "product_showcase",
506
+ "wearable_showcase",
507
+ "cinematic",
508
+ "animated_problem_pixar",
509
+ "sassy_product_pixar"
510
+ ]);
518
511
  var VideoModelEnum = z.enum([
519
512
  "veo",
520
513
  "wan",
@@ -1155,11 +1148,11 @@ var CreateProductTaskInput = z2.object({
1155
1148
  paymentMethods: z2.string().min(1).max(240).optional().describe('Opcional. M\xE9todos de pago que puede mostrar la landing. Ej. "Tarjeta, PSE, Nequi, Contraentrega". Si se omite, la landing no muestra m\xE9todos de pago.'),
1156
1149
  productContext: z2.string().max(3e3).optional().describe("Opcional. Informacion adicional del producto que la IA debe usar como contexto prioritario: uso real, comprador, restricciones, diferenciadores o direccion creativa. No reemplaza la investigacion de competencia; si hay competencia, guia el primer angulo y los outputs no deben contradecirlo."),
1157
1150
  productLabel: z2.string().trim().max(80).optional().describe('Opcional. Qu\xE9 es el producto, en pocas palabras (ej. "quita pelusas para ropa"). Es AUTORITATIVO: si lo env\xEDas, la IA usa esta identidad y no la reemplaza por otra categor\xEDa. Si lo omites, la IA detecta el producto desde la imagen.'),
1158
- videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla (en la web es el \xFAnico dropdown). Default "auto" (la IA escoge tras analizar el producto). V\xEDa MCP todo video renderiza a 15s (single_call), as\xED que ning\xFAn preset requiere 30s aqu\xED. Opciones: "auto", "ugc_dynamic" (UGC testimonial), "ugc_unboxing_chaos" (unboxing agresivo), "ugc_curiosity_reveal" (hook con producto oculto), "ugc_listicle_3" (3 razones, comprimido a 15s v\xEDa MCP \u2014 para el ritmo completo de 30s el usuario debe usar la web), "ugc_skeptic_confession" (esc\xE9ptico\u2192conversi\xF3n, comprimido a 15s v\xEDa MCP \u2014 para el ritmo completo de 30s el usuario debe usar la web), "ugc_demo_review" (demo en uso), "problem_solution" (problema\u2192producto\u2192CTA), "pixar_problem" (problema animado 3D), "pixar_sassy" (producto que habla), "product_showcase" (solo producto, sin actor), "wearable_showcase" (ropa/accesorios puestos). Si el usuario pide algo fuera de esta lista (ej. "estilo anime"), p\xEDdele una opci\xF3n v\xE1lida.'),
1151
+ videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo de video. Default "auto" (la IA escoge tras analizar el producto). Opciones actuales: "auto", "ugc_dynamic" (UGC Review), "ugc_unboxing_chaos" (Unboxing), "problem_solution" (problema\u2192soluci\xF3n), "product_showcase" (Producto en acci\xF3n: una prueba funcional), "wearable_showcase" (modelaje con la prenda/accesorio puesto), "cinematic" (historia visual premium), "pixar_problem" (problema animado Pixar), "pixar_sassy" (producto que habla Pixar). V\xEDa MCP todo video renderiza a 15s single_call. Si el usuario pide algo fuera de esta lista, p\xEDdele una opci\xF3n v\xE1lida.'),
1159
1152
  videoTier: VideoTierEnum.optional().describe('Opcional. Estilo del video. \xDAnico valor disponible v\xEDa MCP: "basic" = "Realista": render de 15s con audio nativo, el m\xE1s natural. El estilo "Multiescena" (multi-toma, 30s) NO est\xE1 disponible v\xEDa MCP por su costo (se factura por escena) \u2014 si el usuario lo pide, dile que lo genere desde la web. Omitir el campo equivale a "basic".'),
1160
1153
  videoResolution: VideoResolutionEnum.optional().describe('Opcional. Resoluci\xF3n del render. En la web se pregunta "\xBFResoluci\xF3n del video?". "480p" = excelente calidad y m\xE1s econ\xF3mico (RECOMENDADO, default si se omite); "720p" = m\xE1xima nitidez (~el doble de precio). Si el usuario no menciona resoluci\xF3n, OMITE este campo (el backend usa 480p).'),
1161
1154
  multiAngleVideos: z2.boolean().optional().describe("Opcional. Si true, genera m\xFAltiples \xE1ngulos de venta en paralelo (3-5 videos). Si false/omitido, genera 1 video del \xE1ngulo principal. Default false."),
1162
- angleNarratives: NarrativeStyleEnum.array().max(3).optional().describe('Opcional. Narrativa POR \xC1NGULO (posicional, override avanzado). Usa los mismos identificadores de narrativa que videoPreset (ej. "animated_problem_pixar", "review", "ugc_skeptic_confession"). angleNarratives[i] fuerza la narrativa del \xE1ngulo en la posici\xF3n i; si la lista es m\xE1s corta que la cantidad de \xE1ngulos, las posiciones restantes las elige la IA. Requiere multiAngleVideos:true para que haya varios \xE1ngulos. Si se omite, todas las narrativas se auto-seleccionan seg\xFAn el producto.'),
1155
+ angleNarratives: PublicNarrativeStyleEnum.array().max(3).optional().describe('Opcional. Narrativa POR \xC1NGULO (posicional). Valores actuales: "review", "ugc_unboxing_chaos", "problem_product_cta", "product_showcase", "wearable_showcase", "cinematic", "animated_problem_pixar", "sassy_product_pixar". angleNarratives[i] fuerza la narrativa del \xE1ngulo i; las posiciones restantes las elige la IA. Requiere multiAngleVideos:true para varios \xE1ngulos.'),
1163
1156
  outputs: z2.array(z2.enum([
1164
1157
  "landing",
1165
1158
  "video",
@@ -1168,7 +1161,7 @@ var CreateProductTaskInput = z2.object({
1168
1161
  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."),
1169
1162
  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."),
1170
1163
  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.'),
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".'),
1164
+ 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" (Landing Viral: UGC, WhatsApp, Instagram y evidencia social), "benefits_clarity", "offer_value" o "lifestyle_identity". Default "classic".'),
1172
1165
  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".')
1173
1166
  }).strict();
1174
1167
  var GetTaskStatusInput = z2.object({
@@ -1472,7 +1465,7 @@ var VideoInput = z3.object({
1472
1465
  "Auto"
1473
1466
  ]).describe('Relaci\xF3n de aspecto. "9:16" = vertical (TikTok/Reels \u2014 default recomendado para ads). "16:9" = horizontal (YouTube). "Auto" = decide la IA.'),
1474
1467
  durationSeconds: z3.number().int().min(5).max(15).optional().describe("Opcional. Duraci\xF3n del video en segundos (5-15). Default 15s. Para hooks cortos usa 8. Duraciones mayores a 15s (video Multiescena de 30s) NO est\xE1n disponibles v\xEDa MCP \u2014 ind\xEDcale al usuario que las genere desde la web."),
1475
- videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla (en la web es el \xFAnico dropdown). Default "auto" (la IA escoge tras analizar el producto). V\xEDa MCP todo video renderiza a m\xE1x. 15s (ver durationSeconds), as\xED que ning\xFAn preset requiere 30s aqu\xED. Opciones: "auto", "ugc_dynamic" (UGC cl\xE1sico testimonial multi-hook), "ugc_unboxing_chaos" (unboxing agresivo), "ugc_curiosity_reveal" (hook con producto oculto), "ugc_listicle_3" (3 razones numeradas, comprimido a 15s v\xEDa MCP \u2014 para el ritmo completo de 30s el usuario debe usar la web), "ugc_skeptic_confession" (esc\xE9ptico\u2192conversi\xF3n, comprimido a 15s v\xEDa MCP \u2014 para el ritmo completo de 30s el usuario debe usar la web), "ugc_demo_review" (demo en uso a lo largo de d\xEDas), "problem_solution" (problema\u2192producto\u2192CTA), "pixar_problem" (problema animado 3D estilo Pixar), "pixar_sassy" (producto que habla con personalidad), "product_showcase" (sin actor, solo producto), "wearable_showcase" (ropa/accesorios puestos).'),
1468
+ videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo de video. Default "auto". Opciones actuales: "auto", "ugc_dynamic" (UGC Review), "ugc_unboxing_chaos" (Unboxing), "problem_solution" (problema\u2192soluci\xF3n), "product_showcase" (Producto en acci\xF3n: una prueba funcional), "wearable_showcase" (modelaje con la prenda/accesorio puesto), "cinematic" (historia visual premium), "pixar_problem" (problema animado Pixar), "pixar_sassy" (producto que habla Pixar). V\xEDa MCP todo video renderiza a m\xE1x. 15s.'),
1476
1469
  videoTier: VideoTierEnum.optional().describe('Opcional. Estilo del video. \xDAnico valor disponible v\xEDa MCP: "basic" = "Realista": render de 15s con audio nativo, el m\xE1s natural. El estilo "Multiescena" (multi-toma, 30s) NO est\xE1 disponible v\xEDa MCP por su costo (se factura por escena) \u2014 si el usuario lo pide, dile que lo genere desde la web. Omitir el campo equivale a "basic".'),
1477
1470
  videoResolution: VideoResolutionEnum.optional().describe('Opcional. Resoluci\xF3n del render. En la web se pregunta "\xBFResoluci\xF3n del video?". "720p" = m\xE1xima nitidez (RECOMENDADO, default si se omite); "480p" = render m\xE1s econ\xF3mico. Si el usuario no menciona resoluci\xF3n, OMITE este campo (el backend usa 720p).'),
1478
1471
  features: z3.array(z3.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
@@ -1534,7 +1527,7 @@ var LandingInput = z3.object({
1534
1527
  competitorScanEnabled: CreativeRefs.competitorScanEnabled,
1535
1528
  brandId: CreativeRefs.brandId,
1536
1529
  creativeDirection: CreativeRefs.creativeDirection,
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".'),
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" (Landing Viral: UGC, WhatsApp, Instagram y evidencia social), "benefits_clarity" (beneficios claros), "offer_value" (promoci\xF3n/valor) o "lifestyle_identity" (estilo de vida). Default "classic".'),
1538
1531
  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".')
1539
1532
  });
1540
1533
  function registerCreativeTools(server, api, { transport = "http" } = {}) {
@@ -1609,7 +1602,7 @@ var ApproveVideoScriptInput = z4.object({
1609
1602
  var RegenerateVideoScriptInput = z4.object({
1610
1603
  taskId: z4.string().min(1).describe("ID de la tarea."),
1611
1604
  angleKey: z4.string().min(1).describe('Clave del \xE1ngulo a regenerar. Single-angle: pasa "angle_0". Multi-angle: el mismo que devuelve get_video_script. NO se puede omitir \u2014 el backend lo exige siempre.'),
1612
- narrativeStyle: NarrativeStyleEnum.describe('Narrativa para el nuevo guion (mismos valores que angleNarratives). REQUERIDO \u2014 el backend NO acepta "mantener la actual"; siempre tienes que elegir una. Si el usuario quiere re-rolear con la misma narrativa, pasa la que ya tiene el \xE1ngulo (la ves en get_video_script o en get_task_status).')
1605
+ narrativeStyle: NarrativeStyleEnum.describe('Narrativa para el nuevo guion. Incluye valores hist\xF3ricos porque esta herramienta tambi\xE9n regenera tareas antiguas. REQUERIDO \u2014 el backend NO acepta "mantener la actual"; siempre tienes que elegir una. Si el usuario quiere re-rolear con la misma narrativa, pasa la que ya tiene el \xE1ngulo (la ves en get_video_script o en get_task_status).')
1613
1606
  }).strict();
1614
1607
  function registerScriptReviewTools(server, api) {
1615
1608
  server.tool("get_video_script", 'Lee el guion del video pendiente de revisi\xF3n. S\xF3lo funciona cuando la tarea fue creada con `pauseForVideoScriptReview: true` (via generate_video_creative). Devuelve { status, angleKey, perClipSeconds?, scenes: [{ index, dialogue, maxWords, minWords?, beatId? }], error? }. El campo `error` (cuando viene con status="ready") es el motivo legible por el que un intento previo de aprobaci\xF3n fue rechazado (p.ej. el guion no cab\xEDa en el tiempo y el \xE1ngulo volvi\xF3 a "ready"); mu\xE9straselo al usuario.\n\nCU\xC1NDO LLAMAR: no existe un status top-level "awaiting_script_review" en get_task_status. Si la tarea fue creada con pauseForVideoScriptReview=true, polea get_task_status hasta que `status === "processing"` y a partir de ah\xED llama este tool peri\xF3dicamente \u2014 la respuesta de get_video_script trae su propio campo `status` que es la se\xF1al real:\n- "ready" \u2192 el guion est\xE1 listo; mu\xE9strale al usuario cada `scenes[].dialogue` por `index` con su tope `maxWords`, recoge ediciones, llama approve_video_script.\n- "not_ready" \u2192 el guion a\xFAn se est\xE1 generando; espera 5\u201310s y reintenta este tool.\n- "approved" \u2192 ya fue aprobado; en multi-angle puede ser que ese \xE1ngulo ya est\xE9 hecho \u2014 vuelve a llamar con otro angleKey.\n- "skipped" \u2192 el usuario opt\xF3 por no revisar en una pausa previa.\n- "error" \u2192 el backend no pudo generar el guion; usa `regenerate_video_script` para re-intentar (es la ruta de recuperaci\xF3n v\xE1lida \u2014 el guion no se aprueba en estado "error").\n\nMULTI-ANGLE: si la tarea tiene varios \xE1ngulos, omitir angleKey siempre devuelve "angle_0" literal \u2014 itera expl\xEDcitamente por "angle_0", "angle_1", ... hasta encontrar uno con status="ready".', GetVideoScriptInput.shape, async (input) => {
@@ -1658,6 +1651,23 @@ function registerConnectionTools(server, api) {
1658
1651
  throw translateError(e);
1659
1652
  }
1660
1653
  });
1654
+ server.tool("list_shopify_products", 'Busca productos en una tienda Shopify conectada para elegir el target de una republicaci\xF3n. Usa el `id` devuelto junto con `publicationMode="existing"` en `publish_to_shopify`. Si `taskId` se proporciona, la respuesta tambi\xE9n indica el producto publicado actualmente para esa tarea y tienda. No modifica Shopify.', z5.object({
1655
+ shopDomain: z5.string().min(3).max(120),
1656
+ query: z5.string().trim().max(120).optional(),
1657
+ taskId: z5.string().uuid().optional()
1658
+ }).shape, async ({ shopDomain, query, taskId }) => {
1659
+ try {
1660
+ const params = new URLSearchParams({
1661
+ shopDomain
1662
+ });
1663
+ if (query) params.set("query", query);
1664
+ if (taskId) params.set("taskId", taskId);
1665
+ const r = await api.getJson(`/v1/shopify/products?${params.toString()}`);
1666
+ return asText(r);
1667
+ } catch (e) {
1668
+ throw translateError(e);
1669
+ }
1670
+ });
1661
1671
  server.tool("list_meta_ad_accounts", "Lista las cuentas publicitarias de Meta conectadas. Si est\xE1 vac\xEDa, ind\xEDcale al usuario conectar Meta en productmaker.app/integrations.", z5.object({}).shape, async () => {
1662
1672
  try {
1663
1673
  const r = await api.getJson("/v1/facebook/ad-accounts");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@productmaker/mcp",
3
- "version": "1.12.0",
3
+ "version": "1.14.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.",
@@ -63,8 +63,8 @@
63
63
  "tsx": "^4.16.0",
64
64
  "typescript": "^7.0.2",
65
65
  "vitest": "^2.1.9",
66
- "@packages/product-economics": "0.1.0",
67
- "@packages/types": "0.1.0"
66
+ "@packages/types": "0.1.0",
67
+ "@packages/product-economics": "0.1.0"
68
68
  },
69
69
  "publishConfig": {
70
70
  "access": "public"