@productmaker/mcp 8.3.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 +4 -0
- package/dist/http.js +154 -118
- package/dist/stdio.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -268,6 +268,10 @@ The `base64` field was removed. MCP JSON-RPC transports truncate large tool argu
|
|
|
268
268
|
|
|
269
269
|
## Version history
|
|
270
270
|
|
|
271
|
+
### 8.3.1 — Refresh token garantizado
|
|
272
|
+
|
|
273
|
+
- OAuth alojado: el proxy `/oauth/authorize` añade `offline_access` al scope antes de redirigir a Auth0. Los hosts que siguen el spec piden solo `mcp:access` (lo que anuncian el challenge y la metadata del recurso) y sin esto Auth0 nunca emitía refresh token.
|
|
274
|
+
|
|
271
275
|
### 8.3.0 — Idempotencia de tools pagadas + refresh OAuth
|
|
272
276
|
|
|
273
277
|
- Tools pagadas (`create_product_task`, `create_service_video`, `generate_video_creative`, `generate_free_video`, `generate_image_creatives`, `generate_landing`, `create_creative_variant`): envían un `Idempotency-Key` derivado de los argumentos semánticos (ventana de 15 min). Un reintento con los mismos argumentos devuelve la misma tarea (`replayed: true`) sin segundo cargo; una tarea en `error` sí se reintenta.
|
package/dist/http.js
CHANGED
|
@@ -16815,6 +16815,121 @@ var Auth0TokenVerifier = class {
|
|
|
16815
16815
|
}
|
|
16816
16816
|
};
|
|
16817
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
|
+
|
|
16818
16933
|
// src/auth/oauth-proxy.ts
|
|
16819
16934
|
import { createHmac as createHmac2, hkdfSync, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
16820
16935
|
import express from "express";
|
|
@@ -16901,6 +17016,15 @@ var OAUTH_PROXY_PATHS = {
|
|
|
16901
17016
|
token: "/oauth/token"
|
|
16902
17017
|
};
|
|
16903
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");
|
|
16904
17028
|
function firstQueryValue(value) {
|
|
16905
17029
|
if (typeof value === "string") return value;
|
|
16906
17030
|
if (Array.isArray(value) && typeof value[0] === "string") return value[0];
|
|
@@ -16914,6 +17038,12 @@ function badRequest(res, description) {
|
|
|
16914
17038
|
});
|
|
16915
17039
|
}
|
|
16916
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");
|
|
16917
17047
|
function createOAuthProxyRouter(options) {
|
|
16918
17048
|
const router = express.Router();
|
|
16919
17049
|
const stateKey3 = deriveStateKey(options.staticClient.clientSecret);
|
|
@@ -16937,13 +17067,14 @@ function createOAuthProxyRouter(options) {
|
|
|
16937
17067
|
}
|
|
16938
17068
|
const upstream = new URL(options.authorizationEndpoint);
|
|
16939
17069
|
for (const [key, value] of Object.entries(req.query)) {
|
|
16940
|
-
if (key === "redirect_uri" || key === "state") continue;
|
|
17070
|
+
if (key === "redirect_uri" || key === "state" || key === "scope") continue;
|
|
16941
17071
|
for (const v of Array.isArray(value) ? value : [
|
|
16942
17072
|
value
|
|
16943
17073
|
]) {
|
|
16944
17074
|
if (typeof v === "string") upstream.searchParams.append(key, v);
|
|
16945
17075
|
}
|
|
16946
17076
|
}
|
|
17077
|
+
upstream.searchParams.set("scope", withRefreshScope(firstQueryValue(req.query.scope)));
|
|
16947
17078
|
upstream.searchParams.set("redirect_uri", PROXY_CALLBACK_URL);
|
|
16948
17079
|
upstream.searchParams.set("state", sealState(stateKey3, {
|
|
16949
17080
|
redirectUri,
|
|
@@ -17023,6 +17154,14 @@ function createOAuthProxyRouter(options) {
|
|
|
17023
17154
|
});
|
|
17024
17155
|
return;
|
|
17025
17156
|
}
|
|
17157
|
+
try {
|
|
17158
|
+
options.onTokenExchange?.({
|
|
17159
|
+
grantType,
|
|
17160
|
+
status: upstream.status,
|
|
17161
|
+
refreshTokenIssued: responseIssuesRefreshToken(payload)
|
|
17162
|
+
});
|
|
17163
|
+
} catch {
|
|
17164
|
+
}
|
|
17026
17165
|
res.status(upstream.status).type(upstream.headers.get("content-type") ?? "application/json").send(payload);
|
|
17027
17166
|
});
|
|
17028
17167
|
return router;
|
|
@@ -17164,11 +17303,23 @@ function createMetadataRouter(config, metadata, fetchFn) {
|
|
|
17164
17303
|
staticClient: config.staticClient,
|
|
17165
17304
|
authorizationEndpoint: metadata.authorization_endpoint,
|
|
17166
17305
|
tokenEndpoint: metadata.token_endpoint,
|
|
17167
|
-
fetchFn
|
|
17306
|
+
fetchFn,
|
|
17307
|
+
onTokenExchange: captureTokenExchange
|
|
17168
17308
|
}));
|
|
17169
17309
|
return router;
|
|
17170
17310
|
}
|
|
17171
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");
|
|
17172
17323
|
function createHttpOAuth(config, metadata, fetchFn) {
|
|
17173
17324
|
const verifier = new Auth0TokenVerifier(config, metadata.jwks_uri);
|
|
17174
17325
|
const oboClient = new Auth0OboClient(config, {
|
|
@@ -17447,121 +17598,6 @@ function registerProductMakerPrompts(server) {
|
|
|
17447
17598
|
}
|
|
17448
17599
|
__name(registerProductMakerPrompts, "registerProductMakerPrompts");
|
|
17449
17600
|
|
|
17450
|
-
// src/oauth-analytics.ts
|
|
17451
|
-
import { PostHog } from "posthog-node";
|
|
17452
|
-
var DEFAULT_POSTHOG_HOST = "https://us.i.posthog.com";
|
|
17453
|
-
var REDACTED = "[REDACTED]";
|
|
17454
|
-
var SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
17455
|
-
"token",
|
|
17456
|
-
"authorization",
|
|
17457
|
-
"apikey",
|
|
17458
|
-
"clientsecret",
|
|
17459
|
-
"subjecttoken",
|
|
17460
|
-
"accesstoken",
|
|
17461
|
-
"refreshtoken",
|
|
17462
|
-
"idtoken",
|
|
17463
|
-
"code",
|
|
17464
|
-
"secret",
|
|
17465
|
-
"password"
|
|
17466
|
-
]);
|
|
17467
|
-
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;
|
|
17468
|
-
var BEARER_TOKEN_PATTERN = /\b(bearer\s+)([A-Za-z0-9._~+/=-]+)/gi;
|
|
17469
|
-
var client = null;
|
|
17470
|
-
var KNOWN_MCP_OAUTH_ANALYTICS_EVENTS = [
|
|
17471
|
-
MCP_OAUTH_ANALYTICS_EVENTS.STARTED,
|
|
17472
|
-
MCP_OAUTH_ANALYTICS_EVENTS.CONNECTED,
|
|
17473
|
-
MCP_OAUTH_ANALYTICS_EVENTS.FAILED,
|
|
17474
|
-
MCP_OAUTH_ANALYTICS_EVENTS.SAFE_VARIANT
|
|
17475
|
-
];
|
|
17476
|
-
function getPostHogClient() {
|
|
17477
|
-
if (client) return client;
|
|
17478
|
-
const apiKey = process.env.POSTHOG_API_KEY;
|
|
17479
|
-
if (!apiKey) return null;
|
|
17480
|
-
client = new PostHog(apiKey, {
|
|
17481
|
-
host: process.env.POSTHOG_HOST || DEFAULT_POSTHOG_HOST,
|
|
17482
|
-
flushAt: 20,
|
|
17483
|
-
flushInterval: 1e4
|
|
17484
|
-
});
|
|
17485
|
-
return client;
|
|
17486
|
-
}
|
|
17487
|
-
__name(getPostHogClient, "getPostHogClient");
|
|
17488
|
-
function normalizeKey(key) {
|
|
17489
|
-
return key.replace(/[^a-z0-9]/gi, "").toLowerCase();
|
|
17490
|
-
}
|
|
17491
|
-
__name(normalizeKey, "normalizeKey");
|
|
17492
|
-
function isSensitiveKey(key) {
|
|
17493
|
-
const normalizedKey = normalizeKey(key);
|
|
17494
|
-
return SENSITIVE_KEYS.has(normalizedKey) || normalizedKey.endsWith("token") || normalizedKey.endsWith("secret");
|
|
17495
|
-
}
|
|
17496
|
-
__name(isSensitiveKey, "isSensitiveKey");
|
|
17497
|
-
function sanitizeUrl(value) {
|
|
17498
|
-
try {
|
|
17499
|
-
const parsed = new URL(value);
|
|
17500
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
17501
|
-
return value;
|
|
17502
|
-
}
|
|
17503
|
-
parsed.username = "";
|
|
17504
|
-
parsed.password = "";
|
|
17505
|
-
parsed.search = "";
|
|
17506
|
-
parsed.hash = "";
|
|
17507
|
-
return parsed.toString();
|
|
17508
|
-
} catch {
|
|
17509
|
-
return value;
|
|
17510
|
-
}
|
|
17511
|
-
}
|
|
17512
|
-
__name(sanitizeUrl, "sanitizeUrl");
|
|
17513
|
-
function sanitizeString(value) {
|
|
17514
|
-
const redacted = value.replace(SENSITIVE_ASSIGNMENT_PATTERN, (_match, prefix, scheme, quote) => {
|
|
17515
|
-
const replacement = quote ? `${quote}${REDACTED}${quote}` : REDACTED;
|
|
17516
|
-
return `${prefix}${scheme ? `${scheme} ` : ""}${replacement}`;
|
|
17517
|
-
}).replace(BEARER_TOKEN_PATTERN, `$1${REDACTED}`);
|
|
17518
|
-
const sanitizedUrl = sanitizeUrl(redacted);
|
|
17519
|
-
if (sanitizedUrl !== redacted) return sanitizedUrl;
|
|
17520
|
-
return redacted.replace(/https?:\/\/[^\s"'<>]+/gi, sanitizeUrl);
|
|
17521
|
-
}
|
|
17522
|
-
__name(sanitizeString, "sanitizeString");
|
|
17523
|
-
function sanitizeValue(value) {
|
|
17524
|
-
if (Array.isArray(value)) {
|
|
17525
|
-
return value.map((entry) => sanitizeValue(entry));
|
|
17526
|
-
}
|
|
17527
|
-
if (value && typeof value === "object") {
|
|
17528
|
-
return Object.fromEntries(Object.entries(value).map(([key, entry]) => {
|
|
17529
|
-
if (isSensitiveKey(key)) {
|
|
17530
|
-
return [
|
|
17531
|
-
key,
|
|
17532
|
-
REDACTED
|
|
17533
|
-
];
|
|
17534
|
-
}
|
|
17535
|
-
return [
|
|
17536
|
-
key,
|
|
17537
|
-
sanitizeValue(entry)
|
|
17538
|
-
];
|
|
17539
|
-
}));
|
|
17540
|
-
}
|
|
17541
|
-
if (typeof value === "string") {
|
|
17542
|
-
return sanitizeString(value);
|
|
17543
|
-
}
|
|
17544
|
-
return value;
|
|
17545
|
-
}
|
|
17546
|
-
__name(sanitizeValue, "sanitizeValue");
|
|
17547
|
-
var OAuthAnalytics = class {
|
|
17548
|
-
static {
|
|
17549
|
-
__name(this, "OAuthAnalytics");
|
|
17550
|
-
}
|
|
17551
|
-
capture(event, subject, properties) {
|
|
17552
|
-
const posthog = getPostHogClient();
|
|
17553
|
-
if (!posthog) return;
|
|
17554
|
-
try {
|
|
17555
|
-
posthog.capture({
|
|
17556
|
-
distinctId: subject ?? "mcp_oauth_aggregate",
|
|
17557
|
-
event,
|
|
17558
|
-
properties: sanitizeValue(properties)
|
|
17559
|
-
});
|
|
17560
|
-
} catch {
|
|
17561
|
-
}
|
|
17562
|
-
}
|
|
17563
|
-
};
|
|
17564
|
-
|
|
17565
17601
|
// src/server.ts
|
|
17566
17602
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
17567
17603
|
|
|
@@ -17749,7 +17785,7 @@ var ApiClient = class {
|
|
|
17749
17785
|
};
|
|
17750
17786
|
|
|
17751
17787
|
// src/index.ts
|
|
17752
|
-
var MCP_VERSION = "8.3.
|
|
17788
|
+
var MCP_VERSION = "8.3.1";
|
|
17753
17789
|
|
|
17754
17790
|
// src/meta-upload-app.ts
|
|
17755
17791
|
import { readFileSync } from "fs";
|
package/dist/stdio.js
CHANGED
|
@@ -16117,7 +16117,7 @@ var ApiClient = class {
|
|
|
16117
16117
|
};
|
|
16118
16118
|
|
|
16119
16119
|
// src/index.ts
|
|
16120
|
-
var MCP_VERSION = "8.3.
|
|
16120
|
+
var MCP_VERSION = "8.3.1";
|
|
16121
16121
|
|
|
16122
16122
|
// src/meta-upload-app.ts
|
|
16123
16123
|
import { readFileSync } from "fs";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@productmaker/mcp",
|
|
3
|
-
"version": "8.3.
|
|
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.",
|