@productmaker/mcp 0.15.2 → 0.15.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/http.js CHANGED
@@ -113,7 +113,7 @@ var ApiClient = class {
113
113
  };
114
114
 
115
115
  // src/index.ts
116
- var MCP_VERSION = "0.3.0";
116
+ var MCP_VERSION = "0.15.3";
117
117
 
118
118
  // src/tools/tasks.ts
119
119
  import { z as z2 } from "zod";
@@ -358,13 +358,13 @@ async function pollStatus(fetcher, waitSeconds, signal, opts = {}) {
358
358
  const effective = Math.min(Math.max(0, waitSeconds), maxWait);
359
359
  let snapshot = await fetcher();
360
360
  if (effective === 0) return snapshot;
361
- if (TERMINAL.has(snapshot.status) || snapshot.status === "awaiting_review") return snapshot;
361
+ if (TERMINAL.has(snapshot.status) || snapshot.status === "awaiting_review" || snapshot.pendingUserAction != null) return snapshot;
362
362
  const deadline = Date.now() + effective * 1e3;
363
363
  while (Date.now() < deadline) {
364
364
  if (signal?.aborted) throw new Error("aborted");
365
365
  await sleep(intervalMs, signal);
366
366
  const next = await fetcher();
367
- if (next.status !== snapshot.status || next.phase !== snapshot.phase || TERMINAL.has(next.status) || next.status === "awaiting_review") {
367
+ if (next.status !== snapshot.status || next.phase !== snapshot.phase || TERMINAL.has(next.status) || next.status === "awaiting_review" || next.pendingUserAction != null) {
368
368
  return next;
369
369
  }
370
370
  snapshot = next;
@@ -1240,8 +1240,25 @@ var StatusForResolveSchema = z2.object({
1240
1240
  featuresDraft: z2.array(z2.string()).optional()
1241
1241
  }).optional()
1242
1242
  }).passthrough();
1243
+ var DEFAULT_PRODUCT_TASK_OUTPUTS = [
1244
+ "landing",
1245
+ "video",
1246
+ "image"
1247
+ ];
1248
+ var PRODUCT_TASK_OUTPUT_LABELS = {
1249
+ landing: "landing",
1250
+ video: "video",
1251
+ image: "im\xE1genes creativas"
1252
+ };
1253
+ function formatOutputList(outputs) {
1254
+ const labels = outputs.map((output) => PRODUCT_TASK_OUTPUT_LABELS[output]);
1255
+ if (labels.length <= 1) return labels[0] ?? "";
1256
+ if (labels.length === 2) return `${labels[0]} + ${labels[1]}`;
1257
+ return `${labels.slice(0, -1).join(" + ")} + ${labels[labels.length - 1]}`;
1258
+ }
1259
+ __name(formatOutputList, "formatOutputList");
1243
1260
  function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http" } = {}) {
1244
- 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). 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\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- videoTier (estilo: basic="Realista"/standard="Multiescena")\n- multiAngleVideos (booleano)\n- angleNarratives (override per-angle de la narrativa, avanzado)\n- outputs (subconjunto de {landing,video,image})\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) => {
1261
+ 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- videoTier (estilo: basic="Realista"/standard="Multiescena")\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) => {
1245
1262
  try {
1246
1263
  const imgs = await resolveImages(input.images, {
1247
1264
  transport
@@ -1250,6 +1267,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
1250
1267
  ...img,
1251
1268
  filename: `image-${i}.png`
1252
1269
  }));
1270
+ const selectedOutputs = input.outputs ?? DEFAULT_PRODUCT_TASK_OUTPUTS;
1253
1271
  const resolved = resolveVideoPreset(input.videoPreset);
1254
1272
  const wireTier = resolveVideoTier(input.videoTier);
1255
1273
  const fields = flatten({
@@ -1278,13 +1296,16 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
1278
1296
  return asText({
1279
1297
  taskId: r.taskId,
1280
1298
  status: "queued",
1281
- estimatedSeconds: 300
1299
+ estimatedSeconds: 300,
1300
+ outputs: selectedOutputs,
1301
+ outputMode: "single_task",
1302
+ message: `Se est\xE1 generando ${formatOutputList(selectedOutputs)} en un mismo task.`
1282
1303
  });
1283
1304
  } catch (e) {
1284
1305
  throw translateError(e);
1285
1306
  }
1286
1307
  });
1287
- server.tool("get_task_status", 'Devuelve el estado y URLs de outputs de una tarea. Si waitSeconds>0, hace polling hasta que cambie la fase o llegue al timeout (max 120s). Cuando la tarea queda en `done`, la respuesta incluye un segundo bloque markdown con previews inline de las im\xE1genes, links descargables al video MP4 y un enlace a Product Maker para ver el resultado completo.\n\nIMPORTANTE \u2014 `pendingUserAction`: si est\xE1 presente, el pipeline est\xE1 PARADO esperando que el usuario decida algo. Mira el `kind` (hoy: `"product_reference_confirmation"` \u2014 el clasificador no est\xE1 seguro si las fotos son variantes del mismo producto o productos distintos), mu\xE9strale al usuario `targetProductLabel` + `reason` en lenguaje natural, recoge su respuesta, y llama `resolve_pending_action` con `action: { kind: <mismo kind>, ... }` (las `options` v\xE1lidas vienen en el mismo objeto). Sin esto la tarea NUNCA avanza por m\xE1s que esperes. Use this to check on a task progress; pass waitSeconds for a near-synchronous response.', GetTaskStatusInput.shape, async (input, extra) => {
1308
+ server.tool("get_task_status", 'Devuelve el estado y URLs de outputs de una tarea. Si waitSeconds>0, hace polling hasta que cambie la fase, aparezca `pendingUserAction`, o llegue al timeout (max 120s). Cuando la tarea queda en `done`, la respuesta incluye un segundo bloque markdown con previews inline de las im\xE1genes, links descargables al video MP4 y un enlace a Product Maker para ver el resultado completo.\n\nIMPORTANTE \u2014 `pendingUserAction`: si est\xE1 presente, el pipeline est\xE1 PARADO esperando que el usuario decida algo. Mira el `kind` y llama `resolve_pending_action` con `action: { kind: <mismo kind>, ... }`; sin esto la tarea NUNCA avanza por m\xE1s que esperes. Kinds actuales:\n- `product_reference_confirmation`: el clasificador no est\xE1 seguro si las fotos son variantes del mismo producto o productos distintos. Mu\xE9strale al usuario `targetProductLabel` + `reason`, recoge su respuesta, y usa SOLO valores presentes en `options`.\n- `angle_review_approval`: la IA ya gener\xF3 el \xE1ngulo/revisi\xF3n antes de renderizar. Mu\xE9strale `customAngleDraft`, `painPointsDraft`, `benefitsDraft`, `featuresDraft` si vienen; si no vienen, puedes pedir aprobaci\xF3n general y llamar `resolve_pending_action` con solo `{ kind: "angle_review_approval" }` para aprobar tal cual.\nUse this to check on a task progress; pass waitSeconds for a near-synchronous response.', GetTaskStatusInput.shape, async (input, extra) => {
1288
1309
  try {
1289
1310
  const fetcher = /* @__PURE__ */ __name(() => api.getJson(`/v1/status/${encodeURIComponent(input.taskId)}`), "fetcher");
1290
1311
  const signal = extra?.signal;
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 = "0.3.0";
113
+ var MCP_VERSION = "0.15.3";
114
114
 
115
115
  // src/tools/tasks.ts
116
116
  import { z as z2 } from "zod";
@@ -355,13 +355,13 @@ async function pollStatus(fetcher, waitSeconds, signal, opts = {}) {
355
355
  const effective = Math.min(Math.max(0, waitSeconds), maxWait);
356
356
  let snapshot = await fetcher();
357
357
  if (effective === 0) return snapshot;
358
- if (TERMINAL.has(snapshot.status) || snapshot.status === "awaiting_review") return snapshot;
358
+ if (TERMINAL.has(snapshot.status) || snapshot.status === "awaiting_review" || snapshot.pendingUserAction != null) return snapshot;
359
359
  const deadline = Date.now() + effective * 1e3;
360
360
  while (Date.now() < deadline) {
361
361
  if (signal?.aborted) throw new Error("aborted");
362
362
  await sleep(intervalMs, signal);
363
363
  const next = await fetcher();
364
- if (next.status !== snapshot.status || next.phase !== snapshot.phase || TERMINAL.has(next.status) || next.status === "awaiting_review") {
364
+ if (next.status !== snapshot.status || next.phase !== snapshot.phase || TERMINAL.has(next.status) || next.status === "awaiting_review" || next.pendingUserAction != null) {
365
365
  return next;
366
366
  }
367
367
  snapshot = next;
@@ -1196,8 +1196,25 @@ var StatusForResolveSchema = z2.object({
1196
1196
  featuresDraft: z2.array(z2.string()).optional()
1197
1197
  }).optional()
1198
1198
  }).passthrough();
1199
+ var DEFAULT_PRODUCT_TASK_OUTPUTS = [
1200
+ "landing",
1201
+ "video",
1202
+ "image"
1203
+ ];
1204
+ var PRODUCT_TASK_OUTPUT_LABELS = {
1205
+ landing: "landing",
1206
+ video: "video",
1207
+ image: "im\xE1genes creativas"
1208
+ };
1209
+ function formatOutputList(outputs) {
1210
+ const labels = outputs.map((output) => PRODUCT_TASK_OUTPUT_LABELS[output]);
1211
+ if (labels.length <= 1) return labels[0] ?? "";
1212
+ if (labels.length === 2) return `${labels[0]} + ${labels[1]}`;
1213
+ return `${labels.slice(0, -1).join(" + ")} + ${labels[labels.length - 1]}`;
1214
+ }
1215
+ __name(formatOutputList, "formatOutputList");
1199
1216
  function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http" } = {}) {
1200
- 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). 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\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- videoTier (estilo: basic="Realista"/standard="Multiescena")\n- multiAngleVideos (booleano)\n- angleNarratives (override per-angle de la narrativa, avanzado)\n- outputs (subconjunto de {landing,video,image})\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) => {
1217
+ 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- videoTier (estilo: basic="Realista"/standard="Multiescena")\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) => {
1201
1218
  try {
1202
1219
  const imgs = await resolveImages(input.images, {
1203
1220
  transport
@@ -1206,6 +1223,7 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
1206
1223
  ...img,
1207
1224
  filename: `image-${i}.png`
1208
1225
  }));
1226
+ const selectedOutputs = input.outputs ?? DEFAULT_PRODUCT_TASK_OUTPUTS;
1209
1227
  const resolved = resolveVideoPreset(input.videoPreset);
1210
1228
  const wireTier = resolveVideoTier(input.videoTier);
1211
1229
  const fields = flatten({
@@ -1234,13 +1252,16 @@ function registerTaskTools(server, api, { proxyBaseUrl = null, transport = "http
1234
1252
  return asText({
1235
1253
  taskId: r.taskId,
1236
1254
  status: "queued",
1237
- estimatedSeconds: 300
1255
+ estimatedSeconds: 300,
1256
+ outputs: selectedOutputs,
1257
+ outputMode: "single_task",
1258
+ message: `Se est\xE1 generando ${formatOutputList(selectedOutputs)} en un mismo task.`
1238
1259
  });
1239
1260
  } catch (e) {
1240
1261
  throw translateError(e);
1241
1262
  }
1242
1263
  });
1243
- server.tool("get_task_status", 'Devuelve el estado y URLs de outputs de una tarea. Si waitSeconds>0, hace polling hasta que cambie la fase o llegue al timeout (max 120s). Cuando la tarea queda en `done`, la respuesta incluye un segundo bloque markdown con previews inline de las im\xE1genes, links descargables al video MP4 y un enlace a Product Maker para ver el resultado completo.\n\nIMPORTANTE \u2014 `pendingUserAction`: si est\xE1 presente, el pipeline est\xE1 PARADO esperando que el usuario decida algo. Mira el `kind` (hoy: `"product_reference_confirmation"` \u2014 el clasificador no est\xE1 seguro si las fotos son variantes del mismo producto o productos distintos), mu\xE9strale al usuario `targetProductLabel` + `reason` en lenguaje natural, recoge su respuesta, y llama `resolve_pending_action` con `action: { kind: <mismo kind>, ... }` (las `options` v\xE1lidas vienen en el mismo objeto). Sin esto la tarea NUNCA avanza por m\xE1s que esperes. Use this to check on a task progress; pass waitSeconds for a near-synchronous response.', GetTaskStatusInput.shape, async (input, extra) => {
1264
+ server.tool("get_task_status", 'Devuelve el estado y URLs de outputs de una tarea. Si waitSeconds>0, hace polling hasta que cambie la fase, aparezca `pendingUserAction`, o llegue al timeout (max 120s). Cuando la tarea queda en `done`, la respuesta incluye un segundo bloque markdown con previews inline de las im\xE1genes, links descargables al video MP4 y un enlace a Product Maker para ver el resultado completo.\n\nIMPORTANTE \u2014 `pendingUserAction`: si est\xE1 presente, el pipeline est\xE1 PARADO esperando que el usuario decida algo. Mira el `kind` y llama `resolve_pending_action` con `action: { kind: <mismo kind>, ... }`; sin esto la tarea NUNCA avanza por m\xE1s que esperes. Kinds actuales:\n- `product_reference_confirmation`: el clasificador no est\xE1 seguro si las fotos son variantes del mismo producto o productos distintos. Mu\xE9strale al usuario `targetProductLabel` + `reason`, recoge su respuesta, y usa SOLO valores presentes en `options`.\n- `angle_review_approval`: la IA ya gener\xF3 el \xE1ngulo/revisi\xF3n antes de renderizar. Mu\xE9strale `customAngleDraft`, `painPointsDraft`, `benefitsDraft`, `featuresDraft` si vienen; si no vienen, puedes pedir aprobaci\xF3n general y llamar `resolve_pending_action` con solo `{ kind: "angle_review_approval" }` para aprobar tal cual.\nUse this to check on a task progress; pass waitSeconds for a near-synchronous response.', GetTaskStatusInput.shape, async (input, extra) => {
1244
1265
  try {
1245
1266
  const fetcher = /* @__PURE__ */ __name(() => api.getJson(`/v1/status/${encodeURIComponent(input.taskId)}`), "fetcher");
1246
1267
  const signal = extra?.signal;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@productmaker/mcp",
3
- "version": "0.15.2",
3
+ "version": "0.15.4",
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.",