@productmaker/mcp 8.2.0 → 8.3.1
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/README.md +10 -1
- package/dist/http.js +478 -182
- package/dist/stdio.js +286 -56
- package/package.json +1 -1
package/dist/stdio.js
CHANGED
|
@@ -1852,6 +1852,9 @@ var VideoCreativeJobStatus = z14.object({
|
|
|
1852
1852
|
var VideoCreativeJobPayload = z14.object({
|
|
1853
1853
|
jobId: z14.string().optional(),
|
|
1854
1854
|
status: VideoCreativeJobStatusEnum.optional(),
|
|
1855
|
+
/** True when the submission replayed an existing task instead of billing
|
|
1856
|
+
* new work — callers surface the twin verbatim. Omitted on fresh creates. */
|
|
1857
|
+
replayed: z14.boolean().optional(),
|
|
1855
1858
|
videoStructure: VideoStructure.optional(),
|
|
1856
1859
|
videoQuality: VideoQuality.optional(),
|
|
1857
1860
|
videoRouteId: VideoRouteId.optional(),
|
|
@@ -1945,7 +1948,8 @@ var ImageCreativeJobStatus = z14.object({
|
|
|
1945
1948
|
});
|
|
1946
1949
|
var ImageCreativeJobPayload = z14.object({
|
|
1947
1950
|
jobId: z14.string().uuid(),
|
|
1948
|
-
status: ImageCreativeJobStatusEnum
|
|
1951
|
+
status: ImageCreativeJobStatusEnum,
|
|
1952
|
+
replayed: z14.boolean().optional()
|
|
1949
1953
|
});
|
|
1950
1954
|
var ImageCreativeHistoryEntry = z14.object({
|
|
1951
1955
|
id: z14.string().uuid(),
|
|
@@ -1972,7 +1976,8 @@ var LandingCreativeJobStatus = z14.object({
|
|
|
1972
1976
|
});
|
|
1973
1977
|
var LandingCreativeJobPayload = z14.object({
|
|
1974
1978
|
jobId: z14.string().uuid(),
|
|
1975
|
-
status: LandingCreativeJobStatusEnum
|
|
1979
|
+
status: LandingCreativeJobStatusEnum,
|
|
1980
|
+
replayed: z14.boolean().optional()
|
|
1976
1981
|
});
|
|
1977
1982
|
var LandingCreativeHistoryEntry = z14.object({
|
|
1978
1983
|
id: z14.string().uuid(),
|
|
@@ -5643,6 +5648,10 @@ var CreateShopifySubscriptionRequest = z28.object({
|
|
|
5643
5648
|
var CreateShopifyBillingChargeRequest = z28.object({
|
|
5644
5649
|
packId: z28.string().min(1)
|
|
5645
5650
|
});
|
|
5651
|
+
var ShopifyBillingCallbackQuery = z28.object({
|
|
5652
|
+
charge_id: z28.string().regex(/^\d+$/),
|
|
5653
|
+
transactionId: z28.string().uuid()
|
|
5654
|
+
});
|
|
5646
5655
|
var CancelShopifySubscriptionRequest = z28.object({
|
|
5647
5656
|
subscriptionId: z28.string().min(1)
|
|
5648
5657
|
});
|
|
@@ -10870,6 +10879,7 @@ var MixSceneResult = z48.object({
|
|
|
10870
10879
|
index: z48.number().int().positive(),
|
|
10871
10880
|
status: z48.enum([
|
|
10872
10881
|
"queued",
|
|
10882
|
+
"submitting",
|
|
10873
10883
|
"submitted",
|
|
10874
10884
|
"done",
|
|
10875
10885
|
"failed"
|
|
@@ -13468,7 +13478,7 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
|
|
|
13468
13478
|
], [
|
|
13469
13479
|
"product_costing"
|
|
13470
13480
|
]),
|
|
13471
|
-
defineCapability("create_creative_variant", "
|
|
13481
|
+
defineCapability("create_creative_variant", "3e02399a7785df328f1c4d63cfbb6d7481abaac9f91d4a5cf51d410e657d63aa", "credit_consuming", "creative", [
|
|
13472
13482
|
"credit_consuming",
|
|
13473
13483
|
"state_change"
|
|
13474
13484
|
], [
|
|
@@ -13718,7 +13728,9 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
|
|
|
13718
13728
|
// MCP 7.0.0 (Task 11, .agent/plans/2026-09-15-model-agnostic-video-routes.md):
|
|
13719
13729
|
// `freeProModel` retired in favor of `modelId` (registry ids). Recomputed
|
|
13720
13730
|
// from `pnpm --filter @productmaker/mcp test` printing the real digest.
|
|
13721
|
-
|
|
13731
|
+
// Re-baselined 2026-09-26: `idempotencyKey` field description corrected —
|
|
13732
|
+
// paid tools ignore caller-provided keys and derive their own semantic key.
|
|
13733
|
+
"35616a32a42a0bc062218fdfbb4cc7f4ecfa814571fb86b4f11ef9e4184855e1",
|
|
13722
13734
|
"credit_consuming",
|
|
13723
13735
|
"creative",
|
|
13724
13736
|
[
|
|
@@ -15980,7 +15992,10 @@ var ApiClient = class {
|
|
|
15980
15992
|
method: "POST",
|
|
15981
15993
|
headers: {
|
|
15982
15994
|
...authHeaders,
|
|
15983
|
-
"Content-Type": "application/json"
|
|
15995
|
+
"Content-Type": "application/json",
|
|
15996
|
+
...options?.idempotencyKey ? {
|
|
15997
|
+
"Idempotency-Key": options.idempotencyKey
|
|
15998
|
+
} : {}
|
|
15984
15999
|
},
|
|
15985
16000
|
body: JSON.stringify(body)
|
|
15986
16001
|
}, options);
|
|
@@ -16102,7 +16117,7 @@ var ApiClient = class {
|
|
|
16102
16117
|
};
|
|
16103
16118
|
|
|
16104
16119
|
// src/index.ts
|
|
16105
|
-
var MCP_VERSION = "8.
|
|
16120
|
+
var MCP_VERSION = "8.3.1";
|
|
16106
16121
|
|
|
16107
16122
|
// src/meta-upload-app.ts
|
|
16108
16123
|
import { readFileSync } from "fs";
|
|
@@ -17086,8 +17101,13 @@ async function submitCreative(api, opts) {
|
|
|
17086
17101
|
const taskId = r.taskId ?? r.jobId;
|
|
17087
17102
|
return asText({
|
|
17088
17103
|
taskId,
|
|
17089
|
-
|
|
17090
|
-
|
|
17104
|
+
// Replay-aware: a keyed retry may land on a task that is already
|
|
17105
|
+
// processing/done — report the real status, not a hopeful "queued".
|
|
17106
|
+
status: r.status ?? "queued",
|
|
17107
|
+
estimatedSeconds: opts.estimatedSeconds,
|
|
17108
|
+
...r.replayed === true ? {
|
|
17109
|
+
replayed: true
|
|
17110
|
+
} : {}
|
|
17091
17111
|
});
|
|
17092
17112
|
}
|
|
17093
17113
|
__name(submitCreative, "submitCreative");
|
|
@@ -18504,6 +18524,170 @@ ${summary}`,
|
|
|
18504
18524
|
}
|
|
18505
18525
|
__name(requirePaidCreativeConfirmation, "requirePaidCreativeConfirmation");
|
|
18506
18526
|
|
|
18527
|
+
// src/tools/stable-uuid.ts
|
|
18528
|
+
import { createHash as createHash2 } from "crypto";
|
|
18529
|
+
function canonicalize(value) {
|
|
18530
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
18531
|
+
if (value === null || typeof value !== "object") return value;
|
|
18532
|
+
const record = value;
|
|
18533
|
+
return Object.fromEntries(Object.keys(record).sort().filter((key) => record[key] !== void 0).map((key) => [
|
|
18534
|
+
key,
|
|
18535
|
+
canonicalize(record[key])
|
|
18536
|
+
]));
|
|
18537
|
+
}
|
|
18538
|
+
__name(canonicalize, "canonicalize");
|
|
18539
|
+
function canonicalJson(value) {
|
|
18540
|
+
return JSON.stringify(canonicalize(value));
|
|
18541
|
+
}
|
|
18542
|
+
__name(canonicalJson, "canonicalJson");
|
|
18543
|
+
function deriveUuidV5(name, namespace) {
|
|
18544
|
+
const namespaceBytes = Buffer.from(namespace.replaceAll("-", ""), "hex");
|
|
18545
|
+
const digest = createHash2("sha1").update(namespaceBytes).update(name, "utf8").digest();
|
|
18546
|
+
const bytes = Buffer.from(digest.subarray(0, 16));
|
|
18547
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 80;
|
|
18548
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
18549
|
+
const hex = bytes.toString("hex");
|
|
18550
|
+
return [
|
|
18551
|
+
hex.slice(0, 8),
|
|
18552
|
+
hex.slice(8, 12),
|
|
18553
|
+
hex.slice(12, 16),
|
|
18554
|
+
hex.slice(16, 20),
|
|
18555
|
+
hex.slice(20)
|
|
18556
|
+
].join("-");
|
|
18557
|
+
}
|
|
18558
|
+
__name(deriveUuidV5, "deriveUuidV5");
|
|
18559
|
+
|
|
18560
|
+
// src/tools/request-key.ts
|
|
18561
|
+
var PAID_REQUEST_KEY_WINDOW_MS = 15 * 60 * 1e3;
|
|
18562
|
+
var PAID_REQUEST_KEY_NAMESPACE = "8f3d6b1c-2a47-4e5b-9c0d-6e7f8a9b0c1d";
|
|
18563
|
+
var PARAPHRASE_PRONE_FIELDS = /* @__PURE__ */ new Set([
|
|
18564
|
+
"additionalContext",
|
|
18565
|
+
"productContext",
|
|
18566
|
+
"scriptNotes",
|
|
18567
|
+
"creativeDirection",
|
|
18568
|
+
"buyerPersona",
|
|
18569
|
+
"actorDescription",
|
|
18570
|
+
"description",
|
|
18571
|
+
"idempotencyKey",
|
|
18572
|
+
"clientRequestId"
|
|
18573
|
+
]);
|
|
18574
|
+
var URL_FIELDS = /* @__PURE__ */ new Set([
|
|
18575
|
+
"landingUrl",
|
|
18576
|
+
"productUrl",
|
|
18577
|
+
"url",
|
|
18578
|
+
"serviceLandingUrl"
|
|
18579
|
+
]);
|
|
18580
|
+
var TRACKING_PARAM_PATTERN = /^(utm_\w+|fbclid|gclid|msclkid|mc_cid|mc_eid|igshid)$/;
|
|
18581
|
+
var LANGUAGE_FIELDS = /* @__PURE__ */ new Set([
|
|
18582
|
+
"language",
|
|
18583
|
+
"lang"
|
|
18584
|
+
]);
|
|
18585
|
+
var CASE_FOLD_FIELDS = /* @__PURE__ */ new Set([
|
|
18586
|
+
"country",
|
|
18587
|
+
"language",
|
|
18588
|
+
"lang"
|
|
18589
|
+
]);
|
|
18590
|
+
var ORDER_INSIGNIFICANT_ARRAY_FIELDS = /* @__PURE__ */ new Set([
|
|
18591
|
+
"outputs",
|
|
18592
|
+
"selectedOutputs",
|
|
18593
|
+
"features"
|
|
18594
|
+
]);
|
|
18595
|
+
function sortCanonical(items) {
|
|
18596
|
+
return [
|
|
18597
|
+
...items
|
|
18598
|
+
].sort((a, b) => {
|
|
18599
|
+
const ka = canonicalJson(a);
|
|
18600
|
+
const kb = canonicalJson(b);
|
|
18601
|
+
return ka < kb ? -1 : ka > kb ? 1 : 0;
|
|
18602
|
+
});
|
|
18603
|
+
}
|
|
18604
|
+
__name(sortCanonical, "sortCanonical");
|
|
18605
|
+
function isRecord3(value) {
|
|
18606
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18607
|
+
}
|
|
18608
|
+
__name(isRecord3, "isRecord");
|
|
18609
|
+
function collectLanguageCodes(value, out) {
|
|
18610
|
+
if (Array.isArray(value)) {
|
|
18611
|
+
const items = value;
|
|
18612
|
+
for (const item of items) collectLanguageCodes(item, out);
|
|
18613
|
+
return out;
|
|
18614
|
+
}
|
|
18615
|
+
if (!isRecord3(value)) return out;
|
|
18616
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
18617
|
+
if (LANGUAGE_FIELDS.has(key) && typeof entry === "string") {
|
|
18618
|
+
const base = entry.trim().split(/[-_]/)[0]?.toLowerCase();
|
|
18619
|
+
if (base) out.add(base);
|
|
18620
|
+
}
|
|
18621
|
+
collectLanguageCodes(entry, out);
|
|
18622
|
+
}
|
|
18623
|
+
return out;
|
|
18624
|
+
}
|
|
18625
|
+
__name(collectLanguageCodes, "collectLanguageCodes");
|
|
18626
|
+
function normalizeSearchForKey(search) {
|
|
18627
|
+
if (!search) return "";
|
|
18628
|
+
const params = [
|
|
18629
|
+
...new URLSearchParams(search).entries()
|
|
18630
|
+
].filter(([key]) => !TRACKING_PARAM_PATTERN.test(key.toLowerCase())).sort(([ka, va], [kb, vb]) => ka === kb ? va < vb ? -1 : va > vb ? 1 : 0 : ka < kb ? -1 : 1);
|
|
18631
|
+
if (!params.length) return "";
|
|
18632
|
+
const out = new URLSearchParams();
|
|
18633
|
+
for (const [key, value] of params) out.append(key, value);
|
|
18634
|
+
return `?${out.toString()}`;
|
|
18635
|
+
}
|
|
18636
|
+
__name(normalizeSearchForKey, "normalizeSearchForKey");
|
|
18637
|
+
function normalizeUrlForKey(value, languages) {
|
|
18638
|
+
const trimmed = value.trim();
|
|
18639
|
+
let url;
|
|
18640
|
+
try {
|
|
18641
|
+
url = new URL(trimmed);
|
|
18642
|
+
} catch {
|
|
18643
|
+
return trimmed.toLowerCase();
|
|
18644
|
+
}
|
|
18645
|
+
const host = url.hostname.toLowerCase().replace(/^www\./, "");
|
|
18646
|
+
const port = url.port ? `:${url.port}` : "";
|
|
18647
|
+
const segments = url.pathname.split("/").filter(Boolean);
|
|
18648
|
+
const soleSegment = segments.length === 1 ? segments[0] : void 0;
|
|
18649
|
+
if (soleSegment !== void 0 && languages.has(soleSegment.toLowerCase())) {
|
|
18650
|
+
segments.pop();
|
|
18651
|
+
}
|
|
18652
|
+
const path3 = segments.length ? `/${segments.join("/")}` : "";
|
|
18653
|
+
return host + port + path3 + normalizeSearchForKey(url.search);
|
|
18654
|
+
}
|
|
18655
|
+
__name(normalizeUrlForKey, "normalizeUrlForKey");
|
|
18656
|
+
function normalizeForKey(value, languages) {
|
|
18657
|
+
if (Array.isArray(value)) {
|
|
18658
|
+
const items = value;
|
|
18659
|
+
return items.map((item) => normalizeForKey(item, languages));
|
|
18660
|
+
}
|
|
18661
|
+
if (!isRecord3(value)) return value;
|
|
18662
|
+
const out = {};
|
|
18663
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
18664
|
+
if (PARAPHRASE_PRONE_FIELDS.has(key)) {
|
|
18665
|
+
if (key === "description" && value.mode === "described") {
|
|
18666
|
+
out[key] = normalizeForKey(entry, languages);
|
|
18667
|
+
}
|
|
18668
|
+
continue;
|
|
18669
|
+
}
|
|
18670
|
+
out[key] = URL_FIELDS.has(key) && typeof entry === "string" ? normalizeUrlForKey(entry, languages) : CASE_FOLD_FIELDS.has(key) && typeof entry === "string" ? (
|
|
18671
|
+
// separator too or a host alternating between them splits the key.
|
|
18672
|
+
entry.trim().toLowerCase().replace(/_/g, "-")
|
|
18673
|
+
) : ORDER_INSIGNIFICANT_ARRAY_FIELDS.has(key) && Array.isArray(entry) ? sortCanonical(entry.map((item) => normalizeForKey(item, languages))) : normalizeForKey(entry, languages);
|
|
18674
|
+
}
|
|
18675
|
+
return out;
|
|
18676
|
+
}
|
|
18677
|
+
__name(normalizeForKey, "normalizeForKey");
|
|
18678
|
+
function derivePaidRequestKey(args) {
|
|
18679
|
+
const languages = collectLanguageCodes(args.input, /* @__PURE__ */ new Set());
|
|
18680
|
+
const semanticInput = normalizeForKey(args.input, languages);
|
|
18681
|
+
const bucket = Math.floor(args.nowMs / PAID_REQUEST_KEY_WINDOW_MS);
|
|
18682
|
+
return deriveUuidV5(canonicalJson({
|
|
18683
|
+
user: args.fingerprint,
|
|
18684
|
+
tool: args.tool,
|
|
18685
|
+
input: semanticInput,
|
|
18686
|
+
bucket
|
|
18687
|
+
}), PAID_REQUEST_KEY_NAMESPACE);
|
|
18688
|
+
}
|
|
18689
|
+
__name(derivePaidRequestKey, "derivePaidRequestKey");
|
|
18690
|
+
|
|
18507
18691
|
// src/tools/winning-product-image.ts
|
|
18508
18692
|
import { z as z75 } from "zod";
|
|
18509
18693
|
var WinnerReferenceInput = z75.object({
|
|
@@ -19040,10 +19224,25 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
19040
19224
|
};
|
|
19041
19225
|
const r = await api.postMultipart("/v1/ingest", {
|
|
19042
19226
|
images: parts
|
|
19043
|
-
}, fields
|
|
19227
|
+
}, fields, {
|
|
19228
|
+
// LLM hosts retry paid tools with rephrased arguments — the API's
|
|
19229
|
+
// byte-fingerprint can't see the duplicate, this key can. Derived
|
|
19230
|
+
// from the semantic inputs, not the wire fields, for the same
|
|
19231
|
+
// reason (see request-key.ts, which also canonicalizes the
|
|
19232
|
+
// order-insignificant `outputs` set).
|
|
19233
|
+
idempotencyKey: derivePaidRequestKey({
|
|
19234
|
+
fingerprint: api.fingerprint(),
|
|
19235
|
+
tool: "create_product_task",
|
|
19236
|
+
input,
|
|
19237
|
+
nowMs: Date.now()
|
|
19238
|
+
})
|
|
19239
|
+
});
|
|
19044
19240
|
return asText({
|
|
19045
19241
|
taskId: r.taskId,
|
|
19046
|
-
status: "queued",
|
|
19242
|
+
status: r.status ?? "queued",
|
|
19243
|
+
...r.replayed ? {
|
|
19244
|
+
replayed: true
|
|
19245
|
+
} : {},
|
|
19047
19246
|
estimatedSeconds: 300,
|
|
19048
19247
|
outputs: selectedOutputs,
|
|
19049
19248
|
outputMode: "single_task",
|
|
@@ -19080,26 +19279,56 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
19080
19279
|
assets: []
|
|
19081
19280
|
},
|
|
19082
19281
|
appDemoVideos: []
|
|
19282
|
+
}, {
|
|
19283
|
+
// The 2026-09-25 incident: four retries of this exact tool created
|
|
19284
|
+
// four charged service tasks because the host rephrased
|
|
19285
|
+
// `additionalContext` and flipped the landing between `/` and
|
|
19286
|
+
// `/es`. The derived key is stable across both rewrites (see
|
|
19287
|
+
// request-key.ts).
|
|
19288
|
+
idempotencyKey: derivePaidRequestKey({
|
|
19289
|
+
fingerprint: api.fingerprint(),
|
|
19290
|
+
tool: "create_service_video",
|
|
19291
|
+
input,
|
|
19292
|
+
nowMs: Date.now()
|
|
19293
|
+
})
|
|
19083
19294
|
});
|
|
19084
19295
|
const angles = await api.postJson(`/v1/tasks/${encodeURIComponent(created.taskId)}/service-angles`, {});
|
|
19085
19296
|
const first = angles[0];
|
|
19297
|
+
const replayed = created.replayed === true;
|
|
19086
19298
|
if (input.reviewMode === "automatic") {
|
|
19087
19299
|
if (!first) throw new Error("ProductMaker no pudo crear un concepto para este servicio.");
|
|
19088
|
-
|
|
19089
|
-
|
|
19090
|
-
|
|
19091
|
-
|
|
19092
|
-
|
|
19300
|
+
if (!(replayed && created.status !== "queued")) {
|
|
19301
|
+
await api.postJson(`/v1/tasks/${encodeURIComponent(created.taskId)}/service-angles/select`, {
|
|
19302
|
+
angleIds: [
|
|
19303
|
+
first.id
|
|
19304
|
+
]
|
|
19305
|
+
});
|
|
19306
|
+
}
|
|
19093
19307
|
return asText({
|
|
19094
19308
|
taskId: created.taskId,
|
|
19095
|
-
status: "processing",
|
|
19309
|
+
status: replayed ? created.status ?? "processing" : "processing",
|
|
19310
|
+
...replayed ? {
|
|
19311
|
+
replayed: true
|
|
19312
|
+
} : {},
|
|
19096
19313
|
reviewMode: input.reviewMode,
|
|
19097
19314
|
selectedConcept: first
|
|
19098
19315
|
});
|
|
19099
19316
|
}
|
|
19317
|
+
if (replayed && created.status !== "queued") {
|
|
19318
|
+
return asText({
|
|
19319
|
+
taskId: created.taskId,
|
|
19320
|
+
status: created.status,
|
|
19321
|
+
replayed: true,
|
|
19322
|
+
reviewMode: input.reviewMode,
|
|
19323
|
+
angles
|
|
19324
|
+
});
|
|
19325
|
+
}
|
|
19100
19326
|
return asText({
|
|
19101
19327
|
taskId: created.taskId,
|
|
19102
19328
|
status: "waiting_for_user",
|
|
19329
|
+
...replayed ? {
|
|
19330
|
+
replayed: true
|
|
19331
|
+
} : {},
|
|
19103
19332
|
reviewMode: input.reviewMode,
|
|
19104
19333
|
pendingUserAction: {
|
|
19105
19334
|
kind: "service_angle_approval",
|
|
@@ -19675,7 +19904,6 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
19675
19904
|
__name(registerTaskTools, "registerTaskTools");
|
|
19676
19905
|
|
|
19677
19906
|
// src/tools/creatives.ts
|
|
19678
|
-
import { randomUUID } from "crypto";
|
|
19679
19907
|
import { z as z77 } from "zod";
|
|
19680
19908
|
var generateFreeVideoGuidance = getProductMakerCapabilityModelGuidance("generate_free_video");
|
|
19681
19909
|
var generateVideoCreativeGuidance = getProductMakerCapabilityModelGuidance("generate_video_creative");
|
|
@@ -19796,7 +20024,7 @@ var FreeVideoInput = z77.object({
|
|
|
19796
20024
|
videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
|
|
19797
20025
|
generateAiMusic: z77.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
|
|
19798
20026
|
addSubtitles: z77.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
|
|
19799
|
-
idempotencyKey: z77.string().uuid().optional().describe("Opcional.
|
|
20027
|
+
idempotencyKey: z77.string().uuid().optional().describe("Opcional. Se acepta por compatibilidad pero se ignora: la identidad del reintento se deriva autom\xE1ticamente de los argumentos sem\xE1nticos, as\xED que un retry con los mismos argumentos ya es idempotente sin este campo.")
|
|
19800
20028
|
}).strict();
|
|
19801
20029
|
var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
|
|
19802
20030
|
var ImageCreativeSetup = z77.discriminatedUnion("mode", [
|
|
@@ -20018,7 +20246,16 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
20018
20246
|
addSubtitles: input.addSubtitles
|
|
20019
20247
|
},
|
|
20020
20248
|
allowEmptyImages: true,
|
|
20021
|
-
|
|
20249
|
+
// Always derived — never `input.idempotencyKey ??`: a host that
|
|
20250
|
+
// mints a fresh UUID per retry would defeat the dedupe entirely.
|
|
20251
|
+
// The semantic key is also a strictly better retry identity: a
|
|
20252
|
+
// reused caller key still lands on it (see request-key.ts).
|
|
20253
|
+
idempotencyKey: derivePaidRequestKey({
|
|
20254
|
+
fingerprint: api.fingerprint(),
|
|
20255
|
+
tool: "generate_free_video",
|
|
20256
|
+
input,
|
|
20257
|
+
nowMs: Date.now()
|
|
20258
|
+
}),
|
|
20022
20259
|
transport
|
|
20023
20260
|
});
|
|
20024
20261
|
} catch (e) {
|
|
@@ -20083,6 +20320,12 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
20083
20320
|
path: "/v1/creatives/videos",
|
|
20084
20321
|
estimatedSeconds: 300,
|
|
20085
20322
|
input: wirePayload,
|
|
20323
|
+
idempotencyKey: derivePaidRequestKey({
|
|
20324
|
+
fingerprint: api.fingerprint(),
|
|
20325
|
+
tool: "generate_video_creative",
|
|
20326
|
+
input,
|
|
20327
|
+
nowMs: Date.now()
|
|
20328
|
+
}),
|
|
20086
20329
|
transport
|
|
20087
20330
|
});
|
|
20088
20331
|
} catch (e) {
|
|
@@ -20117,6 +20360,12 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
20117
20360
|
pauseForAngleReview: true
|
|
20118
20361
|
}
|
|
20119
20362
|
},
|
|
20363
|
+
idempotencyKey: derivePaidRequestKey({
|
|
20364
|
+
fingerprint: api.fingerprint(),
|
|
20365
|
+
tool: "generate_image_creatives",
|
|
20366
|
+
input,
|
|
20367
|
+
nowMs: Date.now()
|
|
20368
|
+
}),
|
|
20120
20369
|
transport
|
|
20121
20370
|
});
|
|
20122
20371
|
} catch (e) {
|
|
@@ -20160,6 +20409,12 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
20160
20409
|
pauseForAngleReview: true
|
|
20161
20410
|
}
|
|
20162
20411
|
},
|
|
20412
|
+
idempotencyKey: derivePaidRequestKey({
|
|
20413
|
+
fingerprint: api.fingerprint(),
|
|
20414
|
+
tool: "generate_landing",
|
|
20415
|
+
input,
|
|
20416
|
+
nowMs: Date.now()
|
|
20417
|
+
}),
|
|
20163
20418
|
transport
|
|
20164
20419
|
});
|
|
20165
20420
|
} catch (e) {
|
|
@@ -20291,7 +20546,7 @@ var CreateCreativeVariantInputBase = z78.object({
|
|
|
20291
20546
|
sourceTaskId: z78.string().uuid().describe("ID de la tarea original completada."),
|
|
20292
20547
|
kind: CreativeVariantKindEnum,
|
|
20293
20548
|
confirm: z78.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
|
|
20294
|
-
clientRequestId: z78.string().uuid().describe("UUID estable
|
|
20549
|
+
clientRequestId: z78.string().uuid().describe("UUID estable para esta intenci\xF3n (obligatorio por compatibilidad de contrato; el servidor deriva la identidad real del request desde los argumentos sem\xE1nticos, as\xED que un UUID nuevo en un retry NO crea una variante duplicada). Si la API devuelve status=error, una nueva generaci\xF3n exige otra confirmaci\xF3n."),
|
|
20295
20550
|
angle: z78.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
|
|
20296
20551
|
buyerPersona: z78.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
|
|
20297
20552
|
scriptNotes: z78.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
|
|
@@ -20413,7 +20668,7 @@ function registerCreativeVariantTools(server, api) {
|
|
|
20413
20668
|
throw translateError(error);
|
|
20414
20669
|
}
|
|
20415
20670
|
});
|
|
20416
|
-
server.tool("create_creative_variant", createCreativeVariantGuidance.summary + " ES UNA ACCI\xD3N DE PAGO: primero llama estimate_creative_variant, muestra el rango de cr\xE9ditos al usuario y solo despu\xE9s invoca este tool con confirm=true. Las recomendaciones IA tambi\xE9n son acciones pagas a 2x del costo real. clientRequestId es obligatorio: genera un UUID por intenci\xF3n
|
|
20671
|
+
server.tool("create_creative_variant", createCreativeVariantGuidance.summary + " ES UNA ACCI\xD3N DE PAGO: primero llama estimate_creative_variant, muestra el rango de cr\xE9ditos al usuario y solo despu\xE9s invoca este tool con confirm=true. Las recomendaciones IA tambi\xE9n son acciones pagas a 2x del costo real. clientRequestId es obligatorio por contrato: genera un UUID por intenci\xF3n. El servidor deriva la identidad real del request desde los argumentos sem\xE1nticos, as\xED que un UUID nuevo NO desbloquea un intento distinto \u2014 si recibes status=error, pide confirmaci\xF3n al usuario y reinvoca con los mismos argumentos. Reutiliza el producto y referencias del task origen; crea un child task independiente y devuelve variantTaskId. Para landing puedes enviar instructions en texto libre y omitir sectionKey: el backend lo clasifica con IA. No expone ni permite cambiar videoModel, videoTier, videoStyle o narrativeStyle; esos valores se heredan del task origen.", CreateCreativeVariantInputBase.shape, CREATES_PAID, async (input) => {
|
|
20417
20672
|
try {
|
|
20418
20673
|
const validatedInput = parseVariantInput(CreateCreativeVariantInput, input);
|
|
20419
20674
|
const estimateInput = EstimateCreativeVariantInput.parse({
|
|
@@ -20440,8 +20695,16 @@ function registerCreativeVariantTools(server, api) {
|
|
|
20440
20695
|
});
|
|
20441
20696
|
sectionKey = LandingSectionKeyEnum.parse(recommendation.value);
|
|
20442
20697
|
}
|
|
20698
|
+
const derivedClientRequestId = derivePaidRequestKey({
|
|
20699
|
+
fingerprint: api.fingerprint(),
|
|
20700
|
+
tool: "create_creative_variant",
|
|
20701
|
+
input: {
|
|
20702
|
+
...validatedInput
|
|
20703
|
+
},
|
|
20704
|
+
nowMs: Date.now()
|
|
20705
|
+
});
|
|
20443
20706
|
const payload = {
|
|
20444
|
-
clientRequestId:
|
|
20707
|
+
clientRequestId: derivedClientRequestId,
|
|
20445
20708
|
kind: validatedInput.kind,
|
|
20446
20709
|
...validatedInput.kind !== "landing" ? {
|
|
20447
20710
|
angle: validatedInput.angle,
|
|
@@ -20467,7 +20730,7 @@ function registerCreativeVariantTools(server, api) {
|
|
|
20467
20730
|
};
|
|
20468
20731
|
const created = await api.postJson(`/v1/tasks/${encodeURIComponent(validatedInput.sourceTaskId)}/creative-variants`, payload);
|
|
20469
20732
|
if (created.status === "error") {
|
|
20470
|
-
throw mcp(McpErrorCode.CREATIVE_VARIANT_FAILED, "La variante anterior termin\xF3 con error. Pide confirmaci\xF3n para un intento nuevo
|
|
20733
|
+
throw mcp(McpErrorCode.CREATIVE_VARIANT_FAILED, "La variante anterior termin\xF3 con error. Pide confirmaci\xF3n al usuario y reinvoca con los mismos argumentos para un intento nuevo.", {
|
|
20471
20734
|
variantTaskId: created.variantTaskId
|
|
20472
20735
|
});
|
|
20473
20736
|
}
|
|
@@ -20682,39 +20945,6 @@ __name(registerActorTools, "registerActorTools");
|
|
|
20682
20945
|
// src/tools/publish.ts
|
|
20683
20946
|
import { z as z82 } from "zod";
|
|
20684
20947
|
|
|
20685
|
-
// src/tools/stable-uuid.ts
|
|
20686
|
-
import { createHash as createHash2 } from "crypto";
|
|
20687
|
-
function canonicalize(value) {
|
|
20688
|
-
if (Array.isArray(value)) return value.map(canonicalize);
|
|
20689
|
-
if (value === null || typeof value !== "object") return value;
|
|
20690
|
-
const record = value;
|
|
20691
|
-
return Object.fromEntries(Object.keys(record).sort().filter((key) => record[key] !== void 0).map((key) => [
|
|
20692
|
-
key,
|
|
20693
|
-
canonicalize(record[key])
|
|
20694
|
-
]));
|
|
20695
|
-
}
|
|
20696
|
-
__name(canonicalize, "canonicalize");
|
|
20697
|
-
function canonicalJson(value) {
|
|
20698
|
-
return JSON.stringify(canonicalize(value));
|
|
20699
|
-
}
|
|
20700
|
-
__name(canonicalJson, "canonicalJson");
|
|
20701
|
-
function deriveUuidV5(name, namespace) {
|
|
20702
|
-
const namespaceBytes = Buffer.from(namespace.replaceAll("-", ""), "hex");
|
|
20703
|
-
const digest = createHash2("sha1").update(namespaceBytes).update(name, "utf8").digest();
|
|
20704
|
-
const bytes = Buffer.from(digest.subarray(0, 16));
|
|
20705
|
-
bytes[6] = (bytes[6] ?? 0) & 15 | 80;
|
|
20706
|
-
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
20707
|
-
const hex = bytes.toString("hex");
|
|
20708
|
-
return [
|
|
20709
|
-
hex.slice(0, 8),
|
|
20710
|
-
hex.slice(8, 12),
|
|
20711
|
-
hex.slice(12, 16),
|
|
20712
|
-
hex.slice(16, 20),
|
|
20713
|
-
hex.slice(20)
|
|
20714
|
-
].join("-");
|
|
20715
|
-
}
|
|
20716
|
-
__name(deriveUuidV5, "deriveUuidV5");
|
|
20717
|
-
|
|
20718
20948
|
// src/tools/meta-publish-attempt-id.ts
|
|
20719
20949
|
var META_PUBLISH_NAMESPACE = "7b19c9ac-b4eb-4db1-970e-c2f24ce30b98";
|
|
20720
20950
|
function deriveMetaPublishAttemptId(input) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@productmaker/mcp",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "ProductMaker MCP server — cost products, generate landing pages, UGC videos, and ad creatives, and publish to Shopify, Meta Ads, and TikTok Ads.",
|