@productmaker/mcp 2.9.2 → 2.9.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 +51 -30
- package/dist/stdio.js +51 -30
- package/dist/ui/meta-upload-app.html +242 -588
- package/package.json +3 -3
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.9.
|
|
156
|
+
var MCP_VERSION = "2.9.4";
|
|
157
157
|
|
|
158
158
|
// src/meta-upload-app.ts
|
|
159
159
|
import { readFileSync } from "fs";
|
|
@@ -8052,7 +8052,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
8052
8052
|
{
|
|
8053
8053
|
name: "publish_creatives_to_meta",
|
|
8054
8054
|
confirmation: "external_mutation",
|
|
8055
|
-
schemaHash: "
|
|
8055
|
+
schemaHash: "f712fe12baee19de6ac5ed94b23de630247a345ed13c3a431a647d421480280f"
|
|
8056
8056
|
},
|
|
8057
8057
|
{
|
|
8058
8058
|
name: "publish_to_meta",
|
|
@@ -8915,7 +8915,7 @@ var CODE_MESSAGES = {
|
|
|
8915
8915
|
META_TOKEN_EXPIRED: "Your Meta session expired. Reconnect Facebook at https://productmaker.app/integrations.",
|
|
8916
8916
|
META_ACCOUNT_RESTRICTED: "Your Meta Ads account is temporarily restricted from creating ads. Check the notices at https://business.facebook.com and complete the required verification.",
|
|
8917
8917
|
META_CAPABILITY_MISSING: "The Facebook app lacks required permissions for this ad account. Reconnect Facebook at https://productmaker.app/integrations and accept ALL requested permissions (including business_management).",
|
|
8918
|
-
META_PERMISSION_DENIED: "
|
|
8918
|
+
META_PERMISSION_DENIED: "Meta denied write access to the selected ad account. Ask a Business Manager admin to grant this user Advertiser role or higher for that account, then reconnect Facebook at https://productmaker.app/integrations and accept every requested permission, including ads_management.",
|
|
8919
8919
|
META_PIXEL_NOT_FOUND: "Pixel not found or not associated with the ad account. Verify the `pixelId` at https://business.facebook.com/events_manager.",
|
|
8920
8920
|
META_OBJECT_NOT_FOUND: "The requested object (campaign/ad set/ad) does not exist in Meta. Verify the ID or create a new one.",
|
|
8921
8921
|
META_AI_DISCLOSURE_REQUIRED: "Meta rejected the ad for undisclosed AI content. The system adds the disclosure automatically; if it persists, edit the copy to avoid language Meta flags as misleading.",
|
|
@@ -10901,10 +10901,10 @@ var GroupedCreative = z52.object({
|
|
|
10901
10901
|
cta: MetaCtaType.optional(),
|
|
10902
10902
|
creativeId: z52.string().min(1).optional()
|
|
10903
10903
|
});
|
|
10904
|
-
var
|
|
10904
|
+
var GroupedCampaignConfigBase = z52.object({
|
|
10905
10905
|
campaignType: MetaCampaignType.default("sales"),
|
|
10906
10906
|
optimizationMode: MetaOptimizationMode.default("advantage_plus"),
|
|
10907
|
-
budgetMode: MetaBudgetMode.
|
|
10907
|
+
budgetMode: MetaBudgetMode.optional(),
|
|
10908
10908
|
campaignDailyBudget: z52.number().int().positive().optional(),
|
|
10909
10909
|
adSets: z52.array(z52.object({
|
|
10910
10910
|
tempId: z52.string().min(1).max(80),
|
|
@@ -10930,8 +10930,17 @@ var GroupedCampaignConfig = z52.object({
|
|
|
10930
10930
|
]),
|
|
10931
10931
|
saveAsDefault: z52.boolean().default(false),
|
|
10932
10932
|
budgetPolicy: MetaBudgetPolicy.optional()
|
|
10933
|
-
})
|
|
10934
|
-
|
|
10933
|
+
});
|
|
10934
|
+
function resolveBudgetMode(value) {
|
|
10935
|
+
if (value.budgetMode) return value.budgetMode;
|
|
10936
|
+
if (value.campaignDailyBudget !== void 0) return "campaign";
|
|
10937
|
+
if (value.adSets.every((adSet) => adSet.dailyBudget !== void 0)) return "ad_set";
|
|
10938
|
+
return "campaign";
|
|
10939
|
+
}
|
|
10940
|
+
__name(resolveBudgetMode, "resolveBudgetMode");
|
|
10941
|
+
var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) => {
|
|
10942
|
+
const budgetMode = resolveBudgetMode(value);
|
|
10943
|
+
if (budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
|
|
10935
10944
|
ctx.addIssue({
|
|
10936
10945
|
code: z52.ZodIssueCode.custom,
|
|
10937
10946
|
path: [
|
|
@@ -10940,7 +10949,7 @@ var GroupedCampaignConfig = z52.object({
|
|
|
10940
10949
|
message: "campaignDailyBudget is required when budgetMode is campaign"
|
|
10941
10950
|
});
|
|
10942
10951
|
}
|
|
10943
|
-
if (
|
|
10952
|
+
if (budgetMode === "ad_set") {
|
|
10944
10953
|
value.adSets.forEach((adSet, index) => {
|
|
10945
10954
|
if (adSet.dailyBudget === void 0) {
|
|
10946
10955
|
ctx.addIssue({
|
|
@@ -10955,7 +10964,10 @@ var GroupedCampaignConfig = z52.object({
|
|
|
10955
10964
|
}
|
|
10956
10965
|
});
|
|
10957
10966
|
}
|
|
10958
|
-
})
|
|
10967
|
+
}).transform((value) => ({
|
|
10968
|
+
...value,
|
|
10969
|
+
budgetMode: resolveBudgetMode(value)
|
|
10970
|
+
}));
|
|
10959
10971
|
var PublishCreativesToMetaInputBase = z52.object({
|
|
10960
10972
|
creatives: z52.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
10961
10973
|
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
@@ -10973,7 +10985,7 @@ var PublishCreativesToMetaInputBase = z52.object({
|
|
|
10973
10985
|
});
|
|
10974
10986
|
var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
|
|
10975
10987
|
const destination = value.campaignConfig?.destination;
|
|
10976
|
-
if (destination?.type !== "whatsapp" && !value.landingUrl) {
|
|
10988
|
+
if (destination?.type !== "whatsapp" && destination?.type !== "landing" && !value.landingUrl) {
|
|
10977
10989
|
ctx.addIssue({
|
|
10978
10990
|
code: z52.ZodIssueCode.custom,
|
|
10979
10991
|
path: [
|
|
@@ -10982,7 +10994,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
10982
10994
|
message: "landingUrl is required unless campaignConfig.destination.type is whatsapp"
|
|
10983
10995
|
});
|
|
10984
10996
|
}
|
|
10985
|
-
if (destination?.type === "landing" && destination.landingUrl !== value.landingUrl) {
|
|
10997
|
+
if (destination?.type === "landing" && value.landingUrl && destination.landingUrl !== value.landingUrl) {
|
|
10986
10998
|
ctx.addIssue({
|
|
10987
10999
|
code: z52.ZodIssueCode.custom,
|
|
10988
11000
|
path: [
|
|
@@ -10993,6 +11005,15 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
10993
11005
|
message: "campaignConfig.destination.landingUrl must match landingUrl"
|
|
10994
11006
|
});
|
|
10995
11007
|
}
|
|
11008
|
+
}).transform((value) => {
|
|
11009
|
+
const destination = value.campaignConfig?.destination;
|
|
11010
|
+
if (destination?.type === "landing" && !value.landingUrl) {
|
|
11011
|
+
return {
|
|
11012
|
+
...value,
|
|
11013
|
+
landingUrl: destination.landingUrl
|
|
11014
|
+
};
|
|
11015
|
+
}
|
|
11016
|
+
return value;
|
|
10996
11017
|
});
|
|
10997
11018
|
var PresignUploadResponseSchema = z52.object({
|
|
10998
11019
|
uploadUrl: z52.string().url().refine((value) => {
|
|
@@ -11104,7 +11125,7 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
11104
11125
|
throw translateError(e);
|
|
11105
11126
|
}
|
|
11106
11127
|
});
|
|
11107
|
-
server.tool("publish_creatives_to_meta", "Crea UNA campa\xF1a de Meta Ads PAUSED agrupando hasta " + CAMPAIGN_LIMITS.MAX_CREATIVES + ' creativos. Antes llama `list_meta_destinations` y pide al usuario elegir el `portfolioId`, `adAccountId` y `pageId` exactos. Para archivos en la laptop con MCP remoto: llama `request_meta_media_upload`, espera al usuario y consulta `check_meta_media_upload`; por CADA entrada de `assets[]` crea un creativo `source.type="staged"`, con `type` igual a su `mediaKind`. Si la carga fue parcial, publica s\xF3lo los assets que ese estado confirm\xF3; nunca inventes assets ni superes diez creativos. Las rutas locales s\xF3lo funcionan con MCP stdio. Los medios propios se suben temporalmente al CDN de ProductMaker y se eliminan despu\xE9s de publicar correctamente; si falla, quedan s\xF3lo para reintento
|
|
11128
|
+
server.tool("publish_creatives_to_meta", "Crea UNA campa\xF1a de Meta Ads PAUSED agrupando hasta " + CAMPAIGN_LIMITS.MAX_CREATIVES + ' creativos. Antes llama `list_meta_destinations` y pide al usuario elegir el `portfolioId`, `adAccountId` y `pageId` exactos. Para archivos en la laptop con MCP remoto: llama `request_meta_media_upload`, espera al usuario y consulta `check_meta_media_upload`; por CADA entrada de `assets[]` crea un creativo `source.type="staged"`, con `type` igual a su `mediaKind`. Si la carga fue parcial, publica s\xF3lo los assets que ese estado confirm\xF3; nunca inventes assets ni superes diez creativos. Las rutas locales s\xF3lo funcionan con MCP stdio. Los medios propios se suben temporalmente al CDN de ProductMaker y se eliminan despu\xE9s de publicar correctamente; si falla, quedan s\xF3lo durante el TTL para reintento: nunca afirmes que una sesi\xF3n no expira. Para videos la miniatura es opcional y ProductMaker puede generarla con FFmpeg. `campaignConfig.destination` acepta landing o WhatsApp. Para landing basta `campaignConfig.destination.landingUrl`; no dupliques la URL arriba. Si omites `budgetMode`, se infiere `ad_set` cuando cada ad set trae `dailyBudget`, o `campaign` cuando env\xEDas `campaignDailyBudget`. No actives la campa\xF1a autom\xE1ticamente.', PublishCreativesToMetaInputBase.shape, async (rawInput) => {
|
|
11108
11129
|
try {
|
|
11109
11130
|
const parsed = PublishCreativesToMetaInput.safeParse(rawInput);
|
|
11110
11131
|
if (!parsed.success) throw invalidInputFromZod(parsed.error);
|
|
@@ -11365,8 +11386,7 @@ function uploadSessionText(result) {
|
|
|
11365
11386
|
text: [
|
|
11366
11387
|
`### Prepara tu ${mediaLabel} en ProductMaker`,
|
|
11367
11388
|
`Usa primero el App inline de este chat. Si tu host no soporta el bridge de archivos, usa este fallback: [Abrir carga segura](${result.uploadPageUrl})`,
|
|
11368
|
-
`Puedes seleccionar hasta ${result.maxFiles} ${result.mediaKind === "mixed" ? "archivos" : "archivo"}. Cuando
|
|
11369
|
-
`sessionId: \`${result.sessionId}\``,
|
|
11389
|
+
`Puedes seleccionar hasta ${result.maxFiles} ${result.mediaKind === "mixed" ? "archivos" : "archivo"}. Cuando termines, pulsa **OK** dentro del App para que ProductMaker contin\xFAe en este chat.`,
|
|
11370
11390
|
`Este enlace expira: ${result.expiresAt}`
|
|
11371
11391
|
].join("\n\n")
|
|
11372
11392
|
}
|
|
@@ -11377,7 +11397,7 @@ __name(uploadSessionText, "uploadSessionText");
|
|
|
11377
11397
|
function registerMetaUploadTools(server, api) {
|
|
11378
11398
|
server.registerTool("request_meta_media_upload", {
|
|
11379
11399
|
title: "Solicitar carga segura de Meta",
|
|
11380
|
-
description: "Inicia una carga segura desde la laptop del usuario para creativos propios de Meta. Por defecto crea una sesi\xF3n `mixed` de hasta diez archivos; tambi\xE9n admite una sola imagen o video. Si el usuario adjunta una foto o video en el mismo mensaje de ChatGPT, pasa esas referencias opcionales en `files` para que el MCP App inline las consuma dentro del chat. ProductMaker conserva `files` solo como metadata del UI bridge: NO los descarga y NO los reenv\xEDa. No pidas URL CDN, ruta local, bytes ni base64. El
|
|
11400
|
+
description: "Inicia una carga segura desde la laptop del usuario para creativos propios de Meta. Esta es la ruta DIRECTA para peticiones puntuales como \u2018sube esta imagen/video a Meta Ads\u2019 o \u2018publica este creativo\u2019; no conviertas esa intenci\xF3n en una generaci\xF3n completa de producto. No llames `guide_dropshipping_launch` para esta operaci\xF3n puntual. Por defecto crea una sesi\xF3n `mixed` de hasta diez archivos; tambi\xE9n admite una sola imagen o video. Si el usuario adjunta una foto o video en el mismo mensaje de ChatGPT, pasa esas referencias opcionales en `files` para que el MCP App inline las consuma dentro del chat. ProductMaker conserva `files` solo como metadata del UI bridge: NO los descarga y NO los reenv\xEDa. No pidas URL CDN, ruta local, bytes ni base64. El App es exclusivamente para cargar archivos: nunca debe pedir ni mostrar portafolio, cuenta publicitaria, p\xE1gina, IDs o JSON. El enlace externo de ProductMaker queda como fallback para hosts sin bridge de archivos. El usuario pulsa OK cuando termina; el App inserta en el chat los asset_id verificados para continuar la petici\xF3n anterior. Luego llama `list_meta_destinations` y pregunta en el chat qu\xE9 `portfolioId`, `adAccountId` y `pageId` desea usar. No muestres tokens, URLs temporales, `sessionId` ni un bloque JSON. Esta herramienta no publica ni consume cr\xE9ditos.",
|
|
11381
11401
|
inputSchema: MetaUploadSessionToolInputShape,
|
|
11382
11402
|
outputSchema: MetaUploadSessionResponse2,
|
|
11383
11403
|
_meta: {
|
|
@@ -11410,7 +11430,7 @@ function registerMetaUploadTools(server, api) {
|
|
|
11410
11430
|
}
|
|
11411
11431
|
});
|
|
11412
11432
|
server.registerTool("check_meta_media_upload", {
|
|
11413
|
-
description: 'Comprueba si el usuario ya termin\xF3 de subir desde el enlace de ProductMaker. Si status=uploaded,
|
|
11433
|
+
description: 'Comprueba si el usuario ya termin\xF3 de subir desde el App o enlace de ProductMaker. Si status=uploaded, conserva `assets[]` privadamente, llama `list_meta_destinations` y pregunta en lenguaje natural dentro del chat qu\xE9 portafolio, cuenta publicitaria y p\xE1gina desea usar; nunca muestres `sessionId`, `stagingAssetId`, IDs t\xE9cnicos ni un bloque JSON. Despu\xE9s crea un creativo `source.type="staged"` por cada asset, usando su `mediaKind` como tipo, hasta diez en el batch. Si la carga fue parcial, usa \xFAnicamente los assets confirmados. Conserva el `stagingAssetId` legado si aparece. Si status=pending, pide al usuario que termine; no publiques todav\xEDa.',
|
|
11414
11434
|
inputSchema: MetaUploadStatusInput.shape,
|
|
11415
11435
|
_meta: {
|
|
11416
11436
|
"openai/widgetAccessible": true
|
|
@@ -12365,20 +12385,21 @@ var CostingInputSchema = z59.object({
|
|
|
12365
12385
|
]
|
|
12366
12386
|
});
|
|
12367
12387
|
});
|
|
12388
|
+
var FiniteCostingNumber = z59.number().finite();
|
|
12368
12389
|
var CostingResultSchema = z59.object({
|
|
12369
12390
|
country: z59.string().min(2).max(3),
|
|
12370
|
-
deliveryRate:
|
|
12371
|
-
adjustedShippingCost:
|
|
12372
|
-
adjustedCpa:
|
|
12373
|
-
totalCost:
|
|
12374
|
-
suggestedPrice:
|
|
12375
|
-
baseCommercialPrice:
|
|
12376
|
-
minimumSafePrice:
|
|
12377
|
-
commercialPrice:
|
|
12378
|
-
comparisonPrice:
|
|
12379
|
-
estimatedProfit:
|
|
12380
|
-
actualMargin:
|
|
12381
|
-
breakEvenAdsManagerCpa:
|
|
12391
|
+
deliveryRate: FiniteCostingNumber,
|
|
12392
|
+
adjustedShippingCost: FiniteCostingNumber,
|
|
12393
|
+
adjustedCpa: FiniteCostingNumber,
|
|
12394
|
+
totalCost: FiniteCostingNumber,
|
|
12395
|
+
suggestedPrice: FiniteCostingNumber,
|
|
12396
|
+
baseCommercialPrice: FiniteCostingNumber,
|
|
12397
|
+
minimumSafePrice: FiniteCostingNumber,
|
|
12398
|
+
commercialPrice: FiniteCostingNumber,
|
|
12399
|
+
comparisonPrice: FiniteCostingNumber,
|
|
12400
|
+
estimatedProfit: FiniteCostingNumber,
|
|
12401
|
+
actualMargin: FiniteCostingNumber,
|
|
12402
|
+
breakEvenAdsManagerCpa: FiniteCostingNumber,
|
|
12382
12403
|
wasPriceAutoAdjusted: z59.boolean(),
|
|
12383
12404
|
profitabilityStatus: z59.literal("healthy")
|
|
12384
12405
|
});
|
|
@@ -12467,13 +12488,13 @@ function registerDropshippingProjectTools(server, api) {
|
|
|
12467
12488
|
throw translateError(error);
|
|
12468
12489
|
}
|
|
12469
12490
|
});
|
|
12470
|
-
server.tool("guide_dropshipping_launch", "Gu\xEDa read-only
|
|
12491
|
+
server.tool("guide_dropshipping_launch", "Gu\xEDa read-only de un recorrido COMPLETO de dropshipping, desde producto/costeo hasta creativos, tienda y publicidad. No uses este tool para subir o publicar un creativo puntual en Meta: llama directamente `request_meta_media_upload`, `list_meta_destinations` y `publish_creatives_to_meta`. Sin projectId explica cu\xE1ndo usar un flujo directo versus uno guiado; con projectId devuelve la fase y la \xFAnica nextAction calculada por ProductMaker. Despu\xE9s de cada mutaci\xF3n vuelve a llamar este tool o lee el proyecto. Nunca activa Meta.", GuideInput.shape, async (input) => {
|
|
12471
12492
|
try {
|
|
12472
12493
|
if (!input.projectId) {
|
|
12473
12494
|
return asText({
|
|
12474
12495
|
mode: "choose",
|
|
12475
12496
|
guided: "Usa start_dropshipping_project cuando el usuario quiera encontrar un producto y dejarlo listo para publicar.",
|
|
12476
|
-
direct: "En modo directo usa
|
|
12497
|
+
direct: "En modo directo usa la herramienta puntual. Para subir/publicar un creativo propio en Meta: request_meta_media_upload \u2192 list_meta_destinations \u2192 publish_creatives_to_meta. No crees un proyecto guiado.",
|
|
12477
12498
|
confirmation: "Antes de acciones pagadas o externas, muestra el estimado y pide confirmaci\xF3n.",
|
|
12478
12499
|
safety: "Las campa\xF1as Meta quedan PAUSED; el usuario las activa manualmente en Ads Manager."
|
|
12479
12500
|
});
|
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.9.
|
|
153
|
+
var MCP_VERSION = "2.9.4";
|
|
154
154
|
|
|
155
155
|
// src/meta-upload-app.ts
|
|
156
156
|
import { readFileSync } from "fs";
|
|
@@ -8049,7 +8049,7 @@ var PRODUCTMAKER_MCP_CAPABILITIES = [
|
|
|
8049
8049
|
{
|
|
8050
8050
|
name: "publish_creatives_to_meta",
|
|
8051
8051
|
confirmation: "external_mutation",
|
|
8052
|
-
schemaHash: "
|
|
8052
|
+
schemaHash: "f712fe12baee19de6ac5ed94b23de630247a345ed13c3a431a647d421480280f"
|
|
8053
8053
|
},
|
|
8054
8054
|
{
|
|
8055
8055
|
name: "publish_to_meta",
|
|
@@ -8912,7 +8912,7 @@ var CODE_MESSAGES = {
|
|
|
8912
8912
|
META_TOKEN_EXPIRED: "Your Meta session expired. Reconnect Facebook at https://productmaker.app/integrations.",
|
|
8913
8913
|
META_ACCOUNT_RESTRICTED: "Your Meta Ads account is temporarily restricted from creating ads. Check the notices at https://business.facebook.com and complete the required verification.",
|
|
8914
8914
|
META_CAPABILITY_MISSING: "The Facebook app lacks required permissions for this ad account. Reconnect Facebook at https://productmaker.app/integrations and accept ALL requested permissions (including business_management).",
|
|
8915
|
-
META_PERMISSION_DENIED: "
|
|
8915
|
+
META_PERMISSION_DENIED: "Meta denied write access to the selected ad account. Ask a Business Manager admin to grant this user Advertiser role or higher for that account, then reconnect Facebook at https://productmaker.app/integrations and accept every requested permission, including ads_management.",
|
|
8916
8916
|
META_PIXEL_NOT_FOUND: "Pixel not found or not associated with the ad account. Verify the `pixelId` at https://business.facebook.com/events_manager.",
|
|
8917
8917
|
META_OBJECT_NOT_FOUND: "The requested object (campaign/ad set/ad) does not exist in Meta. Verify the ID or create a new one.",
|
|
8918
8918
|
META_AI_DISCLOSURE_REQUIRED: "Meta rejected the ad for undisclosed AI content. The system adds the disclosure automatically; if it persists, edit the copy to avoid language Meta flags as misleading.",
|
|
@@ -10857,10 +10857,10 @@ var GroupedCreative = z52.object({
|
|
|
10857
10857
|
cta: MetaCtaType.optional(),
|
|
10858
10858
|
creativeId: z52.string().min(1).optional()
|
|
10859
10859
|
});
|
|
10860
|
-
var
|
|
10860
|
+
var GroupedCampaignConfigBase = z52.object({
|
|
10861
10861
|
campaignType: MetaCampaignType.default("sales"),
|
|
10862
10862
|
optimizationMode: MetaOptimizationMode.default("advantage_plus"),
|
|
10863
|
-
budgetMode: MetaBudgetMode.
|
|
10863
|
+
budgetMode: MetaBudgetMode.optional(),
|
|
10864
10864
|
campaignDailyBudget: z52.number().int().positive().optional(),
|
|
10865
10865
|
adSets: z52.array(z52.object({
|
|
10866
10866
|
tempId: z52.string().min(1).max(80),
|
|
@@ -10886,8 +10886,17 @@ var GroupedCampaignConfig = z52.object({
|
|
|
10886
10886
|
]),
|
|
10887
10887
|
saveAsDefault: z52.boolean().default(false),
|
|
10888
10888
|
budgetPolicy: MetaBudgetPolicy.optional()
|
|
10889
|
-
})
|
|
10890
|
-
|
|
10889
|
+
});
|
|
10890
|
+
function resolveBudgetMode(value) {
|
|
10891
|
+
if (value.budgetMode) return value.budgetMode;
|
|
10892
|
+
if (value.campaignDailyBudget !== void 0) return "campaign";
|
|
10893
|
+
if (value.adSets.every((adSet) => adSet.dailyBudget !== void 0)) return "ad_set";
|
|
10894
|
+
return "campaign";
|
|
10895
|
+
}
|
|
10896
|
+
__name(resolveBudgetMode, "resolveBudgetMode");
|
|
10897
|
+
var GroupedCampaignConfig = GroupedCampaignConfigBase.superRefine((value, ctx) => {
|
|
10898
|
+
const budgetMode = resolveBudgetMode(value);
|
|
10899
|
+
if (budgetMode === "campaign" && value.campaignDailyBudget === void 0) {
|
|
10891
10900
|
ctx.addIssue({
|
|
10892
10901
|
code: z52.ZodIssueCode.custom,
|
|
10893
10902
|
path: [
|
|
@@ -10896,7 +10905,7 @@ var GroupedCampaignConfig = z52.object({
|
|
|
10896
10905
|
message: "campaignDailyBudget is required when budgetMode is campaign"
|
|
10897
10906
|
});
|
|
10898
10907
|
}
|
|
10899
|
-
if (
|
|
10908
|
+
if (budgetMode === "ad_set") {
|
|
10900
10909
|
value.adSets.forEach((adSet, index) => {
|
|
10901
10910
|
if (adSet.dailyBudget === void 0) {
|
|
10902
10911
|
ctx.addIssue({
|
|
@@ -10911,7 +10920,10 @@ var GroupedCampaignConfig = z52.object({
|
|
|
10911
10920
|
}
|
|
10912
10921
|
});
|
|
10913
10922
|
}
|
|
10914
|
-
})
|
|
10923
|
+
}).transform((value) => ({
|
|
10924
|
+
...value,
|
|
10925
|
+
budgetMode: resolveBudgetMode(value)
|
|
10926
|
+
}));
|
|
10915
10927
|
var PublishCreativesToMetaInputBase = z52.object({
|
|
10916
10928
|
creatives: z52.array(GroupedCreative).min(1).max(CAMPAIGN_LIMITS.MAX_CREATIVES),
|
|
10917
10929
|
/** Exact Meta Business portfolio selected from `list_meta_destinations`. */
|
|
@@ -10929,7 +10941,7 @@ var PublishCreativesToMetaInputBase = z52.object({
|
|
|
10929
10941
|
});
|
|
10930
10942
|
var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((value, ctx) => {
|
|
10931
10943
|
const destination = value.campaignConfig?.destination;
|
|
10932
|
-
if (destination?.type !== "whatsapp" && !value.landingUrl) {
|
|
10944
|
+
if (destination?.type !== "whatsapp" && destination?.type !== "landing" && !value.landingUrl) {
|
|
10933
10945
|
ctx.addIssue({
|
|
10934
10946
|
code: z52.ZodIssueCode.custom,
|
|
10935
10947
|
path: [
|
|
@@ -10938,7 +10950,7 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
10938
10950
|
message: "landingUrl is required unless campaignConfig.destination.type is whatsapp"
|
|
10939
10951
|
});
|
|
10940
10952
|
}
|
|
10941
|
-
if (destination?.type === "landing" && destination.landingUrl !== value.landingUrl) {
|
|
10953
|
+
if (destination?.type === "landing" && value.landingUrl && destination.landingUrl !== value.landingUrl) {
|
|
10942
10954
|
ctx.addIssue({
|
|
10943
10955
|
code: z52.ZodIssueCode.custom,
|
|
10944
10956
|
path: [
|
|
@@ -10949,6 +10961,15 @@ var PublishCreativesToMetaInput = PublishCreativesToMetaInputBase.superRefine((v
|
|
|
10949
10961
|
message: "campaignConfig.destination.landingUrl must match landingUrl"
|
|
10950
10962
|
});
|
|
10951
10963
|
}
|
|
10964
|
+
}).transform((value) => {
|
|
10965
|
+
const destination = value.campaignConfig?.destination;
|
|
10966
|
+
if (destination?.type === "landing" && !value.landingUrl) {
|
|
10967
|
+
return {
|
|
10968
|
+
...value,
|
|
10969
|
+
landingUrl: destination.landingUrl
|
|
10970
|
+
};
|
|
10971
|
+
}
|
|
10972
|
+
return value;
|
|
10952
10973
|
});
|
|
10953
10974
|
var PresignUploadResponseSchema = z52.object({
|
|
10954
10975
|
uploadUrl: z52.string().url().refine((value) => {
|
|
@@ -11060,7 +11081,7 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
11060
11081
|
throw translateError(e);
|
|
11061
11082
|
}
|
|
11062
11083
|
});
|
|
11063
|
-
server.tool("publish_creatives_to_meta", "Crea UNA campa\xF1a de Meta Ads PAUSED agrupando hasta " + CAMPAIGN_LIMITS.MAX_CREATIVES + ' creativos. Antes llama `list_meta_destinations` y pide al usuario elegir el `portfolioId`, `adAccountId` y `pageId` exactos. Para archivos en la laptop con MCP remoto: llama `request_meta_media_upload`, espera al usuario y consulta `check_meta_media_upload`; por CADA entrada de `assets[]` crea un creativo `source.type="staged"`, con `type` igual a su `mediaKind`. Si la carga fue parcial, publica s\xF3lo los assets que ese estado confirm\xF3; nunca inventes assets ni superes diez creativos. Las rutas locales s\xF3lo funcionan con MCP stdio. Los medios propios se suben temporalmente al CDN de ProductMaker y se eliminan despu\xE9s de publicar correctamente; si falla, quedan s\xF3lo para reintento
|
|
11084
|
+
server.tool("publish_creatives_to_meta", "Crea UNA campa\xF1a de Meta Ads PAUSED agrupando hasta " + CAMPAIGN_LIMITS.MAX_CREATIVES + ' creativos. Antes llama `list_meta_destinations` y pide al usuario elegir el `portfolioId`, `adAccountId` y `pageId` exactos. Para archivos en la laptop con MCP remoto: llama `request_meta_media_upload`, espera al usuario y consulta `check_meta_media_upload`; por CADA entrada de `assets[]` crea un creativo `source.type="staged"`, con `type` igual a su `mediaKind`. Si la carga fue parcial, publica s\xF3lo los assets que ese estado confirm\xF3; nunca inventes assets ni superes diez creativos. Las rutas locales s\xF3lo funcionan con MCP stdio. Los medios propios se suben temporalmente al CDN de ProductMaker y se eliminan despu\xE9s de publicar correctamente; si falla, quedan s\xF3lo durante el TTL para reintento: nunca afirmes que una sesi\xF3n no expira. Para videos la miniatura es opcional y ProductMaker puede generarla con FFmpeg. `campaignConfig.destination` acepta landing o WhatsApp. Para landing basta `campaignConfig.destination.landingUrl`; no dupliques la URL arriba. Si omites `budgetMode`, se infiere `ad_set` cuando cada ad set trae `dailyBudget`, o `campaign` cuando env\xEDas `campaignDailyBudget`. No actives la campa\xF1a autom\xE1ticamente.', PublishCreativesToMetaInputBase.shape, async (rawInput) => {
|
|
11064
11085
|
try {
|
|
11065
11086
|
const parsed = PublishCreativesToMetaInput.safeParse(rawInput);
|
|
11066
11087
|
if (!parsed.success) throw invalidInputFromZod(parsed.error);
|
|
@@ -11321,8 +11342,7 @@ function uploadSessionText(result) {
|
|
|
11321
11342
|
text: [
|
|
11322
11343
|
`### Prepara tu ${mediaLabel} en ProductMaker`,
|
|
11323
11344
|
`Usa primero el App inline de este chat. Si tu host no soporta el bridge de archivos, usa este fallback: [Abrir carga segura](${result.uploadPageUrl})`,
|
|
11324
|
-
`Puedes seleccionar hasta ${result.maxFiles} ${result.mediaKind === "mixed" ? "archivos" : "archivo"}. Cuando
|
|
11325
|
-
`sessionId: \`${result.sessionId}\``,
|
|
11345
|
+
`Puedes seleccionar hasta ${result.maxFiles} ${result.mediaKind === "mixed" ? "archivos" : "archivo"}. Cuando termines, pulsa **OK** dentro del App para que ProductMaker contin\xFAe en este chat.`,
|
|
11326
11346
|
`Este enlace expira: ${result.expiresAt}`
|
|
11327
11347
|
].join("\n\n")
|
|
11328
11348
|
}
|
|
@@ -11333,7 +11353,7 @@ __name(uploadSessionText, "uploadSessionText");
|
|
|
11333
11353
|
function registerMetaUploadTools(server, api) {
|
|
11334
11354
|
server.registerTool("request_meta_media_upload", {
|
|
11335
11355
|
title: "Solicitar carga segura de Meta",
|
|
11336
|
-
description: "Inicia una carga segura desde la laptop del usuario para creativos propios de Meta. Por defecto crea una sesi\xF3n `mixed` de hasta diez archivos; tambi\xE9n admite una sola imagen o video. Si el usuario adjunta una foto o video en el mismo mensaje de ChatGPT, pasa esas referencias opcionales en `files` para que el MCP App inline las consuma dentro del chat. ProductMaker conserva `files` solo como metadata del UI bridge: NO los descarga y NO los reenv\xEDa. No pidas URL CDN, ruta local, bytes ni base64. El
|
|
11356
|
+
description: "Inicia una carga segura desde la laptop del usuario para creativos propios de Meta. Esta es la ruta DIRECTA para peticiones puntuales como \u2018sube esta imagen/video a Meta Ads\u2019 o \u2018publica este creativo\u2019; no conviertas esa intenci\xF3n en una generaci\xF3n completa de producto. No llames `guide_dropshipping_launch` para esta operaci\xF3n puntual. Por defecto crea una sesi\xF3n `mixed` de hasta diez archivos; tambi\xE9n admite una sola imagen o video. Si el usuario adjunta una foto o video en el mismo mensaje de ChatGPT, pasa esas referencias opcionales en `files` para que el MCP App inline las consuma dentro del chat. ProductMaker conserva `files` solo como metadata del UI bridge: NO los descarga y NO los reenv\xEDa. No pidas URL CDN, ruta local, bytes ni base64. El App es exclusivamente para cargar archivos: nunca debe pedir ni mostrar portafolio, cuenta publicitaria, p\xE1gina, IDs o JSON. El enlace externo de ProductMaker queda como fallback para hosts sin bridge de archivos. El usuario pulsa OK cuando termina; el App inserta en el chat los asset_id verificados para continuar la petici\xF3n anterior. Luego llama `list_meta_destinations` y pregunta en el chat qu\xE9 `portfolioId`, `adAccountId` y `pageId` desea usar. No muestres tokens, URLs temporales, `sessionId` ni un bloque JSON. Esta herramienta no publica ni consume cr\xE9ditos.",
|
|
11337
11357
|
inputSchema: MetaUploadSessionToolInputShape,
|
|
11338
11358
|
outputSchema: MetaUploadSessionResponse2,
|
|
11339
11359
|
_meta: {
|
|
@@ -11366,7 +11386,7 @@ function registerMetaUploadTools(server, api) {
|
|
|
11366
11386
|
}
|
|
11367
11387
|
});
|
|
11368
11388
|
server.registerTool("check_meta_media_upload", {
|
|
11369
|
-
description: 'Comprueba si el usuario ya termin\xF3 de subir desde el enlace de ProductMaker. Si status=uploaded,
|
|
11389
|
+
description: 'Comprueba si el usuario ya termin\xF3 de subir desde el App o enlace de ProductMaker. Si status=uploaded, conserva `assets[]` privadamente, llama `list_meta_destinations` y pregunta en lenguaje natural dentro del chat qu\xE9 portafolio, cuenta publicitaria y p\xE1gina desea usar; nunca muestres `sessionId`, `stagingAssetId`, IDs t\xE9cnicos ni un bloque JSON. Despu\xE9s crea un creativo `source.type="staged"` por cada asset, usando su `mediaKind` como tipo, hasta diez en el batch. Si la carga fue parcial, usa \xFAnicamente los assets confirmados. Conserva el `stagingAssetId` legado si aparece. Si status=pending, pide al usuario que termine; no publiques todav\xEDa.',
|
|
11370
11390
|
inputSchema: MetaUploadStatusInput.shape,
|
|
11371
11391
|
_meta: {
|
|
11372
11392
|
"openai/widgetAccessible": true
|
|
@@ -12321,20 +12341,21 @@ var CostingInputSchema = z59.object({
|
|
|
12321
12341
|
]
|
|
12322
12342
|
});
|
|
12323
12343
|
});
|
|
12344
|
+
var FiniteCostingNumber = z59.number().finite();
|
|
12324
12345
|
var CostingResultSchema = z59.object({
|
|
12325
12346
|
country: z59.string().min(2).max(3),
|
|
12326
|
-
deliveryRate:
|
|
12327
|
-
adjustedShippingCost:
|
|
12328
|
-
adjustedCpa:
|
|
12329
|
-
totalCost:
|
|
12330
|
-
suggestedPrice:
|
|
12331
|
-
baseCommercialPrice:
|
|
12332
|
-
minimumSafePrice:
|
|
12333
|
-
commercialPrice:
|
|
12334
|
-
comparisonPrice:
|
|
12335
|
-
estimatedProfit:
|
|
12336
|
-
actualMargin:
|
|
12337
|
-
breakEvenAdsManagerCpa:
|
|
12347
|
+
deliveryRate: FiniteCostingNumber,
|
|
12348
|
+
adjustedShippingCost: FiniteCostingNumber,
|
|
12349
|
+
adjustedCpa: FiniteCostingNumber,
|
|
12350
|
+
totalCost: FiniteCostingNumber,
|
|
12351
|
+
suggestedPrice: FiniteCostingNumber,
|
|
12352
|
+
baseCommercialPrice: FiniteCostingNumber,
|
|
12353
|
+
minimumSafePrice: FiniteCostingNumber,
|
|
12354
|
+
commercialPrice: FiniteCostingNumber,
|
|
12355
|
+
comparisonPrice: FiniteCostingNumber,
|
|
12356
|
+
estimatedProfit: FiniteCostingNumber,
|
|
12357
|
+
actualMargin: FiniteCostingNumber,
|
|
12358
|
+
breakEvenAdsManagerCpa: FiniteCostingNumber,
|
|
12338
12359
|
wasPriceAutoAdjusted: z59.boolean(),
|
|
12339
12360
|
profitabilityStatus: z59.literal("healthy")
|
|
12340
12361
|
});
|
|
@@ -12423,13 +12444,13 @@ function registerDropshippingProjectTools(server, api) {
|
|
|
12423
12444
|
throw translateError(error);
|
|
12424
12445
|
}
|
|
12425
12446
|
});
|
|
12426
|
-
server.tool("guide_dropshipping_launch", "Gu\xEDa read-only
|
|
12447
|
+
server.tool("guide_dropshipping_launch", "Gu\xEDa read-only de un recorrido COMPLETO de dropshipping, desde producto/costeo hasta creativos, tienda y publicidad. No uses este tool para subir o publicar un creativo puntual en Meta: llama directamente `request_meta_media_upload`, `list_meta_destinations` y `publish_creatives_to_meta`. Sin projectId explica cu\xE1ndo usar un flujo directo versus uno guiado; con projectId devuelve la fase y la \xFAnica nextAction calculada por ProductMaker. Despu\xE9s de cada mutaci\xF3n vuelve a llamar este tool o lee el proyecto. Nunca activa Meta.", GuideInput.shape, async (input) => {
|
|
12427
12448
|
try {
|
|
12428
12449
|
if (!input.projectId) {
|
|
12429
12450
|
return asText({
|
|
12430
12451
|
mode: "choose",
|
|
12431
12452
|
guided: "Usa start_dropshipping_project cuando el usuario quiera encontrar un producto y dejarlo listo para publicar.",
|
|
12432
|
-
direct: "En modo directo usa
|
|
12453
|
+
direct: "En modo directo usa la herramienta puntual. Para subir/publicar un creativo propio en Meta: request_meta_media_upload \u2192 list_meta_destinations \u2192 publish_creatives_to_meta. No crees un proyecto guiado.",
|
|
12433
12454
|
confirmation: "Antes de acciones pagadas o externas, muestra el estimado y pide confirmaci\xF3n.",
|
|
12434
12455
|
safety: "Las campa\xF1as Meta quedan PAUSED; el usuario las activa manualmente en Ads Manager."
|
|
12435
12456
|
});
|