@productmaker/mcp 2.1.0 → 2.2.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.
Files changed (3) hide show
  1. package/dist/http.js +120 -24
  2. package/dist/stdio.js +120 -24
  3. package/package.json +2 -2
package/dist/http.js CHANGED
@@ -153,7 +153,7 @@ var ApiClient = class {
153
153
  };
154
154
 
155
155
  // src/index.ts
156
- var MCP_VERSION = "2.1.0";
156
+ var MCP_VERSION = "2.2.0";
157
157
 
158
158
  // src/tools/tasks.ts
159
159
  import { z as z40 } from "zod";
@@ -1424,11 +1424,12 @@ var IngestOptions = z13.object({
1424
1424
  * shorter than the generated angle count leaves the remaining positions
1425
1425
  * auto-selected. Empty/omitted → all angles auto-select as before. */
1426
1426
  angleNarratives: z13.array(NarrativeStyle).max(3).optional(),
1427
- // 720p is the recommended default for every tier (flipped 2026-06-22 — grok provider
1428
- // unified per-second pricing, so 720p is only a small premium and worth the sharpness).
1427
+ // 480p is the recommended default for every tier (flipped back 2026-07-30 — the
1428
+ // 720p premium is real but small, and users on the entry plan were defaulting
1429
+ // into it while burning half a month of credits on one video).
1429
1430
  // This Zod default is what callers that omit the field (MCP/API) actually get,
1430
- // so it must agree with resolveSingleCallAwareResolution's unset fallback (720p).
1431
- videoResolution: VideoResolution.default("720p"),
1431
+ // so it must agree with resolveSingleCallAwareResolution's unset fallback (480p).
1432
+ videoResolution: VideoResolution.default("480p"),
1432
1433
  generateAdCopy: z13.boolean().default(true),
1433
1434
  generateAiMusic: z13.boolean().optional(),
1434
1435
  voiceId: VoiceId.optional(),
@@ -1543,9 +1544,9 @@ var IngestResumePayload = z13.object({
1543
1544
  videoModel: VideoModel.default("veo"),
1544
1545
  videoTier: VideoTier.optional(),
1545
1546
  narrativeStyle: NarrativeStyle.default("review"),
1546
- // 720p is the recommended default for every tier (flipped 2026-06-22) — keep in sync
1547
- // with IngestOptions.videoResolution and resolveSingleCallAwareResolution.
1548
- videoResolution: VideoResolution.default("720p"),
1547
+ // 480p is the recommended default for every tier (flipped back 2026-07-30) — keep in
1548
+ // sync with IngestOptions.videoResolution and resolveSingleCallAwareResolution.
1549
+ videoResolution: VideoResolution.default("480p"),
1549
1550
  pauseForVideoScriptReview: z13.boolean().optional(),
1550
1551
  generateAiMusic: z13.boolean().optional(),
1551
1552
  scriptNotes: z13.string().max(2e3).optional(),
@@ -5004,16 +5005,37 @@ var DEFAULT_PRICING = {
5004
5005
  ourCostCents: 0.15,
5005
5006
  userPriceCents: 0.3
5006
5007
  },
5007
- // Video QA — independent anatomy arm-scanner (Gemini 3 Flash Preview, one call per
5008
- // sampled frame, up to ARM_SCAN_MAX_FRAMES per clip). Billed PER FRAME (units: 1 per
5009
- // call), so this rate is per frame, not per clip. Sized above the 30d prod average
5010
- // (10.303 entries at $6.81 our => 0.066¢/frame) so the fallback path — used when
5011
- // OpenRouter omits usage.cost — cannot under-charge. Before this key existed those
5012
- // fallbacks hit the `No pricing rule found; skipping` branch and went UNBILLED.
5008
+ // Video QA — anatomy. THREE keys, and they do NOT share a unit. Getting this wrong
5009
+ // over-charges by ~42x, which is exactly what a first pass at the 2026-07-30 change did.
5010
+ //
5011
+ // (a) The arm-scan's per-FRAME counting sweep. STILL LIVE on the whole-video concat path
5012
+ // (`scanClipArms` at apps/api/src/workers/core/video-concat-core.ts), which bills
5013
+ // units:1 PER SAMPLED FRAME, up to ARM_SCAN_FRAME_CEILING. Rate must stay per-frame.
5014
+ // Sized above the 30d prod average (10303 rows => 0.132c user/frame).
5013
5015
  "openrouter:video-anatomy-arm-scan": {
5014
5016
  ourCostCents: 0.1,
5015
5017
  userPriceCents: 0.2
5016
5018
  },
5019
+ // (b) The frame JUDGE that replaced the sweep on the `analyzeVideoClipDJ` path: ALL frames
5020
+ // in ONE multi-image call, so a unit is one call. MEASURED, not estimated —
5021
+ // usage.cost on a real 16-frame judgement at 768px is $0.0417 => 4.17c our. The table is
5022
+ // the FALLBACK used when OpenRouter omits usage.cost, so it must sit at or above that.
5023
+ // Billed PER FRAME (units = frames in the call), like the sweep and for the same reason:
5024
+ // the cost scales with IMAGES and QA_FRAME_JUDGE_FRAMES is env-overridable, so a per-CALL
5025
+ // unit would freeze the fallback charge and the credit-gate quote at the cap the rate
5026
+ // happened to be measured at. Measured $0.0417 over 16 frames => 0.26c/frame; priced at
5027
+ // 0.32c for headroom (n=1 on one clip, and the 1.9k reasoning-token share moves with
5028
+ // QA_DETECTOR_EFFORT). Re-derive from 30d of BillingEntry once this key has volume.
5029
+ "openrouter:video-qa-anatomy-frame-judge": {
5030
+ ourCostCents: 0.32,
5031
+ userPriceCents: 0.64
5032
+ },
5033
+ // (c) Tracing confirmation of a frames-only finding: 3 stills + a 3s 480p slice, one call,
5034
+ // no 16-image prompt — materially cheaper than the judgement it verifies.
5035
+ "openrouter:video-qa-anatomy-tracing-confirm": {
5036
+ ourCostCents: 1.5,
5037
+ userPriceCents: 3
5038
+ },
5017
5039
  // Physics confirmation — skeptical second opinion on D+J floating-object flags
5018
5040
  // (Gemini 3 Flash Preview, ~3 frames per flagged moment, 1 call). Aligned with other vision-QA keys.
5019
5041
  "openrouter:video-physics-confirmation": {
@@ -6566,6 +6588,36 @@ var DropshippingProjectStatusSchema = z37.enum([
6566
6588
  "completed",
6567
6589
  "archived"
6568
6590
  ]);
6591
+ var DROPSHIPPING_LAUNCH_REWARD_CAMPAIGN = "dropshipping-launch-v1";
6592
+ var DropshippingRewardMilestoneSchema = z37.enum([
6593
+ "product_selected",
6594
+ "costing_saved",
6595
+ "product_media_ready",
6596
+ "creatives_completed",
6597
+ "campaign_created"
6598
+ ]);
6599
+ var DROPSHIPPING_LAUNCH_REWARD_CREDITS = {
6600
+ product_selected: 50,
6601
+ costing_saved: 50,
6602
+ product_media_ready: 50,
6603
+ creatives_completed: 150,
6604
+ campaign_created: 200
6605
+ };
6606
+ var DROPSHIPPING_LAUNCH_REWARD_TOTAL_CREDITS = Object.values(DROPSHIPPING_LAUNCH_REWARD_CREDITS).reduce((sum, credits) => sum + credits, 0);
6607
+ var DropshippingProjectRewardMilestoneStateSchema = z37.object({
6608
+ milestone: DropshippingRewardMilestoneSchema,
6609
+ credits: z37.number().int().positive(),
6610
+ available: z37.boolean().optional(),
6611
+ earned: z37.boolean(),
6612
+ grantId: z37.string().uuid().nullable(),
6613
+ grantedAt: z37.string().datetime().nullable()
6614
+ });
6615
+ var DropshippingProjectRewardProgressSchema = z37.object({
6616
+ campaignKey: z37.literal(DROPSHIPPING_LAUNCH_REWARD_CAMPAIGN),
6617
+ totalCredits: z37.number().int().positive(),
6618
+ earnedCredits: z37.number().int().nonnegative(),
6619
+ milestones: z37.array(DropshippingProjectRewardMilestoneStateSchema).length(DropshippingRewardMilestoneSchema.options.length)
6620
+ });
6569
6621
  var DropshippingProjectPhaseSchema = z37.enum([
6570
6622
  "research",
6571
6623
  "costing",
@@ -6723,6 +6775,7 @@ var DropshippingProjectResponseSchema = z37.object({
6723
6775
  version: z37.number().int().positive(),
6724
6776
  phase: DropshippingProjectPhaseSchema,
6725
6777
  nextAction: DropshippingProjectNextActionSchema.nullable(),
6778
+ rewards: DropshippingProjectRewardProgressSchema,
6726
6779
  createdAt: z37.string().datetime(),
6727
6780
  updatedAt: z37.string().datetime()
6728
6781
  });
@@ -6789,7 +6842,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
6789
6842
  {
6790
6843
  name: "create_product_task",
6791
6844
  confirmation: "credit_consuming",
6792
- schemaHash: "828ee643bb3a05a63c6e63730459d35ce1621cbd559bbc76a6c5fcec9a45654d"
6845
+ schemaHash: "5b19f7c6dfdc55b0c472df045e24fe73aa134624a141b63342bfbd3bbb0d043b"
6793
6846
  },
6794
6847
  {
6795
6848
  name: "edit_task_draft",
@@ -6814,17 +6867,17 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
6814
6867
  {
6815
6868
  name: "generate_image_creatives",
6816
6869
  confirmation: "credit_consuming",
6817
- schemaHash: "fb6afcf8571887906340fa0b8dd17196d2814e480b734ad5d374e8810958152e"
6870
+ schemaHash: "82b520e8001e9c57ab0a89d9d66ea5dfc92e6306eed302edfe1005cee5025fbf"
6818
6871
  },
6819
6872
  {
6820
6873
  name: "generate_landing",
6821
6874
  confirmation: "credit_consuming",
6822
- schemaHash: "539922b8fe611c73a7349b1c3f92632fe21f161d7874a0ef2172abfec5834759"
6875
+ schemaHash: "23f0c56dc719235e51d16fd61a9703b64fa6d300356ffbf6b21d703324b0bb18"
6823
6876
  },
6824
6877
  {
6825
6878
  name: "generate_video_creative",
6826
6879
  confirmation: "credit_consuming",
6827
- schemaHash: "94e90d60ad5d68538c31d880a635c5be69d6d204fd2119139735af83ebf0b94a"
6880
+ schemaHash: "79d3ec9310885b482d62c94a27e9c039833c0619d9332a593fe7d32a60ae1388"
6828
6881
  },
6829
6882
  {
6830
6883
  name: "get_meta_campaign_status",
@@ -8107,7 +8160,7 @@ var CreateProductTaskInput = z40.object({
8107
8160
  ]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
8108
8161
  pauseForAngleReview: z40.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo para que el usuario lo confirme o edite antes del render. \xDAsalo si quiere decidir 15/30s cuando aparezca un winner. Default false."),
8109
8162
  videoTier: VideoTierEnum.optional().describe('Opcional. Estilo del video. "basic" = "Realista" para 15s; al pedir durationSeconds=30 el sistema selecciona internamente Multiescena. No se expone el tier interno directamente.'),
8110
- 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).'),
8163
+ 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, entre 3% y 15% m\xE1s caro seg\xFAn formato y tier. Si el usuario no menciona resoluci\xF3n, OMITE este campo (el backend usa 480p).'),
8111
8164
  multiAngleVideos: z40.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."),
8112
8165
  angleNarratives: PublicNarrativeStyleEnum.array().max(3).optional().describe('Opcional. Narrativa POR \xC1NGULO (posicional). Valores actuales: "review", "ugc_how_to", "ugc_unboxing_chaos", "product_showcase", "wearable_showcase", "cinematic", "wild_card", "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.'),
8113
8166
  outputs: z40.array(z40.enum([
@@ -8582,6 +8635,7 @@ var CreativeRefs = {
8582
8635
  isCombo: z41.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
8583
8636
  competitorScanEnabled: z41.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."),
8584
8637
  brandId: z41.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."),
8638
+ projectId: z41.string().uuid().optional().describe("Opcional. ID de un proyecto guiado de dropshipping activo del usuario. Enlaza esta generaci\xF3n con su progreso y recompensas."),
8585
8639
  creativeDirection: z41.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.')
8586
8640
  };
8587
8641
  var VideoInput = z41.object({
@@ -8601,7 +8655,7 @@ var VideoInput = z41.object({
8601
8655
  ]).default(15).describe("Opcional. Duraci\xF3n objetivo: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
8602
8656
  videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Los presets funcionan a 15s y 30s. Opciones: "auto", "ugc_dynamic" (review con experiencia y prueba), "ugc_how_to" (tutorial con pasos y resultado visible), "ugc_unboxing_chaos" (apertura y primera prueba), "product_showcase" (producto ejecutando una funci\xF3n real), "wearable_showcase" (prenda o accesorio modelado), "cinematic" (TV spot con receta cinematogr\xE1fica), "wild_card" (una regla visual experimental original), "pixar_problem" (problema animado 3D), "pixar_sassy" (producto que habla).'),
8603
8657
  videoTier: VideoTierEnum.optional().describe('Opcional. Estilo del video. "basic" = "Realista"; para durationSeconds=30 el sistema selecciona internamente Multiescena. No se expone el tier interno directamente.'),
8604
- 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).'),
8658
+ 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, entre 3% y 15% m\xE1s caro seg\xFAn formato y tier. Si el usuario no menciona resoluci\xF3n, OMITE este campo (el backend usa 480p).'),
8605
8659
  features: z41.array(z41.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
8606
8660
  price: CreativeRefs.price,
8607
8661
  offer: CreativeRefs.offer,
@@ -8612,7 +8666,8 @@ var VideoInput = z41.object({
8612
8666
  pauseForVideoScriptReview: z41.boolean().optional().describe("Opcional. Si true, la tarea pausa despu\xE9s de generar el guion y antes de renderizar. \xDAsalo cuando el usuario quiera editar el guion antes de gastar cr\xE9ditos en el video. Tras la pausa: get_video_script para leerlo, approve_video_script para aprobar (con o sin ediciones). Default false."),
8613
8667
  pauseForAngleReview: z41.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo para que el usuario lo confirme o edite antes del render. Default false."),
8614
8668
  competitorScanEnabled: CreativeRefs.competitorScanEnabled,
8615
- brandId: CreativeRefs.brandId
8669
+ brandId: CreativeRefs.brandId,
8670
+ projectId: CreativeRefs.projectId
8616
8671
  }).strict();
8617
8672
  var FreeVideoQualityEnum = z41.enum([
8618
8673
  "economical",
@@ -8676,6 +8731,7 @@ var ImageInput = z41.object({
8676
8731
  isCombo: CreativeRefs.isCombo,
8677
8732
  competitorScanEnabled: CreativeRefs.competitorScanEnabled,
8678
8733
  brandId: CreativeRefs.brandId,
8734
+ projectId: CreativeRefs.projectId,
8679
8735
  creativeDirection: CreativeRefs.creativeDirection
8680
8736
  });
8681
8737
  var LandingInput = z41.object({
@@ -8693,6 +8749,7 @@ var LandingInput = z41.object({
8693
8749
  isCombo: CreativeRefs.isCombo,
8694
8750
  competitorScanEnabled: CreativeRefs.competitorScanEnabled,
8695
8751
  brandId: CreativeRefs.brandId,
8752
+ projectId: CreativeRefs.projectId,
8696
8753
  creativeDirection: CreativeRefs.creativeDirection,
8697
8754
  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".'),
8698
8755
  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".')
@@ -8732,7 +8789,7 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
8732
8789
  try {
8733
8790
  const resolved = resolveVideoPreset(input.videoPreset);
8734
8791
  const wireTier = resolveWireVideoTier(input.videoTier, input.durationSeconds);
8735
- const { videoTier: _publicTier, videoResolution: _vr, ...rest } = input;
8792
+ const { videoTier: _publicTier, videoResolution: _vr, projectId, ...rest } = input;
8736
8793
  const wirePayload = {
8737
8794
  ...rest
8738
8795
  };
@@ -8742,6 +8799,7 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
8742
8799
  }
8743
8800
  wirePayload.videoTier = wireTier;
8744
8801
  if (input.videoResolution) wirePayload.videoResolution = input.videoResolution;
8802
+ if (projectId) wirePayload.dropshippingProjectId = projectId;
8745
8803
  return await submitCreative(api, {
8746
8804
  path: "/v1/creatives/videos",
8747
8805
  estimatedSeconds: 300,
@@ -8754,10 +8812,16 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
8754
8812
  });
8755
8813
  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) => {
8756
8814
  try {
8815
+ const { projectId, ...rest } = input;
8757
8816
  return await submitCreative(api, {
8758
8817
  path: "/v1/creatives/images",
8759
8818
  estimatedSeconds: 60,
8760
- input,
8819
+ input: {
8820
+ ...rest,
8821
+ ...projectId ? {
8822
+ dropshippingProjectId: projectId
8823
+ } : {}
8824
+ },
8761
8825
  transport
8762
8826
  });
8763
8827
  } catch (e) {
@@ -8766,10 +8830,16 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
8766
8830
  });
8767
8831
  server.tool("generate_landing", 'Genera una landing page cl\xE1sica basada en im\xE1genes, 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. Si hay descuento, pregunta tambi\xE9n compareAtPrice. 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, landingStrategy.', LandingInput.shape, async (input) => {
8768
8832
  try {
8833
+ const { projectId, ...rest } = input;
8769
8834
  return await submitCreative(api, {
8770
8835
  path: "/v1/creatives/landing",
8771
8836
  estimatedSeconds: 60,
8772
- input,
8837
+ input: {
8838
+ ...rest,
8839
+ ...projectId ? {
8840
+ dropshippingProjectId: projectId
8841
+ } : {}
8842
+ },
8773
8843
  transport
8774
8844
  });
8775
8845
  } catch (e) {
@@ -10451,6 +10521,13 @@ var DropshippingProjectPhase = z51.enum([
10451
10521
  "advertising",
10452
10522
  "complete"
10453
10523
  ]);
10524
+ var DropshippingRewardMilestone = z51.enum([
10525
+ "product_selected",
10526
+ "costing_saved",
10527
+ "product_media_ready",
10528
+ "creatives_completed",
10529
+ "campaign_created"
10530
+ ]);
10454
10531
  var TargetCountry = z51.enum([
10455
10532
  "CO",
10456
10533
  "MX",
@@ -10573,6 +10650,19 @@ var CostingResultSchema = z51.object({
10573
10650
  wasPriceAutoAdjusted: z51.boolean(),
10574
10651
  profitabilityStatus: z51.literal("healthy")
10575
10652
  });
10653
+ var RewardProgressSchema = z51.object({
10654
+ campaignKey: z51.literal("dropshipping-launch-v1"),
10655
+ totalCredits: z51.number().int().positive(),
10656
+ earnedCredits: z51.number().int().nonnegative(),
10657
+ milestones: z51.array(z51.object({
10658
+ milestone: DropshippingRewardMilestone,
10659
+ credits: z51.number().int().positive(),
10660
+ available: z51.boolean().optional(),
10661
+ earned: z51.boolean(),
10662
+ grantId: ProjectId.nullable(),
10663
+ grantedAt: z51.string().datetime().nullable()
10664
+ })).length(DropshippingRewardMilestone.options.length)
10665
+ });
10576
10666
  var ProjectResponseSchema = z51.object({
10577
10667
  id: ProjectId,
10578
10668
  taskId: ProjectId.nullable(),
@@ -10592,6 +10682,7 @@ var ProjectResponseSchema = z51.object({
10592
10682
  version: z51.number().int().positive(),
10593
10683
  phase: DropshippingProjectPhase,
10594
10684
  nextAction: DropshippingProjectNextAction.nullable(),
10685
+ rewards: RewardProgressSchema,
10595
10686
  createdAt: z51.string().datetime(),
10596
10687
  updatedAt: z51.string().datetime()
10597
10688
  }).passthrough();
@@ -10609,6 +10700,11 @@ function projectText(raw) {
10609
10700
  nextAction: project.nextAction,
10610
10701
  hasReferenceImage: project.hasReferenceImage,
10611
10702
  costingSaved: Boolean(project.costingResult),
10703
+ rewardProgress: {
10704
+ earnedCredits: project.rewards.earnedCredits,
10705
+ totalCredits: project.rewards.totalCredits,
10706
+ milestones: project.rewards.milestones
10707
+ },
10612
10708
  version: project.version,
10613
10709
  message: `Siguiente paso: ${next}. Relee este proyecto despu\xE9s de cada mutaci\xF3n.`
10614
10710
  })
package/dist/stdio.js CHANGED
@@ -150,7 +150,7 @@ var ApiClient = class {
150
150
  };
151
151
 
152
152
  // src/index.ts
153
- var MCP_VERSION = "2.1.0";
153
+ var MCP_VERSION = "2.2.0";
154
154
 
155
155
  // src/tools/tasks.ts
156
156
  import { z as z40 } from "zod";
@@ -1421,11 +1421,12 @@ var IngestOptions = z13.object({
1421
1421
  * shorter than the generated angle count leaves the remaining positions
1422
1422
  * auto-selected. Empty/omitted → all angles auto-select as before. */
1423
1423
  angleNarratives: z13.array(NarrativeStyle).max(3).optional(),
1424
- // 720p is the recommended default for every tier (flipped 2026-06-22 — grok provider
1425
- // unified per-second pricing, so 720p is only a small premium and worth the sharpness).
1424
+ // 480p is the recommended default for every tier (flipped back 2026-07-30 — the
1425
+ // 720p premium is real but small, and users on the entry plan were defaulting
1426
+ // into it while burning half a month of credits on one video).
1426
1427
  // This Zod default is what callers that omit the field (MCP/API) actually get,
1427
- // so it must agree with resolveSingleCallAwareResolution's unset fallback (720p).
1428
- videoResolution: VideoResolution.default("720p"),
1428
+ // so it must agree with resolveSingleCallAwareResolution's unset fallback (480p).
1429
+ videoResolution: VideoResolution.default("480p"),
1429
1430
  generateAdCopy: z13.boolean().default(true),
1430
1431
  generateAiMusic: z13.boolean().optional(),
1431
1432
  voiceId: VoiceId.optional(),
@@ -1540,9 +1541,9 @@ var IngestResumePayload = z13.object({
1540
1541
  videoModel: VideoModel.default("veo"),
1541
1542
  videoTier: VideoTier.optional(),
1542
1543
  narrativeStyle: NarrativeStyle.default("review"),
1543
- // 720p is the recommended default for every tier (flipped 2026-06-22) — keep in sync
1544
- // with IngestOptions.videoResolution and resolveSingleCallAwareResolution.
1545
- videoResolution: VideoResolution.default("720p"),
1544
+ // 480p is the recommended default for every tier (flipped back 2026-07-30) — keep in
1545
+ // sync with IngestOptions.videoResolution and resolveSingleCallAwareResolution.
1546
+ videoResolution: VideoResolution.default("480p"),
1546
1547
  pauseForVideoScriptReview: z13.boolean().optional(),
1547
1548
  generateAiMusic: z13.boolean().optional(),
1548
1549
  scriptNotes: z13.string().max(2e3).optional(),
@@ -5001,16 +5002,37 @@ var DEFAULT_PRICING = {
5001
5002
  ourCostCents: 0.15,
5002
5003
  userPriceCents: 0.3
5003
5004
  },
5004
- // Video QA — independent anatomy arm-scanner (Gemini 3 Flash Preview, one call per
5005
- // sampled frame, up to ARM_SCAN_MAX_FRAMES per clip). Billed PER FRAME (units: 1 per
5006
- // call), so this rate is per frame, not per clip. Sized above the 30d prod average
5007
- // (10.303 entries at $6.81 our => 0.066¢/frame) so the fallback path — used when
5008
- // OpenRouter omits usage.cost — cannot under-charge. Before this key existed those
5009
- // fallbacks hit the `No pricing rule found; skipping` branch and went UNBILLED.
5005
+ // Video QA — anatomy. THREE keys, and they do NOT share a unit. Getting this wrong
5006
+ // over-charges by ~42x, which is exactly what a first pass at the 2026-07-30 change did.
5007
+ //
5008
+ // (a) The arm-scan's per-FRAME counting sweep. STILL LIVE on the whole-video concat path
5009
+ // (`scanClipArms` at apps/api/src/workers/core/video-concat-core.ts), which bills
5010
+ // units:1 PER SAMPLED FRAME, up to ARM_SCAN_FRAME_CEILING. Rate must stay per-frame.
5011
+ // Sized above the 30d prod average (10303 rows => 0.132c user/frame).
5010
5012
  "openrouter:video-anatomy-arm-scan": {
5011
5013
  ourCostCents: 0.1,
5012
5014
  userPriceCents: 0.2
5013
5015
  },
5016
+ // (b) The frame JUDGE that replaced the sweep on the `analyzeVideoClipDJ` path: ALL frames
5017
+ // in ONE multi-image call, so a unit is one call. MEASURED, not estimated —
5018
+ // usage.cost on a real 16-frame judgement at 768px is $0.0417 => 4.17c our. The table is
5019
+ // the FALLBACK used when OpenRouter omits usage.cost, so it must sit at or above that.
5020
+ // Billed PER FRAME (units = frames in the call), like the sweep and for the same reason:
5021
+ // the cost scales with IMAGES and QA_FRAME_JUDGE_FRAMES is env-overridable, so a per-CALL
5022
+ // unit would freeze the fallback charge and the credit-gate quote at the cap the rate
5023
+ // happened to be measured at. Measured $0.0417 over 16 frames => 0.26c/frame; priced at
5024
+ // 0.32c for headroom (n=1 on one clip, and the 1.9k reasoning-token share moves with
5025
+ // QA_DETECTOR_EFFORT). Re-derive from 30d of BillingEntry once this key has volume.
5026
+ "openrouter:video-qa-anatomy-frame-judge": {
5027
+ ourCostCents: 0.32,
5028
+ userPriceCents: 0.64
5029
+ },
5030
+ // (c) Tracing confirmation of a frames-only finding: 3 stills + a 3s 480p slice, one call,
5031
+ // no 16-image prompt — materially cheaper than the judgement it verifies.
5032
+ "openrouter:video-qa-anatomy-tracing-confirm": {
5033
+ ourCostCents: 1.5,
5034
+ userPriceCents: 3
5035
+ },
5014
5036
  // Physics confirmation — skeptical second opinion on D+J floating-object flags
5015
5037
  // (Gemini 3 Flash Preview, ~3 frames per flagged moment, 1 call). Aligned with other vision-QA keys.
5016
5038
  "openrouter:video-physics-confirmation": {
@@ -6563,6 +6585,36 @@ var DropshippingProjectStatusSchema = z37.enum([
6563
6585
  "completed",
6564
6586
  "archived"
6565
6587
  ]);
6588
+ var DROPSHIPPING_LAUNCH_REWARD_CAMPAIGN = "dropshipping-launch-v1";
6589
+ var DropshippingRewardMilestoneSchema = z37.enum([
6590
+ "product_selected",
6591
+ "costing_saved",
6592
+ "product_media_ready",
6593
+ "creatives_completed",
6594
+ "campaign_created"
6595
+ ]);
6596
+ var DROPSHIPPING_LAUNCH_REWARD_CREDITS = {
6597
+ product_selected: 50,
6598
+ costing_saved: 50,
6599
+ product_media_ready: 50,
6600
+ creatives_completed: 150,
6601
+ campaign_created: 200
6602
+ };
6603
+ var DROPSHIPPING_LAUNCH_REWARD_TOTAL_CREDITS = Object.values(DROPSHIPPING_LAUNCH_REWARD_CREDITS).reduce((sum, credits) => sum + credits, 0);
6604
+ var DropshippingProjectRewardMilestoneStateSchema = z37.object({
6605
+ milestone: DropshippingRewardMilestoneSchema,
6606
+ credits: z37.number().int().positive(),
6607
+ available: z37.boolean().optional(),
6608
+ earned: z37.boolean(),
6609
+ grantId: z37.string().uuid().nullable(),
6610
+ grantedAt: z37.string().datetime().nullable()
6611
+ });
6612
+ var DropshippingProjectRewardProgressSchema = z37.object({
6613
+ campaignKey: z37.literal(DROPSHIPPING_LAUNCH_REWARD_CAMPAIGN),
6614
+ totalCredits: z37.number().int().positive(),
6615
+ earnedCredits: z37.number().int().nonnegative(),
6616
+ milestones: z37.array(DropshippingProjectRewardMilestoneStateSchema).length(DropshippingRewardMilestoneSchema.options.length)
6617
+ });
6566
6618
  var DropshippingProjectPhaseSchema = z37.enum([
6567
6619
  "research",
6568
6620
  "costing",
@@ -6720,6 +6772,7 @@ var DropshippingProjectResponseSchema = z37.object({
6720
6772
  version: z37.number().int().positive(),
6721
6773
  phase: DropshippingProjectPhaseSchema,
6722
6774
  nextAction: DropshippingProjectNextActionSchema.nullable(),
6775
+ rewards: DropshippingProjectRewardProgressSchema,
6723
6776
  createdAt: z37.string().datetime(),
6724
6777
  updatedAt: z37.string().datetime()
6725
6778
  });
@@ -6786,7 +6839,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
6786
6839
  {
6787
6840
  name: "create_product_task",
6788
6841
  confirmation: "credit_consuming",
6789
- schemaHash: "828ee643bb3a05a63c6e63730459d35ce1621cbd559bbc76a6c5fcec9a45654d"
6842
+ schemaHash: "5b19f7c6dfdc55b0c472df045e24fe73aa134624a141b63342bfbd3bbb0d043b"
6790
6843
  },
6791
6844
  {
6792
6845
  name: "edit_task_draft",
@@ -6811,17 +6864,17 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
6811
6864
  {
6812
6865
  name: "generate_image_creatives",
6813
6866
  confirmation: "credit_consuming",
6814
- schemaHash: "fb6afcf8571887906340fa0b8dd17196d2814e480b734ad5d374e8810958152e"
6867
+ schemaHash: "82b520e8001e9c57ab0a89d9d66ea5dfc92e6306eed302edfe1005cee5025fbf"
6815
6868
  },
6816
6869
  {
6817
6870
  name: "generate_landing",
6818
6871
  confirmation: "credit_consuming",
6819
- schemaHash: "539922b8fe611c73a7349b1c3f92632fe21f161d7874a0ef2172abfec5834759"
6872
+ schemaHash: "23f0c56dc719235e51d16fd61a9703b64fa6d300356ffbf6b21d703324b0bb18"
6820
6873
  },
6821
6874
  {
6822
6875
  name: "generate_video_creative",
6823
6876
  confirmation: "credit_consuming",
6824
- schemaHash: "94e90d60ad5d68538c31d880a635c5be69d6d204fd2119139735af83ebf0b94a"
6877
+ schemaHash: "79d3ec9310885b482d62c94a27e9c039833c0619d9332a593fe7d32a60ae1388"
6825
6878
  },
6826
6879
  {
6827
6880
  name: "get_meta_campaign_status",
@@ -8063,7 +8116,7 @@ var CreateProductTaskInput = z40.object({
8063
8116
  ]).default(15).describe("Opcional. Duraci\xF3n objetivo del video: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
8064
8117
  pauseForAngleReview: z40.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo para que el usuario lo confirme o edite antes del render. \xDAsalo si quiere decidir 15/30s cuando aparezca un winner. Default false."),
8065
8118
  videoTier: VideoTierEnum.optional().describe('Opcional. Estilo del video. "basic" = "Realista" para 15s; al pedir durationSeconds=30 el sistema selecciona internamente Multiescena. No se expone el tier interno directamente.'),
8066
- 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).'),
8119
+ 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, entre 3% y 15% m\xE1s caro seg\xFAn formato y tier. Si el usuario no menciona resoluci\xF3n, OMITE este campo (el backend usa 480p).'),
8067
8120
  multiAngleVideos: z40.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."),
8068
8121
  angleNarratives: PublicNarrativeStyleEnum.array().max(3).optional().describe('Opcional. Narrativa POR \xC1NGULO (posicional). Valores actuales: "review", "ugc_how_to", "ugc_unboxing_chaos", "product_showcase", "wearable_showcase", "cinematic", "wild_card", "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.'),
8069
8122
  outputs: z40.array(z40.enum([
@@ -8538,6 +8591,7 @@ var CreativeRefs = {
8538
8591
  isCombo: z41.boolean().optional().describe("Opcional. Si true, el creativo presenta el producto como combo/pack. Default false."),
8539
8592
  competitorScanEnabled: z41.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."),
8540
8593
  brandId: z41.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."),
8594
+ projectId: z41.string().uuid().optional().describe("Opcional. ID de un proyecto guiado de dropshipping activo del usuario. Enlaza esta generaci\xF3n con su progreso y recompensas."),
8541
8595
  creativeDirection: z41.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.')
8542
8596
  };
8543
8597
  var VideoInput = z41.object({
@@ -8557,7 +8611,7 @@ var VideoInput = z41.object({
8557
8611
  ]).default(15).describe("Opcional. Duraci\xF3n objetivo: 15s (single_call, default) o 30s (Multiescena, aproximadamente 30s y facturado por escena)."),
8558
8612
  videoPreset: VideoPresetEnum.default("auto").describe('Opcional. Tipo/narrativa del video \u2014 \xFAnico campo de "estilo" que el usuario controla. Default "auto" (la IA escoge tras analizar el producto). Los presets funcionan a 15s y 30s. Opciones: "auto", "ugc_dynamic" (review con experiencia y prueba), "ugc_how_to" (tutorial con pasos y resultado visible), "ugc_unboxing_chaos" (apertura y primera prueba), "product_showcase" (producto ejecutando una funci\xF3n real), "wearable_showcase" (prenda o accesorio modelado), "cinematic" (TV spot con receta cinematogr\xE1fica), "wild_card" (una regla visual experimental original), "pixar_problem" (problema animado 3D), "pixar_sassy" (producto que habla).'),
8559
8613
  videoTier: VideoTierEnum.optional().describe('Opcional. Estilo del video. "basic" = "Realista"; para durationSeconds=30 el sistema selecciona internamente Multiescena. No se expone el tier interno directamente.'),
8560
- 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).'),
8614
+ 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, entre 3% y 15% m\xE1s caro seg\xFAn formato y tier. Si el usuario no menciona resoluci\xF3n, OMITE este campo (el backend usa 480p).'),
8561
8615
  features: z41.array(z41.string()).optional().describe('Opcional. Lista de features/beneficios clave a mencionar. Ej. ["resistente al agua", "bater\xEDa 24h", "carga inal\xE1mbrica"].'),
8562
8616
  price: CreativeRefs.price,
8563
8617
  offer: CreativeRefs.offer,
@@ -8568,7 +8622,8 @@ var VideoInput = z41.object({
8568
8622
  pauseForVideoScriptReview: z41.boolean().optional().describe("Opcional. Si true, la tarea pausa despu\xE9s de generar el guion y antes de renderizar. \xDAsalo cuando el usuario quiera editar el guion antes de gastar cr\xE9ditos en el video. Tras la pausa: get_video_script para leerlo, approve_video_script para aprobar (con o sin ediciones). Default false."),
8569
8623
  pauseForAngleReview: z41.boolean().optional().describe("Opcional. Si true, pausa despu\xE9s de generar el \xE1ngulo para que el usuario lo confirme o edite antes del render. Default false."),
8570
8624
  competitorScanEnabled: CreativeRefs.competitorScanEnabled,
8571
- brandId: CreativeRefs.brandId
8625
+ brandId: CreativeRefs.brandId,
8626
+ projectId: CreativeRefs.projectId
8572
8627
  }).strict();
8573
8628
  var FreeVideoQualityEnum = z41.enum([
8574
8629
  "economical",
@@ -8632,6 +8687,7 @@ var ImageInput = z41.object({
8632
8687
  isCombo: CreativeRefs.isCombo,
8633
8688
  competitorScanEnabled: CreativeRefs.competitorScanEnabled,
8634
8689
  brandId: CreativeRefs.brandId,
8690
+ projectId: CreativeRefs.projectId,
8635
8691
  creativeDirection: CreativeRefs.creativeDirection
8636
8692
  });
8637
8693
  var LandingInput = z41.object({
@@ -8649,6 +8705,7 @@ var LandingInput = z41.object({
8649
8705
  isCombo: CreativeRefs.isCombo,
8650
8706
  competitorScanEnabled: CreativeRefs.competitorScanEnabled,
8651
8707
  brandId: CreativeRefs.brandId,
8708
+ projectId: CreativeRefs.projectId,
8652
8709
  creativeDirection: CreativeRefs.creativeDirection,
8653
8710
  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".'),
8654
8711
  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".')
@@ -8688,7 +8745,7 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
8688
8745
  try {
8689
8746
  const resolved = resolveVideoPreset(input.videoPreset);
8690
8747
  const wireTier = resolveWireVideoTier(input.videoTier, input.durationSeconds);
8691
- const { videoTier: _publicTier, videoResolution: _vr, ...rest } = input;
8748
+ const { videoTier: _publicTier, videoResolution: _vr, projectId, ...rest } = input;
8692
8749
  const wirePayload = {
8693
8750
  ...rest
8694
8751
  };
@@ -8698,6 +8755,7 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
8698
8755
  }
8699
8756
  wirePayload.videoTier = wireTier;
8700
8757
  if (input.videoResolution) wirePayload.videoResolution = input.videoResolution;
8758
+ if (projectId) wirePayload.dropshippingProjectId = projectId;
8701
8759
  return await submitCreative(api, {
8702
8760
  path: "/v1/creatives/videos",
8703
8761
  estimatedSeconds: 300,
@@ -8710,10 +8768,16 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
8710
8768
  });
8711
8769
  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) => {
8712
8770
  try {
8771
+ const { projectId, ...rest } = input;
8713
8772
  return await submitCreative(api, {
8714
8773
  path: "/v1/creatives/images",
8715
8774
  estimatedSeconds: 60,
8716
- input,
8775
+ input: {
8776
+ ...rest,
8777
+ ...projectId ? {
8778
+ dropshippingProjectId: projectId
8779
+ } : {}
8780
+ },
8717
8781
  transport
8718
8782
  });
8719
8783
  } catch (e) {
@@ -8722,10 +8786,16 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
8722
8786
  });
8723
8787
  server.tool("generate_landing", 'Genera una landing page cl\xE1sica basada en im\xE1genes, 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. Si hay descuento, pregunta tambi\xE9n compareAtPrice. 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, landingStrategy.', LandingInput.shape, async (input) => {
8724
8788
  try {
8789
+ const { projectId, ...rest } = input;
8725
8790
  return await submitCreative(api, {
8726
8791
  path: "/v1/creatives/landing",
8727
8792
  estimatedSeconds: 60,
8728
- input,
8793
+ input: {
8794
+ ...rest,
8795
+ ...projectId ? {
8796
+ dropshippingProjectId: projectId
8797
+ } : {}
8798
+ },
8729
8799
  transport
8730
8800
  });
8731
8801
  } catch (e) {
@@ -10407,6 +10477,13 @@ var DropshippingProjectPhase = z51.enum([
10407
10477
  "advertising",
10408
10478
  "complete"
10409
10479
  ]);
10480
+ var DropshippingRewardMilestone = z51.enum([
10481
+ "product_selected",
10482
+ "costing_saved",
10483
+ "product_media_ready",
10484
+ "creatives_completed",
10485
+ "campaign_created"
10486
+ ]);
10410
10487
  var TargetCountry = z51.enum([
10411
10488
  "CO",
10412
10489
  "MX",
@@ -10529,6 +10606,19 @@ var CostingResultSchema = z51.object({
10529
10606
  wasPriceAutoAdjusted: z51.boolean(),
10530
10607
  profitabilityStatus: z51.literal("healthy")
10531
10608
  });
10609
+ var RewardProgressSchema = z51.object({
10610
+ campaignKey: z51.literal("dropshipping-launch-v1"),
10611
+ totalCredits: z51.number().int().positive(),
10612
+ earnedCredits: z51.number().int().nonnegative(),
10613
+ milestones: z51.array(z51.object({
10614
+ milestone: DropshippingRewardMilestone,
10615
+ credits: z51.number().int().positive(),
10616
+ available: z51.boolean().optional(),
10617
+ earned: z51.boolean(),
10618
+ grantId: ProjectId.nullable(),
10619
+ grantedAt: z51.string().datetime().nullable()
10620
+ })).length(DropshippingRewardMilestone.options.length)
10621
+ });
10532
10622
  var ProjectResponseSchema = z51.object({
10533
10623
  id: ProjectId,
10534
10624
  taskId: ProjectId.nullable(),
@@ -10548,6 +10638,7 @@ var ProjectResponseSchema = z51.object({
10548
10638
  version: z51.number().int().positive(),
10549
10639
  phase: DropshippingProjectPhase,
10550
10640
  nextAction: DropshippingProjectNextAction.nullable(),
10641
+ rewards: RewardProgressSchema,
10551
10642
  createdAt: z51.string().datetime(),
10552
10643
  updatedAt: z51.string().datetime()
10553
10644
  }).passthrough();
@@ -10565,6 +10656,11 @@ function projectText(raw) {
10565
10656
  nextAction: project.nextAction,
10566
10657
  hasReferenceImage: project.hasReferenceImage,
10567
10658
  costingSaved: Boolean(project.costingResult),
10659
+ rewardProgress: {
10660
+ earnedCredits: project.rewards.earnedCredits,
10661
+ totalCredits: project.rewards.totalCredits,
10662
+ milestones: project.rewards.milestones
10663
+ },
10568
10664
  version: project.version,
10569
10665
  message: `Siguiente paso: ${next}. Relee este proyecto despu\xE9s de cada mutaci\xF3n.`
10570
10666
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@productmaker/mcp",
3
- "version": "2.1.0",
3
+ "version": "2.2.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-research": "0.1.0",
67
66
  "@packages/product-economics": "0.1.0",
67
+ "@packages/product-research": "0.1.0",
68
68
  "@packages/types": "0.1.0"
69
69
  },
70
70
  "publishConfig": {