@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/http.js
CHANGED
|
@@ -1830,6 +1830,9 @@ var VideoCreativeJobStatus = z14.object({
|
|
|
1830
1830
|
var VideoCreativeJobPayload = z14.object({
|
|
1831
1831
|
jobId: z14.string().optional(),
|
|
1832
1832
|
status: VideoCreativeJobStatusEnum.optional(),
|
|
1833
|
+
/** True when the submission replayed an existing task instead of billing
|
|
1834
|
+
* new work — callers surface the twin verbatim. Omitted on fresh creates. */
|
|
1835
|
+
replayed: z14.boolean().optional(),
|
|
1833
1836
|
videoStructure: VideoStructure.optional(),
|
|
1834
1837
|
videoQuality: VideoQuality.optional(),
|
|
1835
1838
|
videoRouteId: VideoRouteId.optional(),
|
|
@@ -1923,7 +1926,8 @@ var ImageCreativeJobStatus = z14.object({
|
|
|
1923
1926
|
});
|
|
1924
1927
|
var ImageCreativeJobPayload = z14.object({
|
|
1925
1928
|
jobId: z14.string().uuid(),
|
|
1926
|
-
status: ImageCreativeJobStatusEnum
|
|
1929
|
+
status: ImageCreativeJobStatusEnum,
|
|
1930
|
+
replayed: z14.boolean().optional()
|
|
1927
1931
|
});
|
|
1928
1932
|
var ImageCreativeHistoryEntry = z14.object({
|
|
1929
1933
|
id: z14.string().uuid(),
|
|
@@ -1950,7 +1954,8 @@ var LandingCreativeJobStatus = z14.object({
|
|
|
1950
1954
|
});
|
|
1951
1955
|
var LandingCreativeJobPayload = z14.object({
|
|
1952
1956
|
jobId: z14.string().uuid(),
|
|
1953
|
-
status: LandingCreativeJobStatusEnum
|
|
1957
|
+
status: LandingCreativeJobStatusEnum,
|
|
1958
|
+
replayed: z14.boolean().optional()
|
|
1954
1959
|
});
|
|
1955
1960
|
var LandingCreativeHistoryEntry = z14.object({
|
|
1956
1961
|
id: z14.string().uuid(),
|
|
@@ -5621,6 +5626,10 @@ var CreateShopifySubscriptionRequest = z28.object({
|
|
|
5621
5626
|
var CreateShopifyBillingChargeRequest = z28.object({
|
|
5622
5627
|
packId: z28.string().min(1)
|
|
5623
5628
|
});
|
|
5629
|
+
var ShopifyBillingCallbackQuery = z28.object({
|
|
5630
|
+
charge_id: z28.string().regex(/^\d+$/),
|
|
5631
|
+
transactionId: z28.string().uuid()
|
|
5632
|
+
});
|
|
5624
5633
|
var CancelShopifySubscriptionRequest = z28.object({
|
|
5625
5634
|
subscriptionId: z28.string().min(1)
|
|
5626
5635
|
});
|
|
@@ -10848,6 +10857,7 @@ var MixSceneResult = z48.object({
|
|
|
10848
10857
|
index: z48.number().int().positive(),
|
|
10849
10858
|
status: z48.enum([
|
|
10850
10859
|
"queued",
|
|
10860
|
+
"submitting",
|
|
10851
10861
|
"submitted",
|
|
10852
10862
|
"done",
|
|
10853
10863
|
"failed"
|
|
@@ -13446,7 +13456,7 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
|
|
|
13446
13456
|
], [
|
|
13447
13457
|
"product_costing"
|
|
13448
13458
|
]),
|
|
13449
|
-
defineCapability("create_creative_variant", "
|
|
13459
|
+
defineCapability("create_creative_variant", "3e02399a7785df328f1c4d63cfbb6d7481abaac9f91d4a5cf51d410e657d63aa", "credit_consuming", "creative", [
|
|
13450
13460
|
"credit_consuming",
|
|
13451
13461
|
"state_change"
|
|
13452
13462
|
], [
|
|
@@ -13696,7 +13706,9 @@ var PRODUCTMAKER_CAPABILITY_REGISTRY = deepFreeze([
|
|
|
13696
13706
|
// MCP 7.0.0 (Task 11, .agent/plans/2026-09-15-model-agnostic-video-routes.md):
|
|
13697
13707
|
// `freeProModel` retired in favor of `modelId` (registry ids). Recomputed
|
|
13698
13708
|
// from `pnpm --filter @productmaker/mcp test` printing the real digest.
|
|
13699
|
-
|
|
13709
|
+
// Re-baselined 2026-09-26: `idempotencyKey` field description corrected —
|
|
13710
|
+
// paid tools ignore caller-provided keys and derive their own semantic key.
|
|
13711
|
+
"35616a32a42a0bc062218fdfbb4cc7f4ecfa814571fb86b4f11ef9e4184855e1",
|
|
13700
13712
|
"credit_consuming",
|
|
13701
13713
|
"creative",
|
|
13702
13714
|
[
|
|
@@ -16195,6 +16207,9 @@ function buildProxyUrl(opts) {
|
|
|
16195
16207
|
}
|
|
16196
16208
|
__name(buildProxyUrl, "buildProxyUrl");
|
|
16197
16209
|
|
|
16210
|
+
// src/http.ts
|
|
16211
|
+
import { getOAuthProtectedResourceMetadataUrl as getOAuthProtectedResourceMetadataUrl2 } from "@modelcontextprotocol/sdk/server/auth/router.js";
|
|
16212
|
+
|
|
16198
16213
|
// src/auth/http-oauth.ts
|
|
16199
16214
|
import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
|
|
16200
16215
|
import { getOAuthProtectedResourceMetadataUrl, mcpAuthMetadataRouter } from "@modelcontextprotocol/sdk/server/auth/router.js";
|
|
@@ -16604,6 +16619,7 @@ import { createRemoteJWKSet as createRemoteJWKSet2, jwtVerify as jwtVerify2 } fr
|
|
|
16604
16619
|
import { z as z73 } from "zod";
|
|
16605
16620
|
var MCP_RESOURCE = new URL("https://mcp.productmaker.app/mcp");
|
|
16606
16621
|
var MCP_SCOPE = "mcp:access";
|
|
16622
|
+
var REFRESH_SCOPE = "offline_access";
|
|
16607
16623
|
var STATIC_CLIENT_ISSUER = new URL(MCP_RESOURCE.origin);
|
|
16608
16624
|
var oauthEnvSchema = z73.object({
|
|
16609
16625
|
MCP_REMOTE_AUTH_MODE: z73.literal("oauth"),
|
|
@@ -16799,6 +16815,121 @@ var Auth0TokenVerifier = class {
|
|
|
16799
16815
|
}
|
|
16800
16816
|
};
|
|
16801
16817
|
|
|
16818
|
+
// src/oauth-analytics.ts
|
|
16819
|
+
import { PostHog } from "posthog-node";
|
|
16820
|
+
var DEFAULT_POSTHOG_HOST = "https://us.i.posthog.com";
|
|
16821
|
+
var REDACTED = "[REDACTED]";
|
|
16822
|
+
var SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
16823
|
+
"token",
|
|
16824
|
+
"authorization",
|
|
16825
|
+
"apikey",
|
|
16826
|
+
"clientsecret",
|
|
16827
|
+
"subjecttoken",
|
|
16828
|
+
"accesstoken",
|
|
16829
|
+
"refreshtoken",
|
|
16830
|
+
"idtoken",
|
|
16831
|
+
"code",
|
|
16832
|
+
"secret",
|
|
16833
|
+
"password"
|
|
16834
|
+
]);
|
|
16835
|
+
var SENSITIVE_ASSIGNMENT_PATTERN = /(\b(?:access[_-]?token|authorization|client[_-]?secret|subject[_-]?token|refresh[_-]?token|id[_-]?token|api[_-]?key|token|secret|password|code)\b\s*[:=]\s*)(?:(bearer)\s+)?(?:(['"])(.*?)\3|([^\s,;&}"']+))/gi;
|
|
16836
|
+
var BEARER_TOKEN_PATTERN = /\b(bearer\s+)([A-Za-z0-9._~+/=-]+)/gi;
|
|
16837
|
+
var client = null;
|
|
16838
|
+
var KNOWN_MCP_OAUTH_ANALYTICS_EVENTS = [
|
|
16839
|
+
MCP_OAUTH_ANALYTICS_EVENTS.STARTED,
|
|
16840
|
+
MCP_OAUTH_ANALYTICS_EVENTS.CONNECTED,
|
|
16841
|
+
MCP_OAUTH_ANALYTICS_EVENTS.FAILED,
|
|
16842
|
+
MCP_OAUTH_ANALYTICS_EVENTS.SAFE_VARIANT
|
|
16843
|
+
];
|
|
16844
|
+
function getPostHogClient() {
|
|
16845
|
+
if (client) return client;
|
|
16846
|
+
const apiKey = process.env.POSTHOG_API_KEY;
|
|
16847
|
+
if (!apiKey) return null;
|
|
16848
|
+
client = new PostHog(apiKey, {
|
|
16849
|
+
host: process.env.POSTHOG_HOST || DEFAULT_POSTHOG_HOST,
|
|
16850
|
+
flushAt: 20,
|
|
16851
|
+
flushInterval: 1e4
|
|
16852
|
+
});
|
|
16853
|
+
return client;
|
|
16854
|
+
}
|
|
16855
|
+
__name(getPostHogClient, "getPostHogClient");
|
|
16856
|
+
function normalizeKey(key) {
|
|
16857
|
+
return key.replace(/[^a-z0-9]/gi, "").toLowerCase();
|
|
16858
|
+
}
|
|
16859
|
+
__name(normalizeKey, "normalizeKey");
|
|
16860
|
+
function isSensitiveKey(key) {
|
|
16861
|
+
const normalizedKey = normalizeKey(key);
|
|
16862
|
+
return SENSITIVE_KEYS.has(normalizedKey) || normalizedKey.endsWith("token") || normalizedKey.endsWith("secret");
|
|
16863
|
+
}
|
|
16864
|
+
__name(isSensitiveKey, "isSensitiveKey");
|
|
16865
|
+
function sanitizeUrl(value) {
|
|
16866
|
+
try {
|
|
16867
|
+
const parsed = new URL(value);
|
|
16868
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
16869
|
+
return value;
|
|
16870
|
+
}
|
|
16871
|
+
parsed.username = "";
|
|
16872
|
+
parsed.password = "";
|
|
16873
|
+
parsed.search = "";
|
|
16874
|
+
parsed.hash = "";
|
|
16875
|
+
return parsed.toString();
|
|
16876
|
+
} catch {
|
|
16877
|
+
return value;
|
|
16878
|
+
}
|
|
16879
|
+
}
|
|
16880
|
+
__name(sanitizeUrl, "sanitizeUrl");
|
|
16881
|
+
function sanitizeString(value) {
|
|
16882
|
+
const redacted = value.replace(SENSITIVE_ASSIGNMENT_PATTERN, (_match, prefix, scheme, quote) => {
|
|
16883
|
+
const replacement = quote ? `${quote}${REDACTED}${quote}` : REDACTED;
|
|
16884
|
+
return `${prefix}${scheme ? `${scheme} ` : ""}${replacement}`;
|
|
16885
|
+
}).replace(BEARER_TOKEN_PATTERN, `$1${REDACTED}`);
|
|
16886
|
+
const sanitizedUrl = sanitizeUrl(redacted);
|
|
16887
|
+
if (sanitizedUrl !== redacted) return sanitizedUrl;
|
|
16888
|
+
return redacted.replace(/https?:\/\/[^\s"'<>]+/gi, sanitizeUrl);
|
|
16889
|
+
}
|
|
16890
|
+
__name(sanitizeString, "sanitizeString");
|
|
16891
|
+
function sanitizeValue(value) {
|
|
16892
|
+
if (Array.isArray(value)) {
|
|
16893
|
+
return value.map((entry) => sanitizeValue(entry));
|
|
16894
|
+
}
|
|
16895
|
+
if (value && typeof value === "object") {
|
|
16896
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => {
|
|
16897
|
+
if (isSensitiveKey(key)) {
|
|
16898
|
+
return [
|
|
16899
|
+
key,
|
|
16900
|
+
REDACTED
|
|
16901
|
+
];
|
|
16902
|
+
}
|
|
16903
|
+
return [
|
|
16904
|
+
key,
|
|
16905
|
+
sanitizeValue(entry)
|
|
16906
|
+
];
|
|
16907
|
+
}));
|
|
16908
|
+
}
|
|
16909
|
+
if (typeof value === "string") {
|
|
16910
|
+
return sanitizeString(value);
|
|
16911
|
+
}
|
|
16912
|
+
return value;
|
|
16913
|
+
}
|
|
16914
|
+
__name(sanitizeValue, "sanitizeValue");
|
|
16915
|
+
var OAuthAnalytics = class {
|
|
16916
|
+
static {
|
|
16917
|
+
__name(this, "OAuthAnalytics");
|
|
16918
|
+
}
|
|
16919
|
+
capture(event, subject, properties) {
|
|
16920
|
+
const posthog = getPostHogClient();
|
|
16921
|
+
if (!posthog) return;
|
|
16922
|
+
try {
|
|
16923
|
+
posthog.capture({
|
|
16924
|
+
distinctId: subject ?? "mcp_oauth_aggregate",
|
|
16925
|
+
event,
|
|
16926
|
+
properties: sanitizeValue(properties)
|
|
16927
|
+
});
|
|
16928
|
+
} catch {
|
|
16929
|
+
}
|
|
16930
|
+
}
|
|
16931
|
+
};
|
|
16932
|
+
|
|
16802
16933
|
// src/auth/oauth-proxy.ts
|
|
16803
16934
|
import { createHmac as createHmac2, hkdfSync, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
16804
16935
|
import express from "express";
|
|
@@ -16885,6 +17016,15 @@ var OAUTH_PROXY_PATHS = {
|
|
|
16885
17016
|
token: "/oauth/token"
|
|
16886
17017
|
};
|
|
16887
17018
|
var PROXY_CALLBACK_URL = new URL(OAUTH_PROXY_PATHS.callback.slice(1), STATIC_CLIENT_ISSUER).href;
|
|
17019
|
+
function responseIssuesRefreshToken(payload) {
|
|
17020
|
+
try {
|
|
17021
|
+
const parsed = JSON.parse(payload);
|
|
17022
|
+
return typeof parsed === "object" && parsed !== null && "refresh_token" in parsed && typeof parsed.refresh_token === "string" && parsed.refresh_token !== "";
|
|
17023
|
+
} catch {
|
|
17024
|
+
return false;
|
|
17025
|
+
}
|
|
17026
|
+
}
|
|
17027
|
+
__name(responseIssuesRefreshToken, "responseIssuesRefreshToken");
|
|
16888
17028
|
function firstQueryValue(value) {
|
|
16889
17029
|
if (typeof value === "string") return value;
|
|
16890
17030
|
if (Array.isArray(value) && typeof value[0] === "string") return value[0];
|
|
@@ -16898,6 +17038,12 @@ function badRequest(res, description) {
|
|
|
16898
17038
|
});
|
|
16899
17039
|
}
|
|
16900
17040
|
__name(badRequest, "badRequest");
|
|
17041
|
+
function withRefreshScope(requested) {
|
|
17042
|
+
const scopes = (requested?.trim() ? requested : MCP_SCOPE).split(/\s+/).filter(Boolean);
|
|
17043
|
+
if (!scopes.includes(REFRESH_SCOPE)) scopes.push(REFRESH_SCOPE);
|
|
17044
|
+
return scopes.join(" ");
|
|
17045
|
+
}
|
|
17046
|
+
__name(withRefreshScope, "withRefreshScope");
|
|
16901
17047
|
function createOAuthProxyRouter(options) {
|
|
16902
17048
|
const router = express.Router();
|
|
16903
17049
|
const stateKey3 = deriveStateKey(options.staticClient.clientSecret);
|
|
@@ -16921,13 +17067,14 @@ function createOAuthProxyRouter(options) {
|
|
|
16921
17067
|
}
|
|
16922
17068
|
const upstream = new URL(options.authorizationEndpoint);
|
|
16923
17069
|
for (const [key, value] of Object.entries(req.query)) {
|
|
16924
|
-
if (key === "redirect_uri" || key === "state") continue;
|
|
17070
|
+
if (key === "redirect_uri" || key === "state" || key === "scope") continue;
|
|
16925
17071
|
for (const v of Array.isArray(value) ? value : [
|
|
16926
17072
|
value
|
|
16927
17073
|
]) {
|
|
16928
17074
|
if (typeof v === "string") upstream.searchParams.append(key, v);
|
|
16929
17075
|
}
|
|
16930
17076
|
}
|
|
17077
|
+
upstream.searchParams.set("scope", withRefreshScope(firstQueryValue(req.query.scope)));
|
|
16931
17078
|
upstream.searchParams.set("redirect_uri", PROXY_CALLBACK_URL);
|
|
16932
17079
|
upstream.searchParams.set("state", sealState(stateKey3, {
|
|
16933
17080
|
redirectUri,
|
|
@@ -16987,13 +17134,10 @@ function createOAuthProxyRouter(options) {
|
|
|
16987
17134
|
});
|
|
16988
17135
|
return;
|
|
16989
17136
|
}
|
|
17137
|
+
body.set("client_secret", options.staticClient.clientSecret);
|
|
16990
17138
|
const headers = {
|
|
16991
17139
|
"content-type": "application/x-www-form-urlencoded"
|
|
16992
17140
|
};
|
|
16993
|
-
const authorization = req.headers.authorization;
|
|
16994
|
-
if (typeof authorization === "string") {
|
|
16995
|
-
headers.authorization = authorization;
|
|
16996
|
-
}
|
|
16997
17141
|
let upstream;
|
|
16998
17142
|
let payload;
|
|
16999
17143
|
try {
|
|
@@ -17010,6 +17154,14 @@ function createOAuthProxyRouter(options) {
|
|
|
17010
17154
|
});
|
|
17011
17155
|
return;
|
|
17012
17156
|
}
|
|
17157
|
+
try {
|
|
17158
|
+
options.onTokenExchange?.({
|
|
17159
|
+
grantType,
|
|
17160
|
+
status: upstream.status,
|
|
17161
|
+
refreshTokenIssued: responseIssuesRefreshToken(payload)
|
|
17162
|
+
});
|
|
17163
|
+
} catch {
|
|
17164
|
+
}
|
|
17013
17165
|
res.status(upstream.status).type(upstream.headers.get("content-type") ?? "application/json").send(payload);
|
|
17014
17166
|
});
|
|
17015
17167
|
return router;
|
|
@@ -17026,11 +17178,12 @@ var REGISTRATION_RATE_LIMIT = {
|
|
|
17026
17178
|
windowMs: 6e4,
|
|
17027
17179
|
max: 120
|
|
17028
17180
|
};
|
|
17181
|
+
var PUBLIC_CLIENT_SECRET_PLACEHOLDER = "pm-public-client";
|
|
17029
17182
|
function buildStaticClientInformation(staticClient, requested) {
|
|
17030
17183
|
return {
|
|
17031
17184
|
client_id: staticClient.clientId,
|
|
17032
|
-
client_secret:
|
|
17033
|
-
// 0 = never expires (RFC 7591 §3.2.1); the
|
|
17185
|
+
client_secret: PUBLIC_CLIENT_SECRET_PLACEHOLDER,
|
|
17186
|
+
// 0 = never expires (RFC 7591 §3.2.1); the placeholder is a constant.
|
|
17034
17187
|
client_secret_expires_at: 0,
|
|
17035
17188
|
token_endpoint_auth_method: "client_secret_post",
|
|
17036
17189
|
redirect_uris: requested.redirect_uris,
|
|
@@ -17041,7 +17194,12 @@ function buildStaticClientInformation(staticClient, requested) {
|
|
|
17041
17194
|
response_types: [
|
|
17042
17195
|
"code"
|
|
17043
17196
|
],
|
|
17044
|
-
|
|
17197
|
+
// Clients that defer scope choice to the server get the full intended
|
|
17198
|
+
// set: `mcp:access` for the resource plus `offline_access` so the session
|
|
17199
|
+
// can refresh instead of dying an interactive re-auth every 900s access
|
|
17200
|
+
// token (the auth loop seen in production). A client that names its own
|
|
17201
|
+
// scope list is echoed verbatim — its choice wins.
|
|
17202
|
+
scope: requested.scope ?? `${MCP_SCOPE} ${REFRESH_SCOPE}`,
|
|
17045
17203
|
...requested.client_name !== void 0 ? {
|
|
17046
17204
|
client_name: requested.client_name
|
|
17047
17205
|
} : {}
|
|
@@ -17088,8 +17246,12 @@ function buildStaticClientAuthorizationServerMetadata(auth0Metadata) {
|
|
|
17088
17246
|
token_endpoint_auth_methods_supported: [
|
|
17089
17247
|
"client_secret_post"
|
|
17090
17248
|
],
|
|
17249
|
+
// `offline_access` is what lets a client ASK for a refresh token; without
|
|
17250
|
+
// it in scopes_supported, conforming clients never request it and every
|
|
17251
|
+
// 900s access-token expiry becomes an interactive re-auth loop.
|
|
17091
17252
|
scopes_supported: [
|
|
17092
|
-
MCP_SCOPE
|
|
17253
|
+
MCP_SCOPE,
|
|
17254
|
+
REFRESH_SCOPE
|
|
17093
17255
|
],
|
|
17094
17256
|
service_documentation: "https://productmaker.app/"
|
|
17095
17257
|
};
|
|
@@ -17141,11 +17303,23 @@ function createMetadataRouter(config, metadata, fetchFn) {
|
|
|
17141
17303
|
staticClient: config.staticClient,
|
|
17142
17304
|
authorizationEndpoint: metadata.authorization_endpoint,
|
|
17143
17305
|
tokenEndpoint: metadata.token_endpoint,
|
|
17144
|
-
fetchFn
|
|
17306
|
+
fetchFn,
|
|
17307
|
+
onTokenExchange: captureTokenExchange
|
|
17145
17308
|
}));
|
|
17146
17309
|
return router;
|
|
17147
17310
|
}
|
|
17148
17311
|
__name(createMetadataRouter, "createMetadataRouter");
|
|
17312
|
+
var tokenExchangeAnalytics = new OAuthAnalytics();
|
|
17313
|
+
function captureTokenExchange(outcome) {
|
|
17314
|
+
const ok = outcome.status >= 200 && outcome.status < 300;
|
|
17315
|
+
tokenExchangeAnalytics.capture(ok ? MCP_OAUTH_ANALYTICS_EVENTS.CONNECTED : MCP_OAUTH_ANALYTICS_EVENTS.FAILED, null, {
|
|
17316
|
+
stage: "token_exchange",
|
|
17317
|
+
grantType: outcome.grantType,
|
|
17318
|
+
status: outcome.status,
|
|
17319
|
+
refreshTokenIssued: outcome.refreshTokenIssued
|
|
17320
|
+
});
|
|
17321
|
+
}
|
|
17322
|
+
__name(captureTokenExchange, "captureTokenExchange");
|
|
17149
17323
|
function createHttpOAuth(config, metadata, fetchFn) {
|
|
17150
17324
|
const verifier = new Auth0TokenVerifier(config, metadata.jwks_uri);
|
|
17151
17325
|
const oboClient = new Auth0OboClient(config, {
|
|
@@ -17424,121 +17598,6 @@ function registerProductMakerPrompts(server) {
|
|
|
17424
17598
|
}
|
|
17425
17599
|
__name(registerProductMakerPrompts, "registerProductMakerPrompts");
|
|
17426
17600
|
|
|
17427
|
-
// src/oauth-analytics.ts
|
|
17428
|
-
import { PostHog } from "posthog-node";
|
|
17429
|
-
var DEFAULT_POSTHOG_HOST = "https://us.i.posthog.com";
|
|
17430
|
-
var REDACTED = "[REDACTED]";
|
|
17431
|
-
var SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
17432
|
-
"token",
|
|
17433
|
-
"authorization",
|
|
17434
|
-
"apikey",
|
|
17435
|
-
"clientsecret",
|
|
17436
|
-
"subjecttoken",
|
|
17437
|
-
"accesstoken",
|
|
17438
|
-
"refreshtoken",
|
|
17439
|
-
"idtoken",
|
|
17440
|
-
"code",
|
|
17441
|
-
"secret",
|
|
17442
|
-
"password"
|
|
17443
|
-
]);
|
|
17444
|
-
var SENSITIVE_ASSIGNMENT_PATTERN = /(\b(?:access[_-]?token|authorization|client[_-]?secret|subject[_-]?token|refresh[_-]?token|id[_-]?token|api[_-]?key|token|secret|password|code)\b\s*[:=]\s*)(?:(bearer)\s+)?(?:(['"])(.*?)\3|([^\s,;&}"']+))/gi;
|
|
17445
|
-
var BEARER_TOKEN_PATTERN = /\b(bearer\s+)([A-Za-z0-9._~+/=-]+)/gi;
|
|
17446
|
-
var client = null;
|
|
17447
|
-
var KNOWN_MCP_OAUTH_ANALYTICS_EVENTS = [
|
|
17448
|
-
MCP_OAUTH_ANALYTICS_EVENTS.STARTED,
|
|
17449
|
-
MCP_OAUTH_ANALYTICS_EVENTS.CONNECTED,
|
|
17450
|
-
MCP_OAUTH_ANALYTICS_EVENTS.FAILED,
|
|
17451
|
-
MCP_OAUTH_ANALYTICS_EVENTS.SAFE_VARIANT
|
|
17452
|
-
];
|
|
17453
|
-
function getPostHogClient() {
|
|
17454
|
-
if (client) return client;
|
|
17455
|
-
const apiKey = process.env.POSTHOG_API_KEY;
|
|
17456
|
-
if (!apiKey) return null;
|
|
17457
|
-
client = new PostHog(apiKey, {
|
|
17458
|
-
host: process.env.POSTHOG_HOST || DEFAULT_POSTHOG_HOST,
|
|
17459
|
-
flushAt: 20,
|
|
17460
|
-
flushInterval: 1e4
|
|
17461
|
-
});
|
|
17462
|
-
return client;
|
|
17463
|
-
}
|
|
17464
|
-
__name(getPostHogClient, "getPostHogClient");
|
|
17465
|
-
function normalizeKey(key) {
|
|
17466
|
-
return key.replace(/[^a-z0-9]/gi, "").toLowerCase();
|
|
17467
|
-
}
|
|
17468
|
-
__name(normalizeKey, "normalizeKey");
|
|
17469
|
-
function isSensitiveKey(key) {
|
|
17470
|
-
const normalizedKey = normalizeKey(key);
|
|
17471
|
-
return SENSITIVE_KEYS.has(normalizedKey) || normalizedKey.endsWith("token") || normalizedKey.endsWith("secret");
|
|
17472
|
-
}
|
|
17473
|
-
__name(isSensitiveKey, "isSensitiveKey");
|
|
17474
|
-
function sanitizeUrl(value) {
|
|
17475
|
-
try {
|
|
17476
|
-
const parsed = new URL(value);
|
|
17477
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
17478
|
-
return value;
|
|
17479
|
-
}
|
|
17480
|
-
parsed.username = "";
|
|
17481
|
-
parsed.password = "";
|
|
17482
|
-
parsed.search = "";
|
|
17483
|
-
parsed.hash = "";
|
|
17484
|
-
return parsed.toString();
|
|
17485
|
-
} catch {
|
|
17486
|
-
return value;
|
|
17487
|
-
}
|
|
17488
|
-
}
|
|
17489
|
-
__name(sanitizeUrl, "sanitizeUrl");
|
|
17490
|
-
function sanitizeString(value) {
|
|
17491
|
-
const redacted = value.replace(SENSITIVE_ASSIGNMENT_PATTERN, (_match, prefix, scheme, quote) => {
|
|
17492
|
-
const replacement = quote ? `${quote}${REDACTED}${quote}` : REDACTED;
|
|
17493
|
-
return `${prefix}${scheme ? `${scheme} ` : ""}${replacement}`;
|
|
17494
|
-
}).replace(BEARER_TOKEN_PATTERN, `$1${REDACTED}`);
|
|
17495
|
-
const sanitizedUrl = sanitizeUrl(redacted);
|
|
17496
|
-
if (sanitizedUrl !== redacted) return sanitizedUrl;
|
|
17497
|
-
return redacted.replace(/https?:\/\/[^\s"'<>]+/gi, sanitizeUrl);
|
|
17498
|
-
}
|
|
17499
|
-
__name(sanitizeString, "sanitizeString");
|
|
17500
|
-
function sanitizeValue(value) {
|
|
17501
|
-
if (Array.isArray(value)) {
|
|
17502
|
-
return value.map((entry) => sanitizeValue(entry));
|
|
17503
|
-
}
|
|
17504
|
-
if (value && typeof value === "object") {
|
|
17505
|
-
return Object.fromEntries(Object.entries(value).map(([key, entry]) => {
|
|
17506
|
-
if (isSensitiveKey(key)) {
|
|
17507
|
-
return [
|
|
17508
|
-
key,
|
|
17509
|
-
REDACTED
|
|
17510
|
-
];
|
|
17511
|
-
}
|
|
17512
|
-
return [
|
|
17513
|
-
key,
|
|
17514
|
-
sanitizeValue(entry)
|
|
17515
|
-
];
|
|
17516
|
-
}));
|
|
17517
|
-
}
|
|
17518
|
-
if (typeof value === "string") {
|
|
17519
|
-
return sanitizeString(value);
|
|
17520
|
-
}
|
|
17521
|
-
return value;
|
|
17522
|
-
}
|
|
17523
|
-
__name(sanitizeValue, "sanitizeValue");
|
|
17524
|
-
var OAuthAnalytics = class {
|
|
17525
|
-
static {
|
|
17526
|
-
__name(this, "OAuthAnalytics");
|
|
17527
|
-
}
|
|
17528
|
-
capture(event, subject, properties) {
|
|
17529
|
-
const posthog = getPostHogClient();
|
|
17530
|
-
if (!posthog) return;
|
|
17531
|
-
try {
|
|
17532
|
-
posthog.capture({
|
|
17533
|
-
distinctId: subject ?? "mcp_oauth_aggregate",
|
|
17534
|
-
event,
|
|
17535
|
-
properties: sanitizeValue(properties)
|
|
17536
|
-
});
|
|
17537
|
-
} catch {
|
|
17538
|
-
}
|
|
17539
|
-
}
|
|
17540
|
-
};
|
|
17541
|
-
|
|
17542
17601
|
// src/server.ts
|
|
17543
17602
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
17544
17603
|
|
|
@@ -17601,7 +17660,10 @@ var ApiClient = class {
|
|
|
17601
17660
|
method: "POST",
|
|
17602
17661
|
headers: {
|
|
17603
17662
|
...authHeaders,
|
|
17604
|
-
"Content-Type": "application/json"
|
|
17663
|
+
"Content-Type": "application/json",
|
|
17664
|
+
...options?.idempotencyKey ? {
|
|
17665
|
+
"Idempotency-Key": options.idempotencyKey
|
|
17666
|
+
} : {}
|
|
17605
17667
|
},
|
|
17606
17668
|
body: JSON.stringify(body)
|
|
17607
17669
|
}, options);
|
|
@@ -17723,7 +17785,7 @@ var ApiClient = class {
|
|
|
17723
17785
|
};
|
|
17724
17786
|
|
|
17725
17787
|
// src/index.ts
|
|
17726
|
-
var MCP_VERSION = "8.
|
|
17788
|
+
var MCP_VERSION = "8.3.1";
|
|
17727
17789
|
|
|
17728
17790
|
// src/meta-upload-app.ts
|
|
17729
17791
|
import { readFileSync } from "fs";
|
|
@@ -18707,8 +18769,13 @@ async function submitCreative(api, opts) {
|
|
|
18707
18769
|
const taskId = r.taskId ?? r.jobId;
|
|
18708
18770
|
return asText({
|
|
18709
18771
|
taskId,
|
|
18710
|
-
|
|
18711
|
-
|
|
18772
|
+
// Replay-aware: a keyed retry may land on a task that is already
|
|
18773
|
+
// processing/done — report the real status, not a hopeful "queued".
|
|
18774
|
+
status: r.status ?? "queued",
|
|
18775
|
+
estimatedSeconds: opts.estimatedSeconds,
|
|
18776
|
+
...r.replayed === true ? {
|
|
18777
|
+
replayed: true
|
|
18778
|
+
} : {}
|
|
18712
18779
|
});
|
|
18713
18780
|
}
|
|
18714
18781
|
__name(submitCreative, "submitCreative");
|
|
@@ -20072,6 +20139,170 @@ ${summary}`,
|
|
|
20072
20139
|
}
|
|
20073
20140
|
__name(requirePaidCreativeConfirmation, "requirePaidCreativeConfirmation");
|
|
20074
20141
|
|
|
20142
|
+
// src/tools/stable-uuid.ts
|
|
20143
|
+
import { createHash as createHash3 } from "crypto";
|
|
20144
|
+
function canonicalize(value) {
|
|
20145
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
20146
|
+
if (value === null || typeof value !== "object") return value;
|
|
20147
|
+
const record = value;
|
|
20148
|
+
return Object.fromEntries(Object.keys(record).sort().filter((key) => record[key] !== void 0).map((key) => [
|
|
20149
|
+
key,
|
|
20150
|
+
canonicalize(record[key])
|
|
20151
|
+
]));
|
|
20152
|
+
}
|
|
20153
|
+
__name(canonicalize, "canonicalize");
|
|
20154
|
+
function canonicalJson(value) {
|
|
20155
|
+
return JSON.stringify(canonicalize(value));
|
|
20156
|
+
}
|
|
20157
|
+
__name(canonicalJson, "canonicalJson");
|
|
20158
|
+
function deriveUuidV5(name, namespace) {
|
|
20159
|
+
const namespaceBytes = Buffer.from(namespace.replaceAll("-", ""), "hex");
|
|
20160
|
+
const digest = createHash3("sha1").update(namespaceBytes).update(name, "utf8").digest();
|
|
20161
|
+
const bytes = Buffer.from(digest.subarray(0, 16));
|
|
20162
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 80;
|
|
20163
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
20164
|
+
const hex = bytes.toString("hex");
|
|
20165
|
+
return [
|
|
20166
|
+
hex.slice(0, 8),
|
|
20167
|
+
hex.slice(8, 12),
|
|
20168
|
+
hex.slice(12, 16),
|
|
20169
|
+
hex.slice(16, 20),
|
|
20170
|
+
hex.slice(20)
|
|
20171
|
+
].join("-");
|
|
20172
|
+
}
|
|
20173
|
+
__name(deriveUuidV5, "deriveUuidV5");
|
|
20174
|
+
|
|
20175
|
+
// src/tools/request-key.ts
|
|
20176
|
+
var PAID_REQUEST_KEY_WINDOW_MS = 15 * 60 * 1e3;
|
|
20177
|
+
var PAID_REQUEST_KEY_NAMESPACE = "8f3d6b1c-2a47-4e5b-9c0d-6e7f8a9b0c1d";
|
|
20178
|
+
var PARAPHRASE_PRONE_FIELDS = /* @__PURE__ */ new Set([
|
|
20179
|
+
"additionalContext",
|
|
20180
|
+
"productContext",
|
|
20181
|
+
"scriptNotes",
|
|
20182
|
+
"creativeDirection",
|
|
20183
|
+
"buyerPersona",
|
|
20184
|
+
"actorDescription",
|
|
20185
|
+
"description",
|
|
20186
|
+
"idempotencyKey",
|
|
20187
|
+
"clientRequestId"
|
|
20188
|
+
]);
|
|
20189
|
+
var URL_FIELDS = /* @__PURE__ */ new Set([
|
|
20190
|
+
"landingUrl",
|
|
20191
|
+
"productUrl",
|
|
20192
|
+
"url",
|
|
20193
|
+
"serviceLandingUrl"
|
|
20194
|
+
]);
|
|
20195
|
+
var TRACKING_PARAM_PATTERN = /^(utm_\w+|fbclid|gclid|msclkid|mc_cid|mc_eid|igshid)$/;
|
|
20196
|
+
var LANGUAGE_FIELDS = /* @__PURE__ */ new Set([
|
|
20197
|
+
"language",
|
|
20198
|
+
"lang"
|
|
20199
|
+
]);
|
|
20200
|
+
var CASE_FOLD_FIELDS = /* @__PURE__ */ new Set([
|
|
20201
|
+
"country",
|
|
20202
|
+
"language",
|
|
20203
|
+
"lang"
|
|
20204
|
+
]);
|
|
20205
|
+
var ORDER_INSIGNIFICANT_ARRAY_FIELDS = /* @__PURE__ */ new Set([
|
|
20206
|
+
"outputs",
|
|
20207
|
+
"selectedOutputs",
|
|
20208
|
+
"features"
|
|
20209
|
+
]);
|
|
20210
|
+
function sortCanonical(items) {
|
|
20211
|
+
return [
|
|
20212
|
+
...items
|
|
20213
|
+
].sort((a, b) => {
|
|
20214
|
+
const ka = canonicalJson(a);
|
|
20215
|
+
const kb = canonicalJson(b);
|
|
20216
|
+
return ka < kb ? -1 : ka > kb ? 1 : 0;
|
|
20217
|
+
});
|
|
20218
|
+
}
|
|
20219
|
+
__name(sortCanonical, "sortCanonical");
|
|
20220
|
+
function isRecord3(value) {
|
|
20221
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20222
|
+
}
|
|
20223
|
+
__name(isRecord3, "isRecord");
|
|
20224
|
+
function collectLanguageCodes(value, out) {
|
|
20225
|
+
if (Array.isArray(value)) {
|
|
20226
|
+
const items = value;
|
|
20227
|
+
for (const item of items) collectLanguageCodes(item, out);
|
|
20228
|
+
return out;
|
|
20229
|
+
}
|
|
20230
|
+
if (!isRecord3(value)) return out;
|
|
20231
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
20232
|
+
if (LANGUAGE_FIELDS.has(key) && typeof entry === "string") {
|
|
20233
|
+
const base = entry.trim().split(/[-_]/)[0]?.toLowerCase();
|
|
20234
|
+
if (base) out.add(base);
|
|
20235
|
+
}
|
|
20236
|
+
collectLanguageCodes(entry, out);
|
|
20237
|
+
}
|
|
20238
|
+
return out;
|
|
20239
|
+
}
|
|
20240
|
+
__name(collectLanguageCodes, "collectLanguageCodes");
|
|
20241
|
+
function normalizeSearchForKey(search) {
|
|
20242
|
+
if (!search) return "";
|
|
20243
|
+
const params = [
|
|
20244
|
+
...new URLSearchParams(search).entries()
|
|
20245
|
+
].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);
|
|
20246
|
+
if (!params.length) return "";
|
|
20247
|
+
const out = new URLSearchParams();
|
|
20248
|
+
for (const [key, value] of params) out.append(key, value);
|
|
20249
|
+
return `?${out.toString()}`;
|
|
20250
|
+
}
|
|
20251
|
+
__name(normalizeSearchForKey, "normalizeSearchForKey");
|
|
20252
|
+
function normalizeUrlForKey(value, languages) {
|
|
20253
|
+
const trimmed = value.trim();
|
|
20254
|
+
let url;
|
|
20255
|
+
try {
|
|
20256
|
+
url = new URL(trimmed);
|
|
20257
|
+
} catch {
|
|
20258
|
+
return trimmed.toLowerCase();
|
|
20259
|
+
}
|
|
20260
|
+
const host = url.hostname.toLowerCase().replace(/^www\./, "");
|
|
20261
|
+
const port = url.port ? `:${url.port}` : "";
|
|
20262
|
+
const segments = url.pathname.split("/").filter(Boolean);
|
|
20263
|
+
const soleSegment = segments.length === 1 ? segments[0] : void 0;
|
|
20264
|
+
if (soleSegment !== void 0 && languages.has(soleSegment.toLowerCase())) {
|
|
20265
|
+
segments.pop();
|
|
20266
|
+
}
|
|
20267
|
+
const path3 = segments.length ? `/${segments.join("/")}` : "";
|
|
20268
|
+
return host + port + path3 + normalizeSearchForKey(url.search);
|
|
20269
|
+
}
|
|
20270
|
+
__name(normalizeUrlForKey, "normalizeUrlForKey");
|
|
20271
|
+
function normalizeForKey(value, languages) {
|
|
20272
|
+
if (Array.isArray(value)) {
|
|
20273
|
+
const items = value;
|
|
20274
|
+
return items.map((item) => normalizeForKey(item, languages));
|
|
20275
|
+
}
|
|
20276
|
+
if (!isRecord3(value)) return value;
|
|
20277
|
+
const out = {};
|
|
20278
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
20279
|
+
if (PARAPHRASE_PRONE_FIELDS.has(key)) {
|
|
20280
|
+
if (key === "description" && value.mode === "described") {
|
|
20281
|
+
out[key] = normalizeForKey(entry, languages);
|
|
20282
|
+
}
|
|
20283
|
+
continue;
|
|
20284
|
+
}
|
|
20285
|
+
out[key] = URL_FIELDS.has(key) && typeof entry === "string" ? normalizeUrlForKey(entry, languages) : CASE_FOLD_FIELDS.has(key) && typeof entry === "string" ? (
|
|
20286
|
+
// separator too or a host alternating between them splits the key.
|
|
20287
|
+
entry.trim().toLowerCase().replace(/_/g, "-")
|
|
20288
|
+
) : ORDER_INSIGNIFICANT_ARRAY_FIELDS.has(key) && Array.isArray(entry) ? sortCanonical(entry.map((item) => normalizeForKey(item, languages))) : normalizeForKey(entry, languages);
|
|
20289
|
+
}
|
|
20290
|
+
return out;
|
|
20291
|
+
}
|
|
20292
|
+
__name(normalizeForKey, "normalizeForKey");
|
|
20293
|
+
function derivePaidRequestKey(args) {
|
|
20294
|
+
const languages = collectLanguageCodes(args.input, /* @__PURE__ */ new Set());
|
|
20295
|
+
const semanticInput = normalizeForKey(args.input, languages);
|
|
20296
|
+
const bucket = Math.floor(args.nowMs / PAID_REQUEST_KEY_WINDOW_MS);
|
|
20297
|
+
return deriveUuidV5(canonicalJson({
|
|
20298
|
+
user: args.fingerprint,
|
|
20299
|
+
tool: args.tool,
|
|
20300
|
+
input: semanticInput,
|
|
20301
|
+
bucket
|
|
20302
|
+
}), PAID_REQUEST_KEY_NAMESPACE);
|
|
20303
|
+
}
|
|
20304
|
+
__name(derivePaidRequestKey, "derivePaidRequestKey");
|
|
20305
|
+
|
|
20075
20306
|
// src/tools/winning-product-image.ts
|
|
20076
20307
|
import { z as z76 } from "zod";
|
|
20077
20308
|
var WinnerReferenceInput = z76.object({
|
|
@@ -20608,10 +20839,25 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
20608
20839
|
};
|
|
20609
20840
|
const r = await api.postMultipart("/v1/ingest", {
|
|
20610
20841
|
images: parts
|
|
20611
|
-
}, fields
|
|
20842
|
+
}, fields, {
|
|
20843
|
+
// LLM hosts retry paid tools with rephrased arguments — the API's
|
|
20844
|
+
// byte-fingerprint can't see the duplicate, this key can. Derived
|
|
20845
|
+
// from the semantic inputs, not the wire fields, for the same
|
|
20846
|
+
// reason (see request-key.ts, which also canonicalizes the
|
|
20847
|
+
// order-insignificant `outputs` set).
|
|
20848
|
+
idempotencyKey: derivePaidRequestKey({
|
|
20849
|
+
fingerprint: api.fingerprint(),
|
|
20850
|
+
tool: "create_product_task",
|
|
20851
|
+
input,
|
|
20852
|
+
nowMs: Date.now()
|
|
20853
|
+
})
|
|
20854
|
+
});
|
|
20612
20855
|
return asText({
|
|
20613
20856
|
taskId: r.taskId,
|
|
20614
|
-
status: "queued",
|
|
20857
|
+
status: r.status ?? "queued",
|
|
20858
|
+
...r.replayed ? {
|
|
20859
|
+
replayed: true
|
|
20860
|
+
} : {},
|
|
20615
20861
|
estimatedSeconds: 300,
|
|
20616
20862
|
outputs: selectedOutputs,
|
|
20617
20863
|
outputMode: "single_task",
|
|
@@ -20648,26 +20894,56 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
20648
20894
|
assets: []
|
|
20649
20895
|
},
|
|
20650
20896
|
appDemoVideos: []
|
|
20897
|
+
}, {
|
|
20898
|
+
// The 2026-09-25 incident: four retries of this exact tool created
|
|
20899
|
+
// four charged service tasks because the host rephrased
|
|
20900
|
+
// `additionalContext` and flipped the landing between `/` and
|
|
20901
|
+
// `/es`. The derived key is stable across both rewrites (see
|
|
20902
|
+
// request-key.ts).
|
|
20903
|
+
idempotencyKey: derivePaidRequestKey({
|
|
20904
|
+
fingerprint: api.fingerprint(),
|
|
20905
|
+
tool: "create_service_video",
|
|
20906
|
+
input,
|
|
20907
|
+
nowMs: Date.now()
|
|
20908
|
+
})
|
|
20651
20909
|
});
|
|
20652
20910
|
const angles = await api.postJson(`/v1/tasks/${encodeURIComponent(created.taskId)}/service-angles`, {});
|
|
20653
20911
|
const first = angles[0];
|
|
20912
|
+
const replayed = created.replayed === true;
|
|
20654
20913
|
if (input.reviewMode === "automatic") {
|
|
20655
20914
|
if (!first) throw new Error("ProductMaker no pudo crear un concepto para este servicio.");
|
|
20656
|
-
|
|
20657
|
-
|
|
20658
|
-
|
|
20659
|
-
|
|
20660
|
-
|
|
20915
|
+
if (!(replayed && created.status !== "queued")) {
|
|
20916
|
+
await api.postJson(`/v1/tasks/${encodeURIComponent(created.taskId)}/service-angles/select`, {
|
|
20917
|
+
angleIds: [
|
|
20918
|
+
first.id
|
|
20919
|
+
]
|
|
20920
|
+
});
|
|
20921
|
+
}
|
|
20661
20922
|
return asText({
|
|
20662
20923
|
taskId: created.taskId,
|
|
20663
|
-
status: "processing",
|
|
20924
|
+
status: replayed ? created.status ?? "processing" : "processing",
|
|
20925
|
+
...replayed ? {
|
|
20926
|
+
replayed: true
|
|
20927
|
+
} : {},
|
|
20664
20928
|
reviewMode: input.reviewMode,
|
|
20665
20929
|
selectedConcept: first
|
|
20666
20930
|
});
|
|
20667
20931
|
}
|
|
20932
|
+
if (replayed && created.status !== "queued") {
|
|
20933
|
+
return asText({
|
|
20934
|
+
taskId: created.taskId,
|
|
20935
|
+
status: created.status,
|
|
20936
|
+
replayed: true,
|
|
20937
|
+
reviewMode: input.reviewMode,
|
|
20938
|
+
angles
|
|
20939
|
+
});
|
|
20940
|
+
}
|
|
20668
20941
|
return asText({
|
|
20669
20942
|
taskId: created.taskId,
|
|
20670
20943
|
status: "waiting_for_user",
|
|
20944
|
+
...replayed ? {
|
|
20945
|
+
replayed: true
|
|
20946
|
+
} : {},
|
|
20671
20947
|
reviewMode: input.reviewMode,
|
|
20672
20948
|
pendingUserAction: {
|
|
20673
20949
|
kind: "service_angle_approval",
|
|
@@ -21243,7 +21519,6 @@ ANTES de invocar, resume outputs, cantidad de videos y datos comerciales, explic
|
|
|
21243
21519
|
__name(registerTaskTools, "registerTaskTools");
|
|
21244
21520
|
|
|
21245
21521
|
// src/tools/creatives.ts
|
|
21246
|
-
import { randomUUID } from "crypto";
|
|
21247
21522
|
import { z as z78 } from "zod";
|
|
21248
21523
|
var generateFreeVideoGuidance = getProductMakerCapabilityModelGuidance("generate_free_video");
|
|
21249
21524
|
var generateVideoCreativeGuidance = getProductMakerCapabilityModelGuidance("generate_video_creative");
|
|
@@ -21364,7 +21639,7 @@ var FreeVideoInput = z78.object({
|
|
|
21364
21639
|
videoResolution: VideoResolutionEnum.describe("Resoluci\xF3n del video: 480p o 720p."),
|
|
21365
21640
|
generateAiMusic: z78.boolean().default(false).describe("Opcional. Agrega m\xFAsica instrumental IA debajo del audio nativo. Default false."),
|
|
21366
21641
|
addSubtitles: z78.boolean().default(false).describe("Opcional. Quema subt\xEDtulos usando el rawPrompt exacto. Default false."),
|
|
21367
|
-
idempotencyKey: z78.string().uuid().optional().describe("Opcional.
|
|
21642
|
+
idempotencyKey: z78.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.")
|
|
21368
21643
|
}).strict();
|
|
21369
21644
|
var imageStyleList = ImageCreativeStyleEnum.options.join(" | ");
|
|
21370
21645
|
var ImageCreativeSetup = z78.discriminatedUnion("mode", [
|
|
@@ -21586,7 +21861,16 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
21586
21861
|
addSubtitles: input.addSubtitles
|
|
21587
21862
|
},
|
|
21588
21863
|
allowEmptyImages: true,
|
|
21589
|
-
|
|
21864
|
+
// Always derived — never `input.idempotencyKey ??`: a host that
|
|
21865
|
+
// mints a fresh UUID per retry would defeat the dedupe entirely.
|
|
21866
|
+
// The semantic key is also a strictly better retry identity: a
|
|
21867
|
+
// reused caller key still lands on it (see request-key.ts).
|
|
21868
|
+
idempotencyKey: derivePaidRequestKey({
|
|
21869
|
+
fingerprint: api.fingerprint(),
|
|
21870
|
+
tool: "generate_free_video",
|
|
21871
|
+
input,
|
|
21872
|
+
nowMs: Date.now()
|
|
21873
|
+
}),
|
|
21590
21874
|
transport
|
|
21591
21875
|
});
|
|
21592
21876
|
} catch (e) {
|
|
@@ -21651,6 +21935,12 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
21651
21935
|
path: "/v1/creatives/videos",
|
|
21652
21936
|
estimatedSeconds: 300,
|
|
21653
21937
|
input: wirePayload,
|
|
21938
|
+
idempotencyKey: derivePaidRequestKey({
|
|
21939
|
+
fingerprint: api.fingerprint(),
|
|
21940
|
+
tool: "generate_video_creative",
|
|
21941
|
+
input,
|
|
21942
|
+
nowMs: Date.now()
|
|
21943
|
+
}),
|
|
21654
21944
|
transport
|
|
21655
21945
|
});
|
|
21656
21946
|
} catch (e) {
|
|
@@ -21685,6 +21975,12 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
21685
21975
|
pauseForAngleReview: true
|
|
21686
21976
|
}
|
|
21687
21977
|
},
|
|
21978
|
+
idempotencyKey: derivePaidRequestKey({
|
|
21979
|
+
fingerprint: api.fingerprint(),
|
|
21980
|
+
tool: "generate_image_creatives",
|
|
21981
|
+
input,
|
|
21982
|
+
nowMs: Date.now()
|
|
21983
|
+
}),
|
|
21688
21984
|
transport
|
|
21689
21985
|
});
|
|
21690
21986
|
} catch (e) {
|
|
@@ -21728,6 +22024,12 @@ function registerCreativeTools(server, api, { transport = "http" } = {}) {
|
|
|
21728
22024
|
pauseForAngleReview: true
|
|
21729
22025
|
}
|
|
21730
22026
|
},
|
|
22027
|
+
idempotencyKey: derivePaidRequestKey({
|
|
22028
|
+
fingerprint: api.fingerprint(),
|
|
22029
|
+
tool: "generate_landing",
|
|
22030
|
+
input,
|
|
22031
|
+
nowMs: Date.now()
|
|
22032
|
+
}),
|
|
21731
22033
|
transport
|
|
21732
22034
|
});
|
|
21733
22035
|
} catch (e) {
|
|
@@ -21859,7 +22161,7 @@ var CreateCreativeVariantInputBase = z79.object({
|
|
|
21859
22161
|
sourceTaskId: z79.string().uuid().describe("ID de la tarea original completada."),
|
|
21860
22162
|
kind: CreativeVariantKindEnum,
|
|
21861
22163
|
confirm: z79.literal(true).describe("Confirmaci\xF3n expl\xEDcita del usuario despu\xE9s de mostrarle el estimado de cr\xE9ditos."),
|
|
21862
|
-
clientRequestId: z79.string().uuid().describe("UUID estable
|
|
22164
|
+
clientRequestId: z79.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."),
|
|
21863
22165
|
angle: z79.string().trim().min(1).max(500).optional().describe("\xC1ngulo nuevo para video o imagen."),
|
|
21864
22166
|
buyerPersona: z79.string().trim().min(1).max(360).optional().describe("Buyer persona coherente con el \xE1ngulo."),
|
|
21865
22167
|
scriptNotes: z79.string().trim().min(1).max(2e3).optional().describe("Direcci\xF3n breve del guion para una variante de video."),
|
|
@@ -21981,7 +22283,7 @@ function registerCreativeVariantTools(server, api) {
|
|
|
21981
22283
|
throw translateError(error);
|
|
21982
22284
|
}
|
|
21983
22285
|
});
|
|
21984
|
-
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
|
|
22286
|
+
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) => {
|
|
21985
22287
|
try {
|
|
21986
22288
|
const validatedInput = parseVariantInput(CreateCreativeVariantInput, input);
|
|
21987
22289
|
const estimateInput = EstimateCreativeVariantInput.parse({
|
|
@@ -22008,8 +22310,16 @@ function registerCreativeVariantTools(server, api) {
|
|
|
22008
22310
|
});
|
|
22009
22311
|
sectionKey = LandingSectionKeyEnum.parse(recommendation.value);
|
|
22010
22312
|
}
|
|
22313
|
+
const derivedClientRequestId = derivePaidRequestKey({
|
|
22314
|
+
fingerprint: api.fingerprint(),
|
|
22315
|
+
tool: "create_creative_variant",
|
|
22316
|
+
input: {
|
|
22317
|
+
...validatedInput
|
|
22318
|
+
},
|
|
22319
|
+
nowMs: Date.now()
|
|
22320
|
+
});
|
|
22011
22321
|
const payload = {
|
|
22012
|
-
clientRequestId:
|
|
22322
|
+
clientRequestId: derivedClientRequestId,
|
|
22013
22323
|
kind: validatedInput.kind,
|
|
22014
22324
|
...validatedInput.kind !== "landing" ? {
|
|
22015
22325
|
angle: validatedInput.angle,
|
|
@@ -22035,7 +22345,7 @@ function registerCreativeVariantTools(server, api) {
|
|
|
22035
22345
|
};
|
|
22036
22346
|
const created = await api.postJson(`/v1/tasks/${encodeURIComponent(validatedInput.sourceTaskId)}/creative-variants`, payload);
|
|
22037
22347
|
if (created.status === "error") {
|
|
22038
|
-
throw mcp(McpErrorCode.CREATIVE_VARIANT_FAILED, "La variante anterior termin\xF3 con error. Pide confirmaci\xF3n para un intento nuevo
|
|
22348
|
+
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.", {
|
|
22039
22349
|
variantTaskId: created.variantTaskId
|
|
22040
22350
|
});
|
|
22041
22351
|
}
|
|
@@ -22250,39 +22560,6 @@ __name(registerActorTools, "registerActorTools");
|
|
|
22250
22560
|
// src/tools/publish.ts
|
|
22251
22561
|
import { z as z83 } from "zod";
|
|
22252
22562
|
|
|
22253
|
-
// src/tools/stable-uuid.ts
|
|
22254
|
-
import { createHash as createHash3 } from "crypto";
|
|
22255
|
-
function canonicalize(value) {
|
|
22256
|
-
if (Array.isArray(value)) return value.map(canonicalize);
|
|
22257
|
-
if (value === null || typeof value !== "object") return value;
|
|
22258
|
-
const record = value;
|
|
22259
|
-
return Object.fromEntries(Object.keys(record).sort().filter((key) => record[key] !== void 0).map((key) => [
|
|
22260
|
-
key,
|
|
22261
|
-
canonicalize(record[key])
|
|
22262
|
-
]));
|
|
22263
|
-
}
|
|
22264
|
-
__name(canonicalize, "canonicalize");
|
|
22265
|
-
function canonicalJson(value) {
|
|
22266
|
-
return JSON.stringify(canonicalize(value));
|
|
22267
|
-
}
|
|
22268
|
-
__name(canonicalJson, "canonicalJson");
|
|
22269
|
-
function deriveUuidV5(name, namespace) {
|
|
22270
|
-
const namespaceBytes = Buffer.from(namespace.replaceAll("-", ""), "hex");
|
|
22271
|
-
const digest = createHash3("sha1").update(namespaceBytes).update(name, "utf8").digest();
|
|
22272
|
-
const bytes = Buffer.from(digest.subarray(0, 16));
|
|
22273
|
-
bytes[6] = (bytes[6] ?? 0) & 15 | 80;
|
|
22274
|
-
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
22275
|
-
const hex = bytes.toString("hex");
|
|
22276
|
-
return [
|
|
22277
|
-
hex.slice(0, 8),
|
|
22278
|
-
hex.slice(8, 12),
|
|
22279
|
-
hex.slice(12, 16),
|
|
22280
|
-
hex.slice(16, 20),
|
|
22281
|
-
hex.slice(20)
|
|
22282
|
-
].join("-");
|
|
22283
|
-
}
|
|
22284
|
-
__name(deriveUuidV5, "deriveUuidV5");
|
|
22285
|
-
|
|
22286
22563
|
// src/tools/meta-publish-attempt-id.ts
|
|
22287
22564
|
var META_PUBLISH_NAMESPACE = "7b19c9ac-b4eb-4db1-970e-c2f24ce30b98";
|
|
22288
22565
|
function deriveMetaPublishAttemptId(input) {
|
|
@@ -24931,6 +25208,16 @@ function firstHeader(value) {
|
|
|
24931
25208
|
}
|
|
24932
25209
|
__name(firstHeader, "firstHeader");
|
|
24933
25210
|
var PROXY_FETCH_TIMEOUT_MS = 3e4;
|
|
25211
|
+
var DEFAULT_SUBJECT_TOKEN_REAUTH_WINDOW_MS = 18e4;
|
|
25212
|
+
function subjectTokenReauthWindowMs(env) {
|
|
25213
|
+
const raw = env.MCP_SUBJECT_TOKEN_REAUTH_WINDOW_MS;
|
|
25214
|
+
if (raw === void 0 || raw.trim() === "") {
|
|
25215
|
+
return DEFAULT_SUBJECT_TOKEN_REAUTH_WINDOW_MS;
|
|
25216
|
+
}
|
|
25217
|
+
const parsed = Number.parseInt(raw, 10);
|
|
25218
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_SUBJECT_TOKEN_REAUTH_WINDOW_MS;
|
|
25219
|
+
}
|
|
25220
|
+
__name(subjectTokenReauthWindowMs, "subjectTokenReauthWindowMs");
|
|
24934
25221
|
var INLINE_RENDERABLE_MIMES = /* @__PURE__ */ new Set([
|
|
24935
25222
|
"image/jpeg",
|
|
24936
25223
|
"image/png",
|
|
@@ -25061,7 +25348,8 @@ function resolveDownstreamAuth(req, opts, oauthAnalytics) {
|
|
|
25061
25348
|
credential,
|
|
25062
25349
|
subject,
|
|
25063
25350
|
clientId,
|
|
25064
|
-
isLegacy: false
|
|
25351
|
+
isLegacy: false,
|
|
25352
|
+
subjectExpiresAt: expiresAt
|
|
25065
25353
|
};
|
|
25066
25354
|
}
|
|
25067
25355
|
__name(resolveDownstreamAuth, "resolveDownstreamAuth");
|
|
@@ -25085,6 +25373,14 @@ function createAuthenticatedMcpHandler(opts, oauthAnalytics) {
|
|
|
25085
25373
|
return;
|
|
25086
25374
|
}
|
|
25087
25375
|
const { credential, subject, clientId, isLegacy } = authContext;
|
|
25376
|
+
const reauthWindowMs = subjectTokenReauthWindowMs(process.env);
|
|
25377
|
+
if (reauthWindowMs > 0 && !isLegacy && typeof authContext.subjectExpiresAt === "number" && authContext.subjectExpiresAt * 1e3 <= Date.now() + reauthWindowMs) {
|
|
25378
|
+
res.status(401).set("WWW-Authenticate", `Bearer error="invalid_token", error_description="Access token expired or expiring soon", scope="${MCP_SCOPE}", resource_metadata="${getOAuthProtectedResourceMetadataUrl2(MCP_RESOURCE)}"`).json({
|
|
25379
|
+
error: "invalid_token",
|
|
25380
|
+
error_description: "Access token expired or expiring soon; refresh or re-authenticate."
|
|
25381
|
+
});
|
|
25382
|
+
return;
|
|
25383
|
+
}
|
|
25088
25384
|
const abortController = new AbortController();
|
|
25089
25385
|
const abortRequest = /* @__PURE__ */ __name(() => abortController.abort(), "abortRequest");
|
|
25090
25386
|
req.once("aborted", abortRequest);
|