@productmaker/mcp 1.2.1 → 1.6.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 +37 -11
- package/dist/stdio.js +37 -11
- package/package.json +2 -2
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.
|
|
116
|
+
var MCP_VERSION = "1.6.0";
|
|
117
117
|
|
|
118
118
|
// src/tools/tasks.ts
|
|
119
119
|
import { z as z2 } from "zod";
|
|
@@ -1186,7 +1186,8 @@ var CreateProductTaskInput = z2.object({
|
|
|
1186
1186
|
"image"
|
|
1187
1187
|
])).min(2).max(3).optional().describe('Opcional. Qu\xE9 outputs generar. Default: todos ["landing","video","image"]. Para 2 outputs: ["landing","image"] (sin video), ["video","image"] (sin landing), ["landing","video"] (sin imagen). Para 1 solo output, usa generate_landing / generate_video_creative / generate_image_creatives.'),
|
|
1188
1188
|
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."),
|
|
1189
|
-
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.")
|
|
1189
|
+
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."),
|
|
1190
|
+
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.')
|
|
1190
1191
|
}).strict();
|
|
1191
1192
|
var GetTaskStatusInput = z2.object({
|
|
1192
1193
|
taskId: z2.string().min(1),
|
|
@@ -1215,7 +1216,18 @@ var PendingActionPayload = z2.discriminatedUnion("kind", [
|
|
|
1215
1216
|
z2.object({
|
|
1216
1217
|
kind: z2.literal("product_reference_confirmation"),
|
|
1217
1218
|
userChoice: ProductReferenceUserChoiceEnum,
|
|
1218
|
-
confirmedProductNames: z2.array(z2.string().min(1).max(120)).min(2).max(8).optional()
|
|
1219
|
+
confirmedProductNames: z2.array(z2.string().min(1).max(120)).min(2).max(8).optional(),
|
|
1220
|
+
// SCOPED multi-scene opt-in — the ONLY way STANDARD ("Multiescena") is
|
|
1221
|
+
// reachable via MCP. Valid ONLY together with userChoice='variants' (same
|
|
1222
|
+
// product in several colors): renders one shot per color and stitches them,
|
|
1223
|
+
// so every variant color is shown off. Ignored for any other userChoice.
|
|
1224
|
+
// We deliberately do NOT expose the raw `standard` tier on the MCP video
|
|
1225
|
+
// knobs (VideoTierEnum stays {basic}) because a general tier dial with no
|
|
1226
|
+
// cost surface caused a prod over-spend incident (2026-07-04); this flag is
|
|
1227
|
+
// safe because multi-scene IS the intended render for a color variant set,
|
|
1228
|
+
// not a blind quality upsell. Maps to `videoTier: 'standard'` on the
|
|
1229
|
+
// confirm call; the backend re-gates it (variants + variant_set only).
|
|
1230
|
+
useMultiSceneShowcase: z2.boolean().optional().describe('Opcional (SOLO con userChoice="variants"). true = render Multiescena: una toma por color, mostrando todas las variantes, hasta 30s. Se factura POR ESCENA (m\xE1s caro que el render normal de una sola toma) \u2014 AV\xCDSALE del mayor costo al usuario antes de activarlo. Omitir = render est\xE1ndar de una sola toma. Se ignora si userChoice no es "variants".')
|
|
1219
1231
|
}),
|
|
1220
1232
|
z2.object({
|
|
1221
1233
|
kind: z2.literal("angle_review_approval"),
|
|
@@ -1233,6 +1245,10 @@ var ResolvePendingActionInput = z2.object({
|
|
|
1233
1245
|
taskId: z2.string().min(1),
|
|
1234
1246
|
action: PendingActionPayload
|
|
1235
1247
|
}).strict();
|
|
1248
|
+
function resolveVariantConfirmVideoTier(action) {
|
|
1249
|
+
return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
|
|
1250
|
+
}
|
|
1251
|
+
__name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
|
|
1236
1252
|
var StatusForResolveSchema = z2.object({
|
|
1237
1253
|
pendingUserAction: z2.object({
|
|
1238
1254
|
kind: z2.string(),
|
|
@@ -1263,7 +1279,7 @@ function formatOutputList(outputs) {
|
|
|
1263
1279
|
}
|
|
1264
1280
|
__name(formatOutputList, "formatOutputList");
|
|
1265
1281
|
function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http" } = {}) {
|
|
1266
|
-
server.tool("create_product_task", 'Crea una tarea de producto desde una o varias fotos. Por defecto genera los 3 outputs (landing + video + image creatives) EN UN MISMO TASK. Devuelve un taskId.\n\nINPUTS REQUERIDOS: images (1-5 fotos del MISMO producto; la primera es la hero), language, country.\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 del chat: preg\xFAntale expl\xEDcitamente, incluso si el usuario est\xE1 en LATAM. Si te dice "t\xFA elige" para price/offer, om\xEDtelos.\n\nANTES de invocar, DILE CLARAMENTE al usuario qu\xE9 outputs vas a generar y que `create_product_task` los genera juntos en un mismo task. Ejemplo: "Voy a generar landing + video en un mismo task; esto consume cr\xE9ditos de ambos outputs". Si el usuario s\xF3lo quiere un output, usa generate_landing / generate_video_creative / generate_image_creatives en vez de este tool.\n\nSi el usuario tiene varias fotos del MISMO producto (frente, lateral, detalle, uso), incl\xFAyelas todas en images[] hasta 5. Si tiene fotos de productos DIFERENTES, son llamadas separadas (una tarea por producto).\n\nOTROS OPCIONALES (puedes invocar sin ellos y dejar que la IA elija defaults sensatos):\n- videoPreset (tipo/narrativa del video \u2014 \xFAnico campo de estilo)\n- multiAngleVideos (booleano)\n- angleNarratives (override per-angle de la narrativa, avanzado)\n- outputs (subconjunto de {landing,video,image}); omitido = ["landing","video","image"] en un mismo task\n- productUrl\n\nGUION: para revisar/editar el guion del video antes de renderizar, usa `generate_video_creative` con `pauseForVideoScriptReview: true` en vez de este tool. Despu\xE9s de la pausa: get_video_script para leer, approve_video_script para aprobar con ediciones.\n\nUsa SOLO valores declarados en los enums. Si el usuario pide algo no soportado (ej. "estilo anime", "modelo X"), p\xEDdele una opci\xF3n v\xE1lida del enum correspondiente.', CreateProductTaskInput.shape, async (input) => {
|
|
1282
|
+
server.tool("create_product_task", 'Crea una tarea de producto desde una o varias fotos. Por defecto genera los 3 outputs (landing + video + image creatives) EN UN MISMO TASK. Devuelve un taskId.\n\nINPUTS REQUERIDOS: images (1-5 fotos del MISMO producto; la primera es la hero), language, country.\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 del chat: preg\xFAntale expl\xEDcitamente, incluso si el usuario est\xE1 en LATAM. Si te dice "t\xFA elige" para price/offer, om\xEDtelos.\n\nANTES de invocar, DILE CLARAMENTE al usuario qu\xE9 outputs vas a generar y que `create_product_task` los genera juntos en un mismo task. Ejemplo: "Voy a generar landing + video en un mismo task; esto consume cr\xE9ditos de ambos outputs". Si el usuario s\xF3lo quiere un output, usa generate_landing / generate_video_creative / generate_image_creatives en vez de este tool.\n\nSi el usuario tiene varias fotos del MISMO producto (frente, lateral, detalle, uso), incl\xFAyelas todas en images[] hasta 5. Si tiene fotos de productos DIFERENTES, son llamadas separadas (una tarea por producto).\n\nOTROS OPCIONALES (puedes invocar sin ellos y dejar que la IA elija defaults sensatos):\n- videoPreset (tipo/narrativa del video \u2014 \xFAnico campo de estilo)\n- multiAngleVideos (booleano)\n- angleNarratives (override per-angle de la narrativa, avanzado)\n- outputs (subconjunto de {landing,video,image}); omitido = ["landing","video","image"] en un mismo task\n- productUrl\n- creativeDirection (direcci\xF3n creativa para im\xE1genes y landing; el estilo del video se dirige con scriptNotes)\n\nGUION: para revisar/editar el guion del video antes de renderizar, usa `generate_video_creative` con `pauseForVideoScriptReview: true` en vez de este tool. Despu\xE9s de la pausa: get_video_script para leer, approve_video_script para aprobar con ediciones.\n\nUsa SOLO valores declarados en los enums. Si el usuario pide algo no soportado (ej. "estilo anime", "modelo X"), p\xEDdele una opci\xF3n v\xE1lida del enum correspondiente.', CreateProductTaskInput.shape, async (input) => {
|
|
1267
1283
|
try {
|
|
1268
1284
|
const imgs = await resolveImages(input.images, {
|
|
1269
1285
|
transport
|
|
@@ -1292,6 +1308,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
1292
1308
|
"options.selectedOutputs": input.outputs,
|
|
1293
1309
|
"options.competitorScanEnabled": input.competitorScanEnabled,
|
|
1294
1310
|
"options.brandId": input.brandId,
|
|
1311
|
+
"options.creativeDirection": input.creativeDirection,
|
|
1295
1312
|
url: input.productUrl
|
|
1296
1313
|
});
|
|
1297
1314
|
const r = await api.postMultipart("/v1/ingest", {
|
|
@@ -1366,7 +1383,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
1366
1383
|
throw translateError(e);
|
|
1367
1384
|
}
|
|
1368
1385
|
});
|
|
1369
|
-
server.tool("resolve_pending_action", 'Resuelve una acci\xF3n que el pipeline est\xE1 esperando del usuario. La llamas cuando `get_task_status` devuelve `pendingUserAction` (sin esto la tarea NUNCA avanza por m\xE1s que esperes). El `action.kind` que mandas DEBE coincidir con el `pendingUserAction.kind` que viste en el status \u2014 es el contrato de simetr\xEDa que mantiene este tool como \xFAnica superficie de resoluci\xF3n de gates.\n\nKINDS soportados hoy:\n\n\u2022 `product_reference_confirmation` \u2014 el clasificador no est\xE1 seguro si las fotos son variantes del mismo producto o productos distintos. Payload: `userChoice` \u2208 { "same_product" (mismo producto, fotos son s\xF3lo \xE1ngulos distintos), "variants" (mismo producto, distintos colores/tallas/sabores), "combo_distinct" (son 2 productos distintos vendidos juntos como combo \u2014 marca la tarea como combo), "different_products" (productos distintos y el usuario quiere generar s\xF3lo uno \u2014 el backend bloquea hasta que selecciones im\xE1genes de un solo producto; normalmente NO es la respuesta correcta para destrabar) }. PREG\xDANTALE al usuario antes de invocar \u2014 `pendingUserAction.targetProductLabel` y `reason` te dan contexto para formularle la pregunta en lenguaje natural. Usa SOLO valores presentes en `pendingUserAction.options`. Si el status trae `proposedProductNames` (lista propuesta de nombres), el combo es INCIERTO: mu\xE9strale los nombres al usuario y, si confirma que son productos distintos, manda `userChoice: "combo_distinct"` JUNTO CON `confirmedProductNames` (la lista confirmada o editada, 2-8 nombres) para separarlo en productos distintos; si el usuario dice que es un solo producto, manda `userChoice: "same_product"` (sin `confirmedProductNames`).\n\n\u2022 `angle_review_approval` \u2014 el usuario opto por `pauseForAngleReview: true` al crear el creativo (en `generate_image_creatives`, `generate_video_creative` o `generate_landing`). La IA ya gener\xF3 el \xE1ngulo + pain points + benefits + features y la tarea est\xE1 pausada esperando que el usuario apruebe o edite antes de renderizar. Lee `customAngleDraft`, `painPointsDraft`, `benefitsDraft`, `featuresDraft` del `pendingUserAction` para mostr\xE1rselos al usuario en lenguaje natural ("la IA propuso este \xE1ngulo: ... \xBFlo apruebas o cambias algo?"). Payload (TODOS opcionales \u2014 sin nada = aprobar tal cual; con campos = aprobar con ediciones): `customAngle` (override del \xE1ngulo), `painPoints`, `benefits`, `features` (array de 1-10 bullets). Llamar sin ning\xFAn campo es v\xE1lido y aprueba el draft sin cambios.\n\nFuturos kinds (no implementados a\xFAn) se agregar\xE1n como nuevas variantes del `action` discriminated union \u2014 el flujo "lee kind del status \u2192 manda mismo kind aqu\xED" sigue igual.', ResolvePendingActionInput.shape, async (input) => {
|
|
1386
|
+
server.tool("resolve_pending_action", 'Resuelve una acci\xF3n que el pipeline est\xE1 esperando del usuario. La llamas cuando `get_task_status` devuelve `pendingUserAction` (sin esto la tarea NUNCA avanza por m\xE1s que esperes). El `action.kind` que mandas DEBE coincidir con el `pendingUserAction.kind` que viste en el status \u2014 es el contrato de simetr\xEDa que mantiene este tool como \xFAnica superficie de resoluci\xF3n de gates.\n\nKINDS soportados hoy:\n\n\u2022 `product_reference_confirmation` \u2014 el clasificador no est\xE1 seguro si las fotos son variantes del mismo producto o productos distintos. Payload: `userChoice` \u2208 { "same_product" (mismo producto, fotos son s\xF3lo \xE1ngulos distintos), "variants" (mismo producto, distintos colores/tallas/sabores), "combo_distinct" (son 2 productos distintos vendidos juntos como combo \u2014 marca la tarea como combo), "different_products" (productos distintos y el usuario quiere generar s\xF3lo uno \u2014 el backend bloquea hasta que selecciones im\xE1genes de un solo producto; normalmente NO es la respuesta correcta para destrabar) }. PREG\xDANTALE al usuario antes de invocar \u2014 `pendingUserAction.targetProductLabel` y `reason` te dan contexto para formularle la pregunta en lenguaje natural. Usa SOLO valores presentes en `pendingUserAction.options`. Si el status trae `proposedProductNames` (lista propuesta de nombres), el combo es INCIERTO: mu\xE9strale los nombres al usuario y, si confirma que son productos distintos, manda `userChoice: "combo_distinct"` JUNTO CON `confirmedProductNames` (la lista confirmada o editada, 2-8 nombres) para separarlo en productos distintos; si el usuario dice que es un solo producto, manda `userChoice: "same_product"` (sin `confirmedProductNames`). Cuando el producto son VARIANTES DE COLOR (mismo producto, distintos colores) puedes ofrecer un video "Multiescena" que muestra una toma por color: manda `userChoice: "variants"` JUNTO CON `useMultiSceneShowcase: true`. AVISA al usuario que ese modo se factura por escena (m\xE1s caro que el render normal de una sola toma) antes de activarlo; si prefiere el render normal, manda solo `userChoice: "variants"` sin el flag.\n\n\u2022 `angle_review_approval` \u2014 el usuario opto por `pauseForAngleReview: true` al crear el creativo (en `generate_image_creatives`, `generate_video_creative` o `generate_landing`). La IA ya gener\xF3 el \xE1ngulo + pain points + benefits + features y la tarea est\xE1 pausada esperando que el usuario apruebe o edite antes de renderizar. Lee `customAngleDraft`, `painPointsDraft`, `benefitsDraft`, `featuresDraft` del `pendingUserAction` para mostr\xE1rselos al usuario en lenguaje natural ("la IA propuso este \xE1ngulo: ... \xBFlo apruebas o cambias algo?"). Payload (TODOS opcionales \u2014 sin nada = aprobar tal cual; con campos = aprobar con ediciones): `customAngle` (override del \xE1ngulo), `painPoints`, `benefits`, `features` (array de 1-10 bullets). Llamar sin ning\xFAn campo es v\xE1lido y aprueba el draft sin cambios.\n\nFuturos kinds (no implementados a\xFAn) se agregar\xE1n como nuevas variantes del `action` discriminated union \u2014 el flujo "lee kind del status \u2192 manda mismo kind aqu\xED" sigue igual.', ResolvePendingActionInput.shape, async (input) => {
|
|
1370
1387
|
try {
|
|
1371
1388
|
const raw = await api.getJson(`/v1/status/${encodeURIComponent(input.taskId)}`);
|
|
1372
1389
|
const status = StatusForResolveSchema.parse(raw);
|
|
@@ -1395,6 +1412,12 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
1395
1412
|
},
|
|
1396
1413
|
...input.action.confirmedProductNames ? {
|
|
1397
1414
|
confirmedProductNames: input.action.confirmedProductNames
|
|
1415
|
+
} : {},
|
|
1416
|
+
// Multi-scene color showcase → upgrade to STANDARD. Gated to the
|
|
1417
|
+
// variants choice client-side so the wire body stays coherent; the
|
|
1418
|
+
// backend re-gates on variant_set grouping.
|
|
1419
|
+
...resolveVariantConfirmVideoTier(input.action) === "standard" ? {
|
|
1420
|
+
videoTier: "standard"
|
|
1398
1421
|
} : {}
|
|
1399
1422
|
});
|
|
1400
1423
|
return asText(r);
|
|
@@ -1441,7 +1464,8 @@ var CreativeRefs = {
|
|
|
1441
1464
|
buyerPersona: z3.string().optional().describe('Opcional. Descripci\xF3n del comprador objetivo. Ej. "Mujeres 35-50, profesionales urbanas". Si se omite, la IA infiere desde el \xE1ngulo y producto.'),
|
|
1442
1465
|
isCombo: z3.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
|
|
1443
1466
|
competitorScanEnabled: z3.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."),
|
|
1444
|
-
brandId: z3.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 el creativo.")
|
|
1467
|
+
brandId: z3.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 el creativo."),
|
|
1468
|
+
creativeDirection: z3.string().max(500).optional().describe('Opcional. Direcci\xF3n creativa en texto libre (colores, mood, estilo). M\xC1XIMA PRIORIDAD: anula marca, colores y estilo por defecto. Aplica SOLO a im\xE1genes y landing. Ej. im\xE1genes: "amarillo, azul y rojo, minimalista"; landing: "elegante, paleta fr\xEDa". Para video usa scriptNotes (ambiente/escenario). M\xE1x 500 caracteres.')
|
|
1445
1469
|
};
|
|
1446
1470
|
var VideoInput = z3.object({
|
|
1447
1471
|
images: CreativeRefs.images,
|
|
@@ -1498,7 +1522,8 @@ var ImageInput = z3.object({
|
|
|
1498
1522
|
productLabel: CreativeRefs.productLabel,
|
|
1499
1523
|
isCombo: CreativeRefs.isCombo,
|
|
1500
1524
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
1501
|
-
brandId: CreativeRefs.brandId
|
|
1525
|
+
brandId: CreativeRefs.brandId,
|
|
1526
|
+
creativeDirection: CreativeRefs.creativeDirection
|
|
1502
1527
|
});
|
|
1503
1528
|
var LandingInput = z3.object({
|
|
1504
1529
|
images: CreativeRefs.images,
|
|
@@ -1513,10 +1538,11 @@ var LandingInput = z3.object({
|
|
|
1513
1538
|
paymentMethods: z3.string().optional().describe('Opcional. M\xE9todos de pago a mostrar (lista libre). Ej. "Tarjeta, PSE, Nequi, Contraentrega". Si se omite, la IA usa los m\xE1s comunes del pa\xEDs.'),
|
|
1514
1539
|
isCombo: CreativeRefs.isCombo,
|
|
1515
1540
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
1516
|
-
brandId: CreativeRefs.brandId
|
|
1541
|
+
brandId: CreativeRefs.brandId,
|
|
1542
|
+
creativeDirection: CreativeRefs.creativeDirection
|
|
1517
1543
|
});
|
|
1518
1544
|
function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
1519
|
-
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.\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) => {
|
|
1545
|
+
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) => {
|
|
1520
1546
|
try {
|
|
1521
1547
|
const resolved = resolveVideoPreset(input.videoPreset);
|
|
1522
1548
|
const wireTier = resolveVideoTier(input.videoTier);
|
|
@@ -1540,7 +1566,7 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
1540
1566
|
throw translateError(e);
|
|
1541
1567
|
}
|
|
1542
1568
|
});
|
|
1543
|
-
server.tool("generate_image_creatives", 'Genera N variantes de imagen creativa (ads est\xE1ticos). Devuelve un taskId.\n\nREQUERIDOS: images (array de 1-5 fotos), productTitle, primaryAngle, language, country.\n\nANTES de invocar, PREG\xDANTALE al usuario:\n- price y offer (se renderizan en la imagen \u2014 nunca los asumas; si dice "t\xFA elige", om\xEDtelos).\n- compareAtPrice si hay descuento (ej. "$129.000" para tachar).\n- creativeArchetype y visualTreatment si quiere un look espec\xEDfico (performance vs premium DTC). Si no le importa, om\xEDtelos.\n\nNO defaultes country/language desde el contexto: preg\xFAntale expl\xEDcitamente.\n\nOTROS OPCIONALES: buyerPersona, nVariants (default 3), imageModel, imageStyle, styleMix, aspectRatio (default 4:5), generateAdCopy, pauseForAngleReview, features, brandColors, isCombo.', ImageInput.shape, async (input) => {
|
|
1569
|
+
server.tool("generate_image_creatives", 'Genera N variantes de imagen creativa (ads est\xE1ticos). Devuelve un taskId.\n\nREQUERIDOS: images (array de 1-5 fotos), productTitle, primaryAngle, language, country.\n\nANTES de invocar, PREG\xDANTALE al usuario:\n- price y offer (se renderizan en la imagen \u2014 nunca los asumas; si dice "t\xFA elige", om\xEDtelos).\n- compareAtPrice si hay descuento (ej. "$129.000" para tachar).\n- creativeArchetype y visualTreatment si quiere un look espec\xEDfico (performance vs premium DTC). Si no le importa, om\xEDtelos.\n\nNO defaultes country/language desde el contexto: preg\xFAntale expl\xEDcitamente.\n\nOTROS OPCIONALES: buyerPersona, nVariants (default 3), imageModel, imageStyle, styleMix, aspectRatio (default 4:5), generateAdCopy, pauseForAngleReview, features, brandColors, isCombo, creativeDirection.', ImageInput.shape, async (input) => {
|
|
1544
1570
|
try {
|
|
1545
1571
|
return await submitCreative(api, {
|
|
1546
1572
|
path: "/v1/creatives/images",
|
|
@@ -1552,7 +1578,7 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
1552
1578
|
throw translateError(e);
|
|
1553
1579
|
}
|
|
1554
1580
|
});
|
|
1555
|
-
server.tool("generate_landing", 'Genera una landing page lista para Shopify. Devuelve un taskId.\n\nREQUERIDOS: images (array de 1-5 fotos), language, country.\n\nANTES de invocar, PREG\xDANTALE al usuario por price, offer y paymentMethods 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 esos campos, om\xEDtelos.\n\nOTROS OPCIONALES (puedes invocar sin ellos y dejar que la IA elija defaults): salesAngle, buyerPersona, isCombo.', LandingInput.shape, async (input) => {
|
|
1581
|
+
server.tool("generate_landing", 'Genera una landing page lista para Shopify. Devuelve un taskId.\n\nREQUERIDOS: images (array de 1-5 fotos), language, country.\n\nANTES de invocar, PREG\xDANTALE al usuario por price, offer y paymentMethods 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 esos campos, om\xEDtelos.\n\nOTROS OPCIONALES (puedes invocar sin ellos y dejar que la IA elija defaults): salesAngle, buyerPersona, isCombo, creativeDirection.', LandingInput.shape, async (input) => {
|
|
1556
1582
|
try {
|
|
1557
1583
|
return await submitCreative(api, {
|
|
1558
1584
|
path: "/v1/creatives/landing",
|
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.
|
|
113
|
+
var MCP_VERSION = "1.6.0";
|
|
114
114
|
|
|
115
115
|
// src/tools/tasks.ts
|
|
116
116
|
import { z as z2 } from "zod";
|
|
@@ -1142,7 +1142,8 @@ var CreateProductTaskInput = z2.object({
|
|
|
1142
1142
|
"image"
|
|
1143
1143
|
])).min(2).max(3).optional().describe('Opcional. Qu\xE9 outputs generar. Default: todos ["landing","video","image"]. Para 2 outputs: ["landing","image"] (sin video), ["video","image"] (sin landing), ["landing","video"] (sin imagen). Para 1 solo output, usa generate_landing / generate_video_creative / generate_image_creatives.'),
|
|
1144
1144
|
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."),
|
|
1145
|
-
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.")
|
|
1145
|
+
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."),
|
|
1146
|
+
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.')
|
|
1146
1147
|
}).strict();
|
|
1147
1148
|
var GetTaskStatusInput = z2.object({
|
|
1148
1149
|
taskId: z2.string().min(1),
|
|
@@ -1171,7 +1172,18 @@ var PendingActionPayload = z2.discriminatedUnion("kind", [
|
|
|
1171
1172
|
z2.object({
|
|
1172
1173
|
kind: z2.literal("product_reference_confirmation"),
|
|
1173
1174
|
userChoice: ProductReferenceUserChoiceEnum,
|
|
1174
|
-
confirmedProductNames: z2.array(z2.string().min(1).max(120)).min(2).max(8).optional()
|
|
1175
|
+
confirmedProductNames: z2.array(z2.string().min(1).max(120)).min(2).max(8).optional(),
|
|
1176
|
+
// SCOPED multi-scene opt-in — the ONLY way STANDARD ("Multiescena") is
|
|
1177
|
+
// reachable via MCP. Valid ONLY together with userChoice='variants' (same
|
|
1178
|
+
// product in several colors): renders one shot per color and stitches them,
|
|
1179
|
+
// so every variant color is shown off. Ignored for any other userChoice.
|
|
1180
|
+
// We deliberately do NOT expose the raw `standard` tier on the MCP video
|
|
1181
|
+
// knobs (VideoTierEnum stays {basic}) because a general tier dial with no
|
|
1182
|
+
// cost surface caused a prod over-spend incident (2026-07-04); this flag is
|
|
1183
|
+
// safe because multi-scene IS the intended render for a color variant set,
|
|
1184
|
+
// not a blind quality upsell. Maps to `videoTier: 'standard'` on the
|
|
1185
|
+
// confirm call; the backend re-gates it (variants + variant_set only).
|
|
1186
|
+
useMultiSceneShowcase: z2.boolean().optional().describe('Opcional (SOLO con userChoice="variants"). true = render Multiescena: una toma por color, mostrando todas las variantes, hasta 30s. Se factura POR ESCENA (m\xE1s caro que el render normal de una sola toma) \u2014 AV\xCDSALE del mayor costo al usuario antes de activarlo. Omitir = render est\xE1ndar de una sola toma. Se ignora si userChoice no es "variants".')
|
|
1175
1187
|
}),
|
|
1176
1188
|
z2.object({
|
|
1177
1189
|
kind: z2.literal("angle_review_approval"),
|
|
@@ -1189,6 +1201,10 @@ var ResolvePendingActionInput = z2.object({
|
|
|
1189
1201
|
taskId: z2.string().min(1),
|
|
1190
1202
|
action: PendingActionPayload
|
|
1191
1203
|
}).strict();
|
|
1204
|
+
function resolveVariantConfirmVideoTier(action) {
|
|
1205
|
+
return action.useMultiSceneShowcase && action.userChoice === "variants" ? "standard" : void 0;
|
|
1206
|
+
}
|
|
1207
|
+
__name(resolveVariantConfirmVideoTier, "resolveVariantConfirmVideoTier");
|
|
1192
1208
|
var StatusForResolveSchema = z2.object({
|
|
1193
1209
|
pendingUserAction: z2.object({
|
|
1194
1210
|
kind: z2.string(),
|
|
@@ -1219,7 +1235,7 @@ function formatOutputList(outputs) {
|
|
|
1219
1235
|
}
|
|
1220
1236
|
__name(formatOutputList, "formatOutputList");
|
|
1221
1237
|
function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http" } = {}) {
|
|
1222
|
-
server.tool("create_product_task", 'Crea una tarea de producto desde una o varias fotos. Por defecto genera los 3 outputs (landing + video + image creatives) EN UN MISMO TASK. Devuelve un taskId.\n\nINPUTS REQUERIDOS: images (1-5 fotos del MISMO producto; la primera es la hero), language, country.\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 del chat: preg\xFAntale expl\xEDcitamente, incluso si el usuario est\xE1 en LATAM. Si te dice "t\xFA elige" para price/offer, om\xEDtelos.\n\nANTES de invocar, DILE CLARAMENTE al usuario qu\xE9 outputs vas a generar y que `create_product_task` los genera juntos en un mismo task. Ejemplo: "Voy a generar landing + video en un mismo task; esto consume cr\xE9ditos de ambos outputs". Si el usuario s\xF3lo quiere un output, usa generate_landing / generate_video_creative / generate_image_creatives en vez de este tool.\n\nSi el usuario tiene varias fotos del MISMO producto (frente, lateral, detalle, uso), incl\xFAyelas todas en images[] hasta 5. Si tiene fotos de productos DIFERENTES, son llamadas separadas (una tarea por producto).\n\nOTROS OPCIONALES (puedes invocar sin ellos y dejar que la IA elija defaults sensatos):\n- videoPreset (tipo/narrativa del video \u2014 \xFAnico campo de estilo)\n- multiAngleVideos (booleano)\n- angleNarratives (override per-angle de la narrativa, avanzado)\n- outputs (subconjunto de {landing,video,image}); omitido = ["landing","video","image"] en un mismo task\n- productUrl\n\nGUION: para revisar/editar el guion del video antes de renderizar, usa `generate_video_creative` con `pauseForVideoScriptReview: true` en vez de este tool. Despu\xE9s de la pausa: get_video_script para leer, approve_video_script para aprobar con ediciones.\n\nUsa SOLO valores declarados en los enums. Si el usuario pide algo no soportado (ej. "estilo anime", "modelo X"), p\xEDdele una opci\xF3n v\xE1lida del enum correspondiente.', CreateProductTaskInput.shape, async (input) => {
|
|
1238
|
+
server.tool("create_product_task", 'Crea una tarea de producto desde una o varias fotos. Por defecto genera los 3 outputs (landing + video + image creatives) EN UN MISMO TASK. Devuelve un taskId.\n\nINPUTS REQUERIDOS: images (1-5 fotos del MISMO producto; la primera es la hero), language, country.\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 del chat: preg\xFAntale expl\xEDcitamente, incluso si el usuario est\xE1 en LATAM. Si te dice "t\xFA elige" para price/offer, om\xEDtelos.\n\nANTES de invocar, DILE CLARAMENTE al usuario qu\xE9 outputs vas a generar y que `create_product_task` los genera juntos en un mismo task. Ejemplo: "Voy a generar landing + video en un mismo task; esto consume cr\xE9ditos de ambos outputs". Si el usuario s\xF3lo quiere un output, usa generate_landing / generate_video_creative / generate_image_creatives en vez de este tool.\n\nSi el usuario tiene varias fotos del MISMO producto (frente, lateral, detalle, uso), incl\xFAyelas todas en images[] hasta 5. Si tiene fotos de productos DIFERENTES, son llamadas separadas (una tarea por producto).\n\nOTROS OPCIONALES (puedes invocar sin ellos y dejar que la IA elija defaults sensatos):\n- videoPreset (tipo/narrativa del video \u2014 \xFAnico campo de estilo)\n- multiAngleVideos (booleano)\n- angleNarratives (override per-angle de la narrativa, avanzado)\n- outputs (subconjunto de {landing,video,image}); omitido = ["landing","video","image"] en un mismo task\n- productUrl\n- creativeDirection (direcci\xF3n creativa para im\xE1genes y landing; el estilo del video se dirige con scriptNotes)\n\nGUION: para revisar/editar el guion del video antes de renderizar, usa `generate_video_creative` con `pauseForVideoScriptReview: true` en vez de este tool. Despu\xE9s de la pausa: get_video_script para leer, approve_video_script para aprobar con ediciones.\n\nUsa SOLO valores declarados en los enums. Si el usuario pide algo no soportado (ej. "estilo anime", "modelo X"), p\xEDdele una opci\xF3n v\xE1lida del enum correspondiente.', CreateProductTaskInput.shape, async (input) => {
|
|
1223
1239
|
try {
|
|
1224
1240
|
const imgs = await resolveImages(input.images, {
|
|
1225
1241
|
transport
|
|
@@ -1248,6 +1264,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
1248
1264
|
"options.selectedOutputs": input.outputs,
|
|
1249
1265
|
"options.competitorScanEnabled": input.competitorScanEnabled,
|
|
1250
1266
|
"options.brandId": input.brandId,
|
|
1267
|
+
"options.creativeDirection": input.creativeDirection,
|
|
1251
1268
|
url: input.productUrl
|
|
1252
1269
|
});
|
|
1253
1270
|
const r = await api.postMultipart("/v1/ingest", {
|
|
@@ -1322,7 +1339,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
1322
1339
|
throw translateError(e);
|
|
1323
1340
|
}
|
|
1324
1341
|
});
|
|
1325
|
-
server.tool("resolve_pending_action", 'Resuelve una acci\xF3n que el pipeline est\xE1 esperando del usuario. La llamas cuando `get_task_status` devuelve `pendingUserAction` (sin esto la tarea NUNCA avanza por m\xE1s que esperes). El `action.kind` que mandas DEBE coincidir con el `pendingUserAction.kind` que viste en el status \u2014 es el contrato de simetr\xEDa que mantiene este tool como \xFAnica superficie de resoluci\xF3n de gates.\n\nKINDS soportados hoy:\n\n\u2022 `product_reference_confirmation` \u2014 el clasificador no est\xE1 seguro si las fotos son variantes del mismo producto o productos distintos. Payload: `userChoice` \u2208 { "same_product" (mismo producto, fotos son s\xF3lo \xE1ngulos distintos), "variants" (mismo producto, distintos colores/tallas/sabores), "combo_distinct" (son 2 productos distintos vendidos juntos como combo \u2014 marca la tarea como combo), "different_products" (productos distintos y el usuario quiere generar s\xF3lo uno \u2014 el backend bloquea hasta que selecciones im\xE1genes de un solo producto; normalmente NO es la respuesta correcta para destrabar) }. PREG\xDANTALE al usuario antes de invocar \u2014 `pendingUserAction.targetProductLabel` y `reason` te dan contexto para formularle la pregunta en lenguaje natural. Usa SOLO valores presentes en `pendingUserAction.options`. Si el status trae `proposedProductNames` (lista propuesta de nombres), el combo es INCIERTO: mu\xE9strale los nombres al usuario y, si confirma que son productos distintos, manda `userChoice: "combo_distinct"` JUNTO CON `confirmedProductNames` (la lista confirmada o editada, 2-8 nombres) para separarlo en productos distintos; si el usuario dice que es un solo producto, manda `userChoice: "same_product"` (sin `confirmedProductNames`).\n\n\u2022 `angle_review_approval` \u2014 el usuario opto por `pauseForAngleReview: true` al crear el creativo (en `generate_image_creatives`, `generate_video_creative` o `generate_landing`). La IA ya gener\xF3 el \xE1ngulo + pain points + benefits + features y la tarea est\xE1 pausada esperando que el usuario apruebe o edite antes de renderizar. Lee `customAngleDraft`, `painPointsDraft`, `benefitsDraft`, `featuresDraft` del `pendingUserAction` para mostr\xE1rselos al usuario en lenguaje natural ("la IA propuso este \xE1ngulo: ... \xBFlo apruebas o cambias algo?"). Payload (TODOS opcionales \u2014 sin nada = aprobar tal cual; con campos = aprobar con ediciones): `customAngle` (override del \xE1ngulo), `painPoints`, `benefits`, `features` (array de 1-10 bullets). Llamar sin ning\xFAn campo es v\xE1lido y aprueba el draft sin cambios.\n\nFuturos kinds (no implementados a\xFAn) se agregar\xE1n como nuevas variantes del `action` discriminated union \u2014 el flujo "lee kind del status \u2192 manda mismo kind aqu\xED" sigue igual.', ResolvePendingActionInput.shape, async (input) => {
|
|
1342
|
+
server.tool("resolve_pending_action", 'Resuelve una acci\xF3n que el pipeline est\xE1 esperando del usuario. La llamas cuando `get_task_status` devuelve `pendingUserAction` (sin esto la tarea NUNCA avanza por m\xE1s que esperes). El `action.kind` que mandas DEBE coincidir con el `pendingUserAction.kind` que viste en el status \u2014 es el contrato de simetr\xEDa que mantiene este tool como \xFAnica superficie de resoluci\xF3n de gates.\n\nKINDS soportados hoy:\n\n\u2022 `product_reference_confirmation` \u2014 el clasificador no est\xE1 seguro si las fotos son variantes del mismo producto o productos distintos. Payload: `userChoice` \u2208 { "same_product" (mismo producto, fotos son s\xF3lo \xE1ngulos distintos), "variants" (mismo producto, distintos colores/tallas/sabores), "combo_distinct" (son 2 productos distintos vendidos juntos como combo \u2014 marca la tarea como combo), "different_products" (productos distintos y el usuario quiere generar s\xF3lo uno \u2014 el backend bloquea hasta que selecciones im\xE1genes de un solo producto; normalmente NO es la respuesta correcta para destrabar) }. PREG\xDANTALE al usuario antes de invocar \u2014 `pendingUserAction.targetProductLabel` y `reason` te dan contexto para formularle la pregunta en lenguaje natural. Usa SOLO valores presentes en `pendingUserAction.options`. Si el status trae `proposedProductNames` (lista propuesta de nombres), el combo es INCIERTO: mu\xE9strale los nombres al usuario y, si confirma que son productos distintos, manda `userChoice: "combo_distinct"` JUNTO CON `confirmedProductNames` (la lista confirmada o editada, 2-8 nombres) para separarlo en productos distintos; si el usuario dice que es un solo producto, manda `userChoice: "same_product"` (sin `confirmedProductNames`). Cuando el producto son VARIANTES DE COLOR (mismo producto, distintos colores) puedes ofrecer un video "Multiescena" que muestra una toma por color: manda `userChoice: "variants"` JUNTO CON `useMultiSceneShowcase: true`. AVISA al usuario que ese modo se factura por escena (m\xE1s caro que el render normal de una sola toma) antes de activarlo; si prefiere el render normal, manda solo `userChoice: "variants"` sin el flag.\n\n\u2022 `angle_review_approval` \u2014 el usuario opto por `pauseForAngleReview: true` al crear el creativo (en `generate_image_creatives`, `generate_video_creative` o `generate_landing`). La IA ya gener\xF3 el \xE1ngulo + pain points + benefits + features y la tarea est\xE1 pausada esperando que el usuario apruebe o edite antes de renderizar. Lee `customAngleDraft`, `painPointsDraft`, `benefitsDraft`, `featuresDraft` del `pendingUserAction` para mostr\xE1rselos al usuario en lenguaje natural ("la IA propuso este \xE1ngulo: ... \xBFlo apruebas o cambias algo?"). Payload (TODOS opcionales \u2014 sin nada = aprobar tal cual; con campos = aprobar con ediciones): `customAngle` (override del \xE1ngulo), `painPoints`, `benefits`, `features` (array de 1-10 bullets). Llamar sin ning\xFAn campo es v\xE1lido y aprueba el draft sin cambios.\n\nFuturos kinds (no implementados a\xFAn) se agregar\xE1n como nuevas variantes del `action` discriminated union \u2014 el flujo "lee kind del status \u2192 manda mismo kind aqu\xED" sigue igual.', ResolvePendingActionInput.shape, async (input) => {
|
|
1326
1343
|
try {
|
|
1327
1344
|
const raw = await api.getJson(`/v1/status/${encodeURIComponent(input.taskId)}`);
|
|
1328
1345
|
const status = StatusForResolveSchema.parse(raw);
|
|
@@ -1351,6 +1368,12 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
|
|
|
1351
1368
|
},
|
|
1352
1369
|
...input.action.confirmedProductNames ? {
|
|
1353
1370
|
confirmedProductNames: input.action.confirmedProductNames
|
|
1371
|
+
} : {},
|
|
1372
|
+
// Multi-scene color showcase → upgrade to STANDARD. Gated to the
|
|
1373
|
+
// variants choice client-side so the wire body stays coherent; the
|
|
1374
|
+
// backend re-gates on variant_set grouping.
|
|
1375
|
+
...resolveVariantConfirmVideoTier(input.action) === "standard" ? {
|
|
1376
|
+
videoTier: "standard"
|
|
1354
1377
|
} : {}
|
|
1355
1378
|
});
|
|
1356
1379
|
return asText(r);
|
|
@@ -1397,7 +1420,8 @@ var CreativeRefs = {
|
|
|
1397
1420
|
buyerPersona: z3.string().optional().describe('Opcional. Descripci\xF3n del comprador objetivo. Ej. "Mujeres 35-50, profesionales urbanas". Si se omite, la IA infiere desde el \xE1ngulo y producto.'),
|
|
1398
1421
|
isCombo: z3.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
|
|
1399
1422
|
competitorScanEnabled: z3.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."),
|
|
1400
|
-
brandId: z3.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 el creativo.")
|
|
1423
|
+
brandId: z3.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 el creativo."),
|
|
1424
|
+
creativeDirection: z3.string().max(500).optional().describe('Opcional. Direcci\xF3n creativa en texto libre (colores, mood, estilo). M\xC1XIMA PRIORIDAD: anula marca, colores y estilo por defecto. Aplica SOLO a im\xE1genes y landing. Ej. im\xE1genes: "amarillo, azul y rojo, minimalista"; landing: "elegante, paleta fr\xEDa". Para video usa scriptNotes (ambiente/escenario). M\xE1x 500 caracteres.')
|
|
1401
1425
|
};
|
|
1402
1426
|
var VideoInput = z3.object({
|
|
1403
1427
|
images: CreativeRefs.images,
|
|
@@ -1454,7 +1478,8 @@ var ImageInput = z3.object({
|
|
|
1454
1478
|
productLabel: CreativeRefs.productLabel,
|
|
1455
1479
|
isCombo: CreativeRefs.isCombo,
|
|
1456
1480
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
1457
|
-
brandId: CreativeRefs.brandId
|
|
1481
|
+
brandId: CreativeRefs.brandId,
|
|
1482
|
+
creativeDirection: CreativeRefs.creativeDirection
|
|
1458
1483
|
});
|
|
1459
1484
|
var LandingInput = z3.object({
|
|
1460
1485
|
images: CreativeRefs.images,
|
|
@@ -1469,10 +1494,11 @@ var LandingInput = z3.object({
|
|
|
1469
1494
|
paymentMethods: z3.string().optional().describe('Opcional. M\xE9todos de pago a mostrar (lista libre). Ej. "Tarjeta, PSE, Nequi, Contraentrega". Si se omite, la IA usa los m\xE1s comunes del pa\xEDs.'),
|
|
1470
1495
|
isCombo: CreativeRefs.isCombo,
|
|
1471
1496
|
competitorScanEnabled: CreativeRefs.competitorScanEnabled,
|
|
1472
|
-
brandId: CreativeRefs.brandId
|
|
1497
|
+
brandId: CreativeRefs.brandId,
|
|
1498
|
+
creativeDirection: CreativeRefs.creativeDirection
|
|
1473
1499
|
});
|
|
1474
1500
|
function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
1475
|
-
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.\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) => {
|
|
1501
|
+
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) => {
|
|
1476
1502
|
try {
|
|
1477
1503
|
const resolved = resolveVideoPreset(input.videoPreset);
|
|
1478
1504
|
const wireTier = resolveVideoTier(input.videoTier);
|
|
@@ -1496,7 +1522,7 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
1496
1522
|
throw translateError(e);
|
|
1497
1523
|
}
|
|
1498
1524
|
});
|
|
1499
|
-
server.tool("generate_image_creatives", 'Genera N variantes de imagen creativa (ads est\xE1ticos). Devuelve un taskId.\n\nREQUERIDOS: images (array de 1-5 fotos), productTitle, primaryAngle, language, country.\n\nANTES de invocar, PREG\xDANTALE al usuario:\n- price y offer (se renderizan en la imagen \u2014 nunca los asumas; si dice "t\xFA elige", om\xEDtelos).\n- compareAtPrice si hay descuento (ej. "$129.000" para tachar).\n- creativeArchetype y visualTreatment si quiere un look espec\xEDfico (performance vs premium DTC). Si no le importa, om\xEDtelos.\n\nNO defaultes country/language desde el contexto: preg\xFAntale expl\xEDcitamente.\n\nOTROS OPCIONALES: buyerPersona, nVariants (default 3), imageModel, imageStyle, styleMix, aspectRatio (default 4:5), generateAdCopy, pauseForAngleReview, features, brandColors, isCombo.', ImageInput.shape, async (input) => {
|
|
1525
|
+
server.tool("generate_image_creatives", 'Genera N variantes de imagen creativa (ads est\xE1ticos). Devuelve un taskId.\n\nREQUERIDOS: images (array de 1-5 fotos), productTitle, primaryAngle, language, country.\n\nANTES de invocar, PREG\xDANTALE al usuario:\n- price y offer (se renderizan en la imagen \u2014 nunca los asumas; si dice "t\xFA elige", om\xEDtelos).\n- compareAtPrice si hay descuento (ej. "$129.000" para tachar).\n- creativeArchetype y visualTreatment si quiere un look espec\xEDfico (performance vs premium DTC). Si no le importa, om\xEDtelos.\n\nNO defaultes country/language desde el contexto: preg\xFAntale expl\xEDcitamente.\n\nOTROS OPCIONALES: buyerPersona, nVariants (default 3), imageModel, imageStyle, styleMix, aspectRatio (default 4:5), generateAdCopy, pauseForAngleReview, features, brandColors, isCombo, creativeDirection.', ImageInput.shape, async (input) => {
|
|
1500
1526
|
try {
|
|
1501
1527
|
return await submitCreative(api, {
|
|
1502
1528
|
path: "/v1/creatives/images",
|
|
@@ -1508,7 +1534,7 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
1508
1534
|
throw translateError(e);
|
|
1509
1535
|
}
|
|
1510
1536
|
});
|
|
1511
|
-
server.tool("generate_landing", 'Genera una landing page lista para Shopify. Devuelve un taskId.\n\nREQUERIDOS: images (array de 1-5 fotos), language, country.\n\nANTES de invocar, PREG\xDANTALE al usuario por price, offer y paymentMethods 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 esos campos, om\xEDtelos.\n\nOTROS OPCIONALES (puedes invocar sin ellos y dejar que la IA elija defaults): salesAngle, buyerPersona, isCombo.', LandingInput.shape, async (input) => {
|
|
1537
|
+
server.tool("generate_landing", 'Genera una landing page lista para Shopify. Devuelve un taskId.\n\nREQUERIDOS: images (array de 1-5 fotos), language, country.\n\nANTES de invocar, PREG\xDANTALE al usuario por price, offer y paymentMethods 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 esos campos, om\xEDtelos.\n\nOTROS OPCIONALES (puedes invocar sin ellos y dejar que la IA elija defaults): salesAngle, buyerPersona, isCombo, creativeDirection.', LandingInput.shape, async (input) => {
|
|
1512
1538
|
try {
|
|
1513
1539
|
return await submitCreative(api, {
|
|
1514
1540
|
path: "/v1/creatives/landing",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@productmaker/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.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.",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"msw": "^2.4.0",
|
|
61
61
|
"tsup": "^8.3.0",
|
|
62
62
|
"tsx": "^4.16.0",
|
|
63
|
-
"typescript": "^
|
|
63
|
+
"typescript": "^7.0.2",
|
|
64
64
|
"vitest": "^2.1.9",
|
|
65
65
|
"@packages/types": "0.1.0"
|
|
66
66
|
},
|