@productmaker/mcp 3.4.2 → 3.4.3
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 +112 -25
- package/dist/stdio.js +112 -25
- package/package.json +1 -1
package/dist/http.js
CHANGED
|
@@ -4901,6 +4901,7 @@ var MetaCampaignType = z33.enum([
|
|
|
4901
4901
|
"traffic",
|
|
4902
4902
|
"engagement"
|
|
4903
4903
|
]);
|
|
4904
|
+
var MetaPublishAttemptId = z33.string().uuid();
|
|
4904
4905
|
var MetaOptimizationMode = z33.enum([
|
|
4905
4906
|
"advantage_plus",
|
|
4906
4907
|
"manual"
|
|
@@ -5084,6 +5085,8 @@ var MetaCampaignDefaults = MetaCampaignConfigBase.omit({
|
|
|
5084
5085
|
currency: z33.string().length(3).regex(/^[A-Z]{3}$/).nullable().optional()
|
|
5085
5086
|
});
|
|
5086
5087
|
var UploadToMetaRequest = z33.object({
|
|
5088
|
+
/** Caller-owned retry fence. Required before any provider operation. */
|
|
5089
|
+
publishAttemptId: MetaPublishAttemptId,
|
|
5087
5090
|
/** Array of creatives to upload */
|
|
5088
5091
|
creatives: z33.array(z33.object({
|
|
5089
5092
|
creativeId: z33.string().min(1).optional(),
|
|
@@ -5240,6 +5243,8 @@ var MetaCampaignOverridesSchema = z34.object({
|
|
|
5240
5243
|
destination: MetaCampaignDestination.optional()
|
|
5241
5244
|
});
|
|
5242
5245
|
var FacebookCreateCampaignBodySchema = z34.object({
|
|
5246
|
+
/** Caller-owned retry fence. Required before any provider operation. */
|
|
5247
|
+
publishAttemptId: MetaPublishAttemptId,
|
|
5243
5248
|
adAccountId: MetaAdAccountId,
|
|
5244
5249
|
portfolioId: z34.string().min(1).optional(),
|
|
5245
5250
|
pageId: z34.string().min(1).optional(),
|
|
@@ -5309,6 +5314,8 @@ var FacebookCreateCampaignResponseSchema = z34.object({
|
|
|
5309
5314
|
appliedBudget: z34.number().int().positive()
|
|
5310
5315
|
});
|
|
5311
5316
|
var FacebookCreateCampaignFromCreativesBodySchema = z34.object({
|
|
5317
|
+
/** Caller-owned retry fence. Required before any provider operation. */
|
|
5318
|
+
publishAttemptId: MetaPublishAttemptId,
|
|
5312
5319
|
creatives: z34.array(z34.object({
|
|
5313
5320
|
type: z34.enum([
|
|
5314
5321
|
"image",
|
|
@@ -5456,6 +5463,8 @@ var FacebookCreativeSourceSchema = z34.discriminatedUnion("type", [
|
|
|
5456
5463
|
FacebookMediaCreativeSourceSchema
|
|
5457
5464
|
]);
|
|
5458
5465
|
var FacebookCreateCampaignFromTasksBodySchema = z34.object({
|
|
5466
|
+
/** Caller-owned retry fence. Required before any provider operation. */
|
|
5467
|
+
publishAttemptId: MetaPublishAttemptId,
|
|
5459
5468
|
creatives: z34.array(z34.object({
|
|
5460
5469
|
type: z34.enum([
|
|
5461
5470
|
"image",
|
|
@@ -11678,7 +11687,7 @@ var ApiClient = class {
|
|
|
11678
11687
|
};
|
|
11679
11688
|
|
|
11680
11689
|
// src/index.ts
|
|
11681
|
-
var MCP_VERSION = "3.4.
|
|
11690
|
+
var MCP_VERSION = "3.4.3";
|
|
11682
11691
|
|
|
11683
11692
|
// src/meta-upload-app.ts
|
|
11684
11693
|
import { readFileSync } from "fs";
|
|
@@ -12724,6 +12733,12 @@ var McpErrorCode = {
|
|
|
12724
12733
|
META_STAGED_ASSET_AMBIGUOUS: "META_STAGED_ASSET_AMBIGUOUS",
|
|
12725
12734
|
META_STAGED_ASSET_TYPE_MISMATCH: "META_STAGED_ASSET_TYPE_MISMATCH",
|
|
12726
12735
|
META_STAGED_ASSET_INVALID: "META_STAGED_ASSET_INVALID",
|
|
12736
|
+
META_STAGING_QUOTA_EXCEEDED: "META_STAGING_QUOTA_EXCEEDED",
|
|
12737
|
+
CAPACITY_UNAVAILABLE: "CAPACITY_UNAVAILABLE",
|
|
12738
|
+
UPLOAD_INCOMPLETE: "UPLOAD_INCOMPLETE",
|
|
12739
|
+
UPLOAD_EXPIRED: "UPLOAD_EXPIRED",
|
|
12740
|
+
META_UPLOAD_SESSION_EXPIRED: "META_UPLOAD_SESSION_EXPIRED",
|
|
12741
|
+
META_PUBLICATION_LEASE_STALE: "META_PUBLICATION_LEASE_STALE",
|
|
12727
12742
|
META_PUBLICATION_RECONCILIATION_REQUIRED: "META_PUBLICATION_RECONCILIATION_REQUIRED",
|
|
12728
12743
|
META_PUBLICATION_IN_PROGRESS: "META_PUBLICATION_IN_PROGRESS",
|
|
12729
12744
|
META_NO_ADS_CREATED: "META_NO_ADS_CREATED",
|
|
@@ -12785,8 +12800,14 @@ var CODE_MESSAGES = {
|
|
|
12785
12800
|
META_STAGED_ASSET_AMBIGUOUS: "The temporary media upload could not be uniquely resolved. Re-upload the media and retry the Meta publication.",
|
|
12786
12801
|
META_STAGED_ASSET_TYPE_MISMATCH: "The temporary media type does not match the creative. Re-upload the correct image or video and retry.",
|
|
12787
12802
|
META_STAGED_ASSET_INVALID: "The temporary media is empty or exceeds the server size limit. Re-upload the file and retry.",
|
|
12788
|
-
|
|
12789
|
-
|
|
12803
|
+
META_STAGING_QUOTA_EXCEEDED: "The temporary Meta staging quota was reached. Keep completed files and retry only pending files after retryAt.",
|
|
12804
|
+
CAPACITY_UNAVAILABLE: "Temporary Meta staging capacity is unavailable. Keep completed files and retry only pending files later.",
|
|
12805
|
+
UPLOAD_INCOMPLETE: "A temporary Meta upload is incomplete. Keep completed files and retry only pending files.",
|
|
12806
|
+
UPLOAD_EXPIRED: "The temporary Meta upload expired. Re-upload this file before publishing.",
|
|
12807
|
+
META_UPLOAD_SESSION_EXPIRED: "The Meta upload session expired. Open a new session and retry only pending files.",
|
|
12808
|
+
META_PUBLICATION_LEASE_STALE: "The Meta publication lease expired or was replaced. Do not continue this stale publication; start a new explicit attempt only after confirming Meta did not receive it.",
|
|
12809
|
+
META_PUBLICATION_RECONCILIATION_REQUIRED: "Meta may already have received this publication. Do not resend it; check Meta Ads Manager.",
|
|
12810
|
+
META_PUBLICATION_IN_PROGRESS: "This Meta publication is already in progress. Do not resend it.",
|
|
12790
12811
|
META_NO_ADS_CREATED: "No ads could be created. Most likely: the Meta Ads account has no payment method, no Facebook Page is linked, or the Facebook app lost permissions. Verify all three at https://business.facebook.com.",
|
|
12791
12812
|
META_NO_ASSETS_READY: "The task has no videos or ad creative ready to publish yet. Wait for generation to finish or regenerate the assets.",
|
|
12792
12813
|
META_NO_THUMBNAIL_SOURCE: "No image is available to use as the video thumbnail. Upload a product image or generate the ad creative before publishing.",
|
|
@@ -12935,6 +12956,11 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
|
|
|
12935
12956
|
McpErrorCode.META_STAGED_ASSET_AMBIGUOUS,
|
|
12936
12957
|
McpErrorCode.META_STAGED_ASSET_TYPE_MISMATCH,
|
|
12937
12958
|
McpErrorCode.META_STAGED_ASSET_INVALID,
|
|
12959
|
+
McpErrorCode.META_STAGING_QUOTA_EXCEEDED,
|
|
12960
|
+
McpErrorCode.UPLOAD_INCOMPLETE,
|
|
12961
|
+
McpErrorCode.UPLOAD_EXPIRED,
|
|
12962
|
+
McpErrorCode.META_UPLOAD_SESSION_EXPIRED,
|
|
12963
|
+
McpErrorCode.META_PUBLICATION_LEASE_STALE,
|
|
12938
12964
|
McpErrorCode.META_PUBLICATION_RECONCILIATION_REQUIRED,
|
|
12939
12965
|
McpErrorCode.META_PUBLICATION_IN_PROGRESS,
|
|
12940
12966
|
McpErrorCode.TIKTOK_TOKEN_EXPIRED,
|
|
@@ -12994,6 +13020,14 @@ var PASSTHROUGH_CODES = /* @__PURE__ */ new Set([
|
|
|
12994
13020
|
McpErrorCode.META_CAMPAIGN_CONFIG_INVALID,
|
|
12995
13021
|
McpErrorCode.META_WHATSAPP_INVALID_NUMBER,
|
|
12996
13022
|
McpErrorCode.META_INVALID_CTA,
|
|
13023
|
+
McpErrorCode.META_STAGING_QUOTA_EXCEEDED,
|
|
13024
|
+
McpErrorCode.CAPACITY_UNAVAILABLE,
|
|
13025
|
+
McpErrorCode.UPLOAD_INCOMPLETE,
|
|
13026
|
+
McpErrorCode.UPLOAD_EXPIRED,
|
|
13027
|
+
McpErrorCode.META_UPLOAD_SESSION_EXPIRED,
|
|
13028
|
+
McpErrorCode.META_PUBLICATION_LEASE_STALE,
|
|
13029
|
+
McpErrorCode.META_PUBLICATION_RECONCILIATION_REQUIRED,
|
|
13030
|
+
McpErrorCode.META_PUBLICATION_IN_PROGRESS,
|
|
12997
13031
|
// TikTok
|
|
12998
13032
|
McpErrorCode.TIKTOK_NOT_CONNECTED,
|
|
12999
13033
|
McpErrorCode.TIKTOK_TOKEN_EXPIRED,
|
|
@@ -13082,6 +13116,10 @@ function translateError(err) {
|
|
|
13082
13116
|
if (body.helpUrl !== void 0) extra.helpUrl = body.helpUrl;
|
|
13083
13117
|
if (typeof body.requiredCredits === "number") extra.requiredCredits = body.requiredCredits;
|
|
13084
13118
|
if (typeof body.availableCredits === "number") extra.availableCredits = body.availableCredits;
|
|
13119
|
+
if (typeof body.dimension === "string") extra.dimension = body.dimension;
|
|
13120
|
+
if (typeof body.usage === "number") extra.usage = body.usage;
|
|
13121
|
+
if (typeof body.limit === "number") extra.limit = body.limit;
|
|
13122
|
+
if (typeof body.retryAt === "string") extra.retryAt = body.retryAt;
|
|
13085
13123
|
return mcp(code, resolveMessage(code, body.message), extra);
|
|
13086
13124
|
}
|
|
13087
13125
|
if (err.status === 401) {
|
|
@@ -14755,6 +14793,57 @@ __name(registerActorTools, "registerActorTools");
|
|
|
14755
14793
|
|
|
14756
14794
|
// src/tools/publish.ts
|
|
14757
14795
|
import { z as z56 } from "zod";
|
|
14796
|
+
|
|
14797
|
+
// src/tools/stable-uuid.ts
|
|
14798
|
+
import { createHash as createHash3 } from "crypto";
|
|
14799
|
+
function canonicalize(value) {
|
|
14800
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
14801
|
+
if (value === null || typeof value !== "object") return value;
|
|
14802
|
+
const record = value;
|
|
14803
|
+
return Object.fromEntries(Object.keys(record).sort().filter((key) => record[key] !== void 0).map((key) => [
|
|
14804
|
+
key,
|
|
14805
|
+
canonicalize(record[key])
|
|
14806
|
+
]));
|
|
14807
|
+
}
|
|
14808
|
+
__name(canonicalize, "canonicalize");
|
|
14809
|
+
function canonicalJson(value) {
|
|
14810
|
+
return JSON.stringify(canonicalize(value));
|
|
14811
|
+
}
|
|
14812
|
+
__name(canonicalJson, "canonicalJson");
|
|
14813
|
+
function deriveUuidV5(name, namespace) {
|
|
14814
|
+
const namespaceBytes = Buffer.from(namespace.replaceAll("-", ""), "hex");
|
|
14815
|
+
const digest = createHash3("sha1").update(namespaceBytes).update(name, "utf8").digest();
|
|
14816
|
+
const bytes = Buffer.from(digest.subarray(0, 16));
|
|
14817
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 80;
|
|
14818
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
14819
|
+
const hex = bytes.toString("hex");
|
|
14820
|
+
return [
|
|
14821
|
+
hex.slice(0, 8),
|
|
14822
|
+
hex.slice(8, 12),
|
|
14823
|
+
hex.slice(12, 16),
|
|
14824
|
+
hex.slice(16, 20),
|
|
14825
|
+
hex.slice(20)
|
|
14826
|
+
].join("-");
|
|
14827
|
+
}
|
|
14828
|
+
__name(deriveUuidV5, "deriveUuidV5");
|
|
14829
|
+
|
|
14830
|
+
// src/tools/meta-publish-attempt-id.ts
|
|
14831
|
+
var META_PUBLISH_NAMESPACE = "7b19c9ac-b4eb-4db1-970e-c2f24ce30b98";
|
|
14832
|
+
function deriveMetaPublishAttemptId(input) {
|
|
14833
|
+
const operationIdentity = canonicalJson([
|
|
14834
|
+
input.fingerprint,
|
|
14835
|
+
input.toolName,
|
|
14836
|
+
[
|
|
14837
|
+
typeof input.requestId,
|
|
14838
|
+
input.requestId
|
|
14839
|
+
],
|
|
14840
|
+
input.canonicalInput
|
|
14841
|
+
]);
|
|
14842
|
+
return deriveUuidV5(operationIdentity, META_PUBLISH_NAMESPACE);
|
|
14843
|
+
}
|
|
14844
|
+
__name(deriveMetaPublishAttemptId, "deriveMetaPublishAttemptId");
|
|
14845
|
+
|
|
14846
|
+
// src/tools/publish.ts
|
|
14758
14847
|
var ShopifyInput = z56.object({
|
|
14759
14848
|
taskId: z56.string().min(1),
|
|
14760
14849
|
shopDomain: z56.string().min(1),
|
|
@@ -15082,10 +15171,19 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
15082
15171
|
throw translateError(e);
|
|
15083
15172
|
}
|
|
15084
15173
|
});
|
|
15085
|
-
server.tool("publish_to_meta", "Crea una campa\xF1a de Meta Ads (Facebook/Instagram) desde UNA sola tarea. Antes llama `list_meta_destinations`, deja que el usuario elija el `portfolioId` (Business portfolio), el `adAccountId` y el `pageId` exactos, y env\xEDa esos tres valores; no adivines ni uses la primera p\xE1gina. Si quieres juntar creativos de VARIAS tareas usa `publish_creatives_to_meta`. La campa\xF1a se crea SIEMPRE PAUSED y el usuario la activa manualmente. `pixelId` es opcional. `angleIndexes` permite seleccionar \xE1ngulos. `campaignConfig` admite presupuesto en USD, pa\xEDses ISO-2 y nombre. `primaryText` permite reemplazar el copy y `phoneNumber` configura WhatsApp. Despu\xE9s usa `get_meta_campaign_status`.", MetaInput.shape, CREATES_EXTERNAL, async (
|
|
15174
|
+
server.tool("publish_to_meta", "Publica tus creativos en Meta sin gastar cr\xE9ditos de ProductMaker. El presupuesto de anuncios se paga directamente desde tu cuenta de Meta. Crear creativos o copy con IA sigue usando cr\xE9ditos. Crea una campa\xF1a de Meta Ads (Facebook/Instagram) desde UNA sola tarea. Antes llama `list_meta_destinations`, deja que el usuario elija el `portfolioId` (Business portfolio), el `adAccountId` y el `pageId` exactos, y env\xEDa esos tres valores; no adivines ni uses la primera p\xE1gina. Si quieres juntar creativos de VARIAS tareas usa `publish_creatives_to_meta`. La campa\xF1a se crea SIEMPRE PAUSED y el usuario la activa manualmente. `pixelId` es opcional. `angleIndexes` permite seleccionar \xE1ngulos. `campaignConfig` admite presupuesto en USD, pa\xEDses ISO-2 y nombre. `primaryText` permite reemplazar el copy y `phoneNumber` configura WhatsApp. Despu\xE9s usa `get_meta_campaign_status`. Si el resultado es ambiguo, NO reenv\xEDes: revisa Meta Ads Manager.", MetaInput.shape, CREATES_EXTERNAL, async (rawInput, extra) => {
|
|
15086
15175
|
try {
|
|
15176
|
+
const parsed = MetaInput.safeParse(rawInput);
|
|
15177
|
+
if (!parsed.success) throw invalidInputFromZod(parsed.error);
|
|
15178
|
+
const { taskId, campaignConfig, primaryText, phoneNumber, ...rest } = parsed.data;
|
|
15087
15179
|
const body = {
|
|
15088
|
-
...rest
|
|
15180
|
+
...rest,
|
|
15181
|
+
publishAttemptId: deriveMetaPublishAttemptId({
|
|
15182
|
+
fingerprint: api.fingerprint(),
|
|
15183
|
+
toolName: "publish_to_meta",
|
|
15184
|
+
requestId: extra.requestId,
|
|
15185
|
+
canonicalInput: parsed.data
|
|
15186
|
+
})
|
|
15089
15187
|
};
|
|
15090
15188
|
if (campaignConfig) {
|
|
15091
15189
|
body.campaignOverrides = toMetaCampaignOverrides(campaignConfig);
|
|
@@ -15110,7 +15208,7 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
15110
15208
|
throw translateError(e);
|
|
15111
15209
|
}
|
|
15112
15210
|
});
|
|
15113
|
-
server.tool("publish_creatives_to_meta", "Crea UNA campa\xF1a de Meta Ads PAUSED agrupando hasta " + CAMPAIGN_LIMITS.MAX_CREATIVES + ' creativos. Si la petici\xF3n natural adjunta o menciona archivos locales, llama primero `request_media_upload`; tras OK usa cada resultado como `source.type="asset"` con su `assetId` y el `type` que coincide con `mediaKind`. No pidas path ni URL. Despu\xE9s llama `list_meta_destinations` y pregunta en el chat por el `portfolioId`, `adAccountId` y `pageId` exactos; el App es solo para archivos. Si la carga fue parcial,
|
|
15211
|
+
server.tool("publish_creatives_to_meta", "Crea UNA campa\xF1a de Meta Ads PAUSED agrupando hasta " + CAMPAIGN_LIMITS.MAX_CREATIVES + ' creativos. Publica tus creativos en Meta sin gastar cr\xE9ditos de ProductMaker. El presupuesto de anuncios se paga directamente desde tu cuenta de Meta. Crear creativos o copy con IA sigue usando cr\xE9ditos. Si la petici\xF3n natural adjunta o menciona archivos locales, llama primero `request_media_upload`; tras OK usa cada resultado como `source.type="asset"` con su `assetId` y el `type` que coincide con `mediaKind`. No pidas path ni URL. Despu\xE9s llama `list_meta_destinations` y pregunta en el chat por el `portfolioId`, `adAccountId` y `pageId` exactos; el App es solo para archivos. Si la carga fue parcial, conserva los assets confirmados, procesa s\xF3lo los pendientes y publica \xFAnicamente assets confirmados; nunca inventes assets ni superes diez creativos. Las rutas locales s\xF3lo funcionan con MCP stdio. Los medios propios se eliminan despu\xE9s de publicar correctamente; si falla antes de enviar una mutaci\xF3n a Meta, quedan durante el TTL. 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. Si el resultado es ambiguo, NO reenv\xEDes: revisa Meta Ads Manager.', PublishCreativesToMetaInputBase.shape, CREATES_EXTERNAL, async (rawInput, extra) => {
|
|
15114
15212
|
try {
|
|
15115
15213
|
const parsed = PublishCreativesToMetaInput.safeParse(rawInput);
|
|
15116
15214
|
if (!parsed.success) throw invalidInputFromZod(parsed.error);
|
|
@@ -15190,7 +15288,13 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
15190
15288
|
landingUrl: input.landingUrl,
|
|
15191
15289
|
targetCountry: input.targetCountry,
|
|
15192
15290
|
campaignName: input.campaignName,
|
|
15193
|
-
campaignConfig: input.campaignConfig
|
|
15291
|
+
campaignConfig: input.campaignConfig,
|
|
15292
|
+
publishAttemptId: deriveMetaPublishAttemptId({
|
|
15293
|
+
fingerprint: api.fingerprint(),
|
|
15294
|
+
toolName: "publish_creatives_to_meta",
|
|
15295
|
+
requestId: extra.requestId,
|
|
15296
|
+
canonicalInput: input
|
|
15297
|
+
})
|
|
15194
15298
|
});
|
|
15195
15299
|
return asText({
|
|
15196
15300
|
...r,
|
|
@@ -16077,29 +16181,12 @@ __name(formatPercent, "formatPercent");
|
|
|
16077
16181
|
import { z as z62 } from "zod";
|
|
16078
16182
|
|
|
16079
16183
|
// src/tools/product-research-idempotency.ts
|
|
16080
|
-
import { createHash as createHash3 } from "crypto";
|
|
16081
16184
|
var RESEARCH_IDEMPOTENCY_WINDOW_MS = 2 * 60 * 60 * 1e3;
|
|
16082
16185
|
var NAMESPACE = "6f9d3a1e-8c47-4b52-9f10-2d7e5a3c8b64";
|
|
16083
|
-
function uuidV5(name, namespace) {
|
|
16084
|
-
const namespaceBytes = Buffer.from(namespace.replace(/-/g, ""), "hex");
|
|
16085
|
-
const digest = createHash3("sha1").update(namespaceBytes).update(name, "utf8").digest();
|
|
16086
|
-
const bytes = Buffer.from(digest.subarray(0, 16));
|
|
16087
|
-
bytes[6] = (bytes[6] ?? 0) & 15 | 80;
|
|
16088
|
-
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
16089
|
-
const hex = bytes.toString("hex");
|
|
16090
|
-
return [
|
|
16091
|
-
hex.slice(0, 8),
|
|
16092
|
-
hex.slice(8, 12),
|
|
16093
|
-
hex.slice(12, 16),
|
|
16094
|
-
hex.slice(16, 20),
|
|
16095
|
-
hex.slice(20)
|
|
16096
|
-
].join("-");
|
|
16097
|
-
}
|
|
16098
|
-
__name(uuidV5, "uuidV5");
|
|
16099
16186
|
function deriveIdempotencyKey(input) {
|
|
16100
16187
|
const window = Math.floor(input.nowMs / RESEARCH_IDEMPOTENCY_WINDOW_MS);
|
|
16101
16188
|
const name = `${input.fingerprint}:${input.targetCountry}:${input.categoryKey}:${window}`;
|
|
16102
|
-
return
|
|
16189
|
+
return deriveUuidV5(name, NAMESPACE);
|
|
16103
16190
|
}
|
|
16104
16191
|
__name(deriveIdempotencyKey, "deriveIdempotencyKey");
|
|
16105
16192
|
|
package/dist/stdio.js
CHANGED
|
@@ -264,7 +264,7 @@ var ApiClient = class {
|
|
|
264
264
|
};
|
|
265
265
|
|
|
266
266
|
// src/index.ts
|
|
267
|
-
var MCP_VERSION = "3.4.
|
|
267
|
+
var MCP_VERSION = "3.4.3";
|
|
268
268
|
|
|
269
269
|
// src/meta-upload-app.ts
|
|
270
270
|
import { readFileSync } from "fs";
|
|
@@ -5523,6 +5523,7 @@ var MetaCampaignType = z33.enum([
|
|
|
5523
5523
|
"traffic",
|
|
5524
5524
|
"engagement"
|
|
5525
5525
|
]);
|
|
5526
|
+
var MetaPublishAttemptId = z33.string().uuid();
|
|
5526
5527
|
var MetaOptimizationMode = z33.enum([
|
|
5527
5528
|
"advantage_plus",
|
|
5528
5529
|
"manual"
|
|
@@ -5706,6 +5707,8 @@ var MetaCampaignDefaults = MetaCampaignConfigBase.omit({
|
|
|
5706
5707
|
currency: z33.string().length(3).regex(/^[A-Z]{3}$/).nullable().optional()
|
|
5707
5708
|
});
|
|
5708
5709
|
var UploadToMetaRequest = z33.object({
|
|
5710
|
+
/** Caller-owned retry fence. Required before any provider operation. */
|
|
5711
|
+
publishAttemptId: MetaPublishAttemptId,
|
|
5709
5712
|
/** Array of creatives to upload */
|
|
5710
5713
|
creatives: z33.array(z33.object({
|
|
5711
5714
|
creativeId: z33.string().min(1).optional(),
|
|
@@ -5862,6 +5865,8 @@ var MetaCampaignOverridesSchema = z34.object({
|
|
|
5862
5865
|
destination: MetaCampaignDestination.optional()
|
|
5863
5866
|
});
|
|
5864
5867
|
var FacebookCreateCampaignBodySchema = z34.object({
|
|
5868
|
+
/** Caller-owned retry fence. Required before any provider operation. */
|
|
5869
|
+
publishAttemptId: MetaPublishAttemptId,
|
|
5865
5870
|
adAccountId: MetaAdAccountId,
|
|
5866
5871
|
portfolioId: z34.string().min(1).optional(),
|
|
5867
5872
|
pageId: z34.string().min(1).optional(),
|
|
@@ -5931,6 +5936,8 @@ var FacebookCreateCampaignResponseSchema = z34.object({
|
|
|
5931
5936
|
appliedBudget: z34.number().int().positive()
|
|
5932
5937
|
});
|
|
5933
5938
|
var FacebookCreateCampaignFromCreativesBodySchema = z34.object({
|
|
5939
|
+
/** Caller-owned retry fence. Required before any provider operation. */
|
|
5940
|
+
publishAttemptId: MetaPublishAttemptId,
|
|
5934
5941
|
creatives: z34.array(z34.object({
|
|
5935
5942
|
type: z34.enum([
|
|
5936
5943
|
"image",
|
|
@@ -6078,6 +6085,8 @@ var FacebookCreativeSourceSchema = z34.discriminatedUnion("type", [
|
|
|
6078
6085
|
FacebookMediaCreativeSourceSchema
|
|
6079
6086
|
]);
|
|
6080
6087
|
var FacebookCreateCampaignFromTasksBodySchema = z34.object({
|
|
6088
|
+
/** Caller-owned retry fence. Required before any provider operation. */
|
|
6089
|
+
publishAttemptId: MetaPublishAttemptId,
|
|
6081
6090
|
creatives: z34.array(z34.object({
|
|
6082
6091
|
type: z34.enum([
|
|
6083
6092
|
"image",
|
|
@@ -11473,6 +11482,12 @@ var McpErrorCode = {
|
|
|
11473
11482
|
META_STAGED_ASSET_AMBIGUOUS: "META_STAGED_ASSET_AMBIGUOUS",
|
|
11474
11483
|
META_STAGED_ASSET_TYPE_MISMATCH: "META_STAGED_ASSET_TYPE_MISMATCH",
|
|
11475
11484
|
META_STAGED_ASSET_INVALID: "META_STAGED_ASSET_INVALID",
|
|
11485
|
+
META_STAGING_QUOTA_EXCEEDED: "META_STAGING_QUOTA_EXCEEDED",
|
|
11486
|
+
CAPACITY_UNAVAILABLE: "CAPACITY_UNAVAILABLE",
|
|
11487
|
+
UPLOAD_INCOMPLETE: "UPLOAD_INCOMPLETE",
|
|
11488
|
+
UPLOAD_EXPIRED: "UPLOAD_EXPIRED",
|
|
11489
|
+
META_UPLOAD_SESSION_EXPIRED: "META_UPLOAD_SESSION_EXPIRED",
|
|
11490
|
+
META_PUBLICATION_LEASE_STALE: "META_PUBLICATION_LEASE_STALE",
|
|
11476
11491
|
META_PUBLICATION_RECONCILIATION_REQUIRED: "META_PUBLICATION_RECONCILIATION_REQUIRED",
|
|
11477
11492
|
META_PUBLICATION_IN_PROGRESS: "META_PUBLICATION_IN_PROGRESS",
|
|
11478
11493
|
META_NO_ADS_CREATED: "META_NO_ADS_CREATED",
|
|
@@ -11534,8 +11549,14 @@ var CODE_MESSAGES = {
|
|
|
11534
11549
|
META_STAGED_ASSET_AMBIGUOUS: "The temporary media upload could not be uniquely resolved. Re-upload the media and retry the Meta publication.",
|
|
11535
11550
|
META_STAGED_ASSET_TYPE_MISMATCH: "The temporary media type does not match the creative. Re-upload the correct image or video and retry.",
|
|
11536
11551
|
META_STAGED_ASSET_INVALID: "The temporary media is empty or exceeds the server size limit. Re-upload the file and retry.",
|
|
11537
|
-
|
|
11538
|
-
|
|
11552
|
+
META_STAGING_QUOTA_EXCEEDED: "The temporary Meta staging quota was reached. Keep completed files and retry only pending files after retryAt.",
|
|
11553
|
+
CAPACITY_UNAVAILABLE: "Temporary Meta staging capacity is unavailable. Keep completed files and retry only pending files later.",
|
|
11554
|
+
UPLOAD_INCOMPLETE: "A temporary Meta upload is incomplete. Keep completed files and retry only pending files.",
|
|
11555
|
+
UPLOAD_EXPIRED: "The temporary Meta upload expired. Re-upload this file before publishing.",
|
|
11556
|
+
META_UPLOAD_SESSION_EXPIRED: "The Meta upload session expired. Open a new session and retry only pending files.",
|
|
11557
|
+
META_PUBLICATION_LEASE_STALE: "The Meta publication lease expired or was replaced. Do not continue this stale publication; start a new explicit attempt only after confirming Meta did not receive it.",
|
|
11558
|
+
META_PUBLICATION_RECONCILIATION_REQUIRED: "Meta may already have received this publication. Do not resend it; check Meta Ads Manager.",
|
|
11559
|
+
META_PUBLICATION_IN_PROGRESS: "This Meta publication is already in progress. Do not resend it.",
|
|
11539
11560
|
META_NO_ADS_CREATED: "No ads could be created. Most likely: the Meta Ads account has no payment method, no Facebook Page is linked, or the Facebook app lost permissions. Verify all three at https://business.facebook.com.",
|
|
11540
11561
|
META_NO_ASSETS_READY: "The task has no videos or ad creative ready to publish yet. Wait for generation to finish or regenerate the assets.",
|
|
11541
11562
|
META_NO_THUMBNAIL_SOURCE: "No image is available to use as the video thumbnail. Upload a product image or generate the ad creative before publishing.",
|
|
@@ -11684,6 +11705,11 @@ var INVALID_PARAM_CODES = /* @__PURE__ */ new Set([
|
|
|
11684
11705
|
McpErrorCode.META_STAGED_ASSET_AMBIGUOUS,
|
|
11685
11706
|
McpErrorCode.META_STAGED_ASSET_TYPE_MISMATCH,
|
|
11686
11707
|
McpErrorCode.META_STAGED_ASSET_INVALID,
|
|
11708
|
+
McpErrorCode.META_STAGING_QUOTA_EXCEEDED,
|
|
11709
|
+
McpErrorCode.UPLOAD_INCOMPLETE,
|
|
11710
|
+
McpErrorCode.UPLOAD_EXPIRED,
|
|
11711
|
+
McpErrorCode.META_UPLOAD_SESSION_EXPIRED,
|
|
11712
|
+
McpErrorCode.META_PUBLICATION_LEASE_STALE,
|
|
11687
11713
|
McpErrorCode.META_PUBLICATION_RECONCILIATION_REQUIRED,
|
|
11688
11714
|
McpErrorCode.META_PUBLICATION_IN_PROGRESS,
|
|
11689
11715
|
McpErrorCode.TIKTOK_TOKEN_EXPIRED,
|
|
@@ -11743,6 +11769,14 @@ var PASSTHROUGH_CODES = /* @__PURE__ */ new Set([
|
|
|
11743
11769
|
McpErrorCode.META_CAMPAIGN_CONFIG_INVALID,
|
|
11744
11770
|
McpErrorCode.META_WHATSAPP_INVALID_NUMBER,
|
|
11745
11771
|
McpErrorCode.META_INVALID_CTA,
|
|
11772
|
+
McpErrorCode.META_STAGING_QUOTA_EXCEEDED,
|
|
11773
|
+
McpErrorCode.CAPACITY_UNAVAILABLE,
|
|
11774
|
+
McpErrorCode.UPLOAD_INCOMPLETE,
|
|
11775
|
+
McpErrorCode.UPLOAD_EXPIRED,
|
|
11776
|
+
McpErrorCode.META_UPLOAD_SESSION_EXPIRED,
|
|
11777
|
+
McpErrorCode.META_PUBLICATION_LEASE_STALE,
|
|
11778
|
+
McpErrorCode.META_PUBLICATION_RECONCILIATION_REQUIRED,
|
|
11779
|
+
McpErrorCode.META_PUBLICATION_IN_PROGRESS,
|
|
11746
11780
|
// TikTok
|
|
11747
11781
|
McpErrorCode.TIKTOK_NOT_CONNECTED,
|
|
11748
11782
|
McpErrorCode.TIKTOK_TOKEN_EXPIRED,
|
|
@@ -11831,6 +11865,10 @@ function translateError(err) {
|
|
|
11831
11865
|
if (body.helpUrl !== void 0) extra.helpUrl = body.helpUrl;
|
|
11832
11866
|
if (typeof body.requiredCredits === "number") extra.requiredCredits = body.requiredCredits;
|
|
11833
11867
|
if (typeof body.availableCredits === "number") extra.availableCredits = body.availableCredits;
|
|
11868
|
+
if (typeof body.dimension === "string") extra.dimension = body.dimension;
|
|
11869
|
+
if (typeof body.usage === "number") extra.usage = body.usage;
|
|
11870
|
+
if (typeof body.limit === "number") extra.limit = body.limit;
|
|
11871
|
+
if (typeof body.retryAt === "string") extra.retryAt = body.retryAt;
|
|
11834
11872
|
return mcp(code, resolveMessage(code, body.message), extra);
|
|
11835
11873
|
}
|
|
11836
11874
|
if (err.status === 401) {
|
|
@@ -13557,6 +13595,57 @@ __name(registerActorTools, "registerActorTools");
|
|
|
13557
13595
|
|
|
13558
13596
|
// src/tools/publish.ts
|
|
13559
13597
|
import { z as z55 } from "zod";
|
|
13598
|
+
|
|
13599
|
+
// src/tools/stable-uuid.ts
|
|
13600
|
+
import { createHash as createHash2 } from "crypto";
|
|
13601
|
+
function canonicalize(value) {
|
|
13602
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
13603
|
+
if (value === null || typeof value !== "object") return value;
|
|
13604
|
+
const record = value;
|
|
13605
|
+
return Object.fromEntries(Object.keys(record).sort().filter((key) => record[key] !== void 0).map((key) => [
|
|
13606
|
+
key,
|
|
13607
|
+
canonicalize(record[key])
|
|
13608
|
+
]));
|
|
13609
|
+
}
|
|
13610
|
+
__name(canonicalize, "canonicalize");
|
|
13611
|
+
function canonicalJson(value) {
|
|
13612
|
+
return JSON.stringify(canonicalize(value));
|
|
13613
|
+
}
|
|
13614
|
+
__name(canonicalJson, "canonicalJson");
|
|
13615
|
+
function deriveUuidV5(name, namespace) {
|
|
13616
|
+
const namespaceBytes = Buffer.from(namespace.replaceAll("-", ""), "hex");
|
|
13617
|
+
const digest = createHash2("sha1").update(namespaceBytes).update(name, "utf8").digest();
|
|
13618
|
+
const bytes = Buffer.from(digest.subarray(0, 16));
|
|
13619
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 80;
|
|
13620
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
13621
|
+
const hex = bytes.toString("hex");
|
|
13622
|
+
return [
|
|
13623
|
+
hex.slice(0, 8),
|
|
13624
|
+
hex.slice(8, 12),
|
|
13625
|
+
hex.slice(12, 16),
|
|
13626
|
+
hex.slice(16, 20),
|
|
13627
|
+
hex.slice(20)
|
|
13628
|
+
].join("-");
|
|
13629
|
+
}
|
|
13630
|
+
__name(deriveUuidV5, "deriveUuidV5");
|
|
13631
|
+
|
|
13632
|
+
// src/tools/meta-publish-attempt-id.ts
|
|
13633
|
+
var META_PUBLISH_NAMESPACE = "7b19c9ac-b4eb-4db1-970e-c2f24ce30b98";
|
|
13634
|
+
function deriveMetaPublishAttemptId(input) {
|
|
13635
|
+
const operationIdentity = canonicalJson([
|
|
13636
|
+
input.fingerprint,
|
|
13637
|
+
input.toolName,
|
|
13638
|
+
[
|
|
13639
|
+
typeof input.requestId,
|
|
13640
|
+
input.requestId
|
|
13641
|
+
],
|
|
13642
|
+
input.canonicalInput
|
|
13643
|
+
]);
|
|
13644
|
+
return deriveUuidV5(operationIdentity, META_PUBLISH_NAMESPACE);
|
|
13645
|
+
}
|
|
13646
|
+
__name(deriveMetaPublishAttemptId, "deriveMetaPublishAttemptId");
|
|
13647
|
+
|
|
13648
|
+
// src/tools/publish.ts
|
|
13560
13649
|
var ShopifyInput = z55.object({
|
|
13561
13650
|
taskId: z55.string().min(1),
|
|
13562
13651
|
shopDomain: z55.string().min(1),
|
|
@@ -13884,10 +13973,19 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
13884
13973
|
throw translateError(e);
|
|
13885
13974
|
}
|
|
13886
13975
|
});
|
|
13887
|
-
server.tool("publish_to_meta", "Crea una campa\xF1a de Meta Ads (Facebook/Instagram) desde UNA sola tarea. Antes llama `list_meta_destinations`, deja que el usuario elija el `portfolioId` (Business portfolio), el `adAccountId` y el `pageId` exactos, y env\xEDa esos tres valores; no adivines ni uses la primera p\xE1gina. Si quieres juntar creativos de VARIAS tareas usa `publish_creatives_to_meta`. La campa\xF1a se crea SIEMPRE PAUSED y el usuario la activa manualmente. `pixelId` es opcional. `angleIndexes` permite seleccionar \xE1ngulos. `campaignConfig` admite presupuesto en USD, pa\xEDses ISO-2 y nombre. `primaryText` permite reemplazar el copy y `phoneNumber` configura WhatsApp. Despu\xE9s usa `get_meta_campaign_status`.", MetaInput.shape, CREATES_EXTERNAL, async (
|
|
13976
|
+
server.tool("publish_to_meta", "Publica tus creativos en Meta sin gastar cr\xE9ditos de ProductMaker. El presupuesto de anuncios se paga directamente desde tu cuenta de Meta. Crear creativos o copy con IA sigue usando cr\xE9ditos. Crea una campa\xF1a de Meta Ads (Facebook/Instagram) desde UNA sola tarea. Antes llama `list_meta_destinations`, deja que el usuario elija el `portfolioId` (Business portfolio), el `adAccountId` y el `pageId` exactos, y env\xEDa esos tres valores; no adivines ni uses la primera p\xE1gina. Si quieres juntar creativos de VARIAS tareas usa `publish_creatives_to_meta`. La campa\xF1a se crea SIEMPRE PAUSED y el usuario la activa manualmente. `pixelId` es opcional. `angleIndexes` permite seleccionar \xE1ngulos. `campaignConfig` admite presupuesto en USD, pa\xEDses ISO-2 y nombre. `primaryText` permite reemplazar el copy y `phoneNumber` configura WhatsApp. Despu\xE9s usa `get_meta_campaign_status`. Si el resultado es ambiguo, NO reenv\xEDes: revisa Meta Ads Manager.", MetaInput.shape, CREATES_EXTERNAL, async (rawInput, extra) => {
|
|
13888
13977
|
try {
|
|
13978
|
+
const parsed = MetaInput.safeParse(rawInput);
|
|
13979
|
+
if (!parsed.success) throw invalidInputFromZod(parsed.error);
|
|
13980
|
+
const { taskId, campaignConfig, primaryText, phoneNumber, ...rest } = parsed.data;
|
|
13889
13981
|
const body = {
|
|
13890
|
-
...rest
|
|
13982
|
+
...rest,
|
|
13983
|
+
publishAttemptId: deriveMetaPublishAttemptId({
|
|
13984
|
+
fingerprint: api.fingerprint(),
|
|
13985
|
+
toolName: "publish_to_meta",
|
|
13986
|
+
requestId: extra.requestId,
|
|
13987
|
+
canonicalInput: parsed.data
|
|
13988
|
+
})
|
|
13891
13989
|
};
|
|
13892
13990
|
if (campaignConfig) {
|
|
13893
13991
|
body.campaignOverrides = toMetaCampaignOverrides(campaignConfig);
|
|
@@ -13912,7 +14010,7 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
13912
14010
|
throw translateError(e);
|
|
13913
14011
|
}
|
|
13914
14012
|
});
|
|
13915
|
-
server.tool("publish_creatives_to_meta", "Crea UNA campa\xF1a de Meta Ads PAUSED agrupando hasta " + CAMPAIGN_LIMITS.MAX_CREATIVES + ' creativos. Si la petici\xF3n natural adjunta o menciona archivos locales, llama primero `request_media_upload`; tras OK usa cada resultado como `source.type="asset"` con su `assetId` y el `type` que coincide con `mediaKind`. No pidas path ni URL. Despu\xE9s llama `list_meta_destinations` y pregunta en el chat por el `portfolioId`, `adAccountId` y `pageId` exactos; el App es solo para archivos. Si la carga fue parcial,
|
|
14013
|
+
server.tool("publish_creatives_to_meta", "Crea UNA campa\xF1a de Meta Ads PAUSED agrupando hasta " + CAMPAIGN_LIMITS.MAX_CREATIVES + ' creativos. Publica tus creativos en Meta sin gastar cr\xE9ditos de ProductMaker. El presupuesto de anuncios se paga directamente desde tu cuenta de Meta. Crear creativos o copy con IA sigue usando cr\xE9ditos. Si la petici\xF3n natural adjunta o menciona archivos locales, llama primero `request_media_upload`; tras OK usa cada resultado como `source.type="asset"` con su `assetId` y el `type` que coincide con `mediaKind`. No pidas path ni URL. Despu\xE9s llama `list_meta_destinations` y pregunta en el chat por el `portfolioId`, `adAccountId` y `pageId` exactos; el App es solo para archivos. Si la carga fue parcial, conserva los assets confirmados, procesa s\xF3lo los pendientes y publica \xFAnicamente assets confirmados; nunca inventes assets ni superes diez creativos. Las rutas locales s\xF3lo funcionan con MCP stdio. Los medios propios se eliminan despu\xE9s de publicar correctamente; si falla antes de enviar una mutaci\xF3n a Meta, quedan durante el TTL. 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. Si el resultado es ambiguo, NO reenv\xEDes: revisa Meta Ads Manager.', PublishCreativesToMetaInputBase.shape, CREATES_EXTERNAL, async (rawInput, extra) => {
|
|
13916
14014
|
try {
|
|
13917
14015
|
const parsed = PublishCreativesToMetaInput.safeParse(rawInput);
|
|
13918
14016
|
if (!parsed.success) throw invalidInputFromZod(parsed.error);
|
|
@@ -13992,7 +14090,13 @@ function registerPublishTools(server, api, opts = {}) {
|
|
|
13992
14090
|
landingUrl: input.landingUrl,
|
|
13993
14091
|
targetCountry: input.targetCountry,
|
|
13994
14092
|
campaignName: input.campaignName,
|
|
13995
|
-
campaignConfig: input.campaignConfig
|
|
14093
|
+
campaignConfig: input.campaignConfig,
|
|
14094
|
+
publishAttemptId: deriveMetaPublishAttemptId({
|
|
14095
|
+
fingerprint: api.fingerprint(),
|
|
14096
|
+
toolName: "publish_creatives_to_meta",
|
|
14097
|
+
requestId: extra.requestId,
|
|
14098
|
+
canonicalInput: input
|
|
14099
|
+
})
|
|
13996
14100
|
});
|
|
13997
14101
|
return asText({
|
|
13998
14102
|
...r,
|
|
@@ -14879,29 +14983,12 @@ __name(formatPercent, "formatPercent");
|
|
|
14879
14983
|
import { z as z61 } from "zod";
|
|
14880
14984
|
|
|
14881
14985
|
// src/tools/product-research-idempotency.ts
|
|
14882
|
-
import { createHash as createHash2 } from "crypto";
|
|
14883
14986
|
var RESEARCH_IDEMPOTENCY_WINDOW_MS = 2 * 60 * 60 * 1e3;
|
|
14884
14987
|
var NAMESPACE = "6f9d3a1e-8c47-4b52-9f10-2d7e5a3c8b64";
|
|
14885
|
-
function uuidV5(name, namespace) {
|
|
14886
|
-
const namespaceBytes = Buffer.from(namespace.replace(/-/g, ""), "hex");
|
|
14887
|
-
const digest = createHash2("sha1").update(namespaceBytes).update(name, "utf8").digest();
|
|
14888
|
-
const bytes = Buffer.from(digest.subarray(0, 16));
|
|
14889
|
-
bytes[6] = (bytes[6] ?? 0) & 15 | 80;
|
|
14890
|
-
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
14891
|
-
const hex = bytes.toString("hex");
|
|
14892
|
-
return [
|
|
14893
|
-
hex.slice(0, 8),
|
|
14894
|
-
hex.slice(8, 12),
|
|
14895
|
-
hex.slice(12, 16),
|
|
14896
|
-
hex.slice(16, 20),
|
|
14897
|
-
hex.slice(20)
|
|
14898
|
-
].join("-");
|
|
14899
|
-
}
|
|
14900
|
-
__name(uuidV5, "uuidV5");
|
|
14901
14988
|
function deriveIdempotencyKey(input) {
|
|
14902
14989
|
const window = Math.floor(input.nowMs / RESEARCH_IDEMPOTENCY_WINDOW_MS);
|
|
14903
14990
|
const name = `${input.fingerprint}:${input.targetCountry}:${input.categoryKey}:${window}`;
|
|
14904
|
-
return
|
|
14991
|
+
return deriveUuidV5(name, NAMESPACE);
|
|
14905
14992
|
}
|
|
14906
14993
|
__name(deriveIdempotencyKey, "deriveIdempotencyKey");
|
|
14907
14994
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@productmaker/mcp",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.3",
|
|
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.",
|