@productbrain/mcp 0.0.1-beta.11641 → 0.0.1-beta.11653
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-AHEM4ZWQ.js → chunk-MYUOYKYA.js} +2 -2
- package/dist/{chunk-OVT3HPIV.js → chunk-VX3MF2N3.js} +28 -3
- package/dist/chunk-VX3MF2N3.js.map +1 -0
- package/dist/cli/index.js +1 -1
- package/dist/http.js +46 -24
- package/dist/http.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{setup-JZ45B67U.js → setup-VQC53QUT.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-OVT3HPIV.js.map +0 -1
- /package/dist/{chunk-AHEM4ZWQ.js.map → chunk-MYUOYKYA.js.map} +0 -0
- /package/dist/{setup-JZ45B67U.js.map → setup-VQC53QUT.js.map} +0 -0
package/dist/http.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
PRODUCT_NAME,
|
|
3
3
|
SERVER_VERSION,
|
|
4
4
|
createProductBrainServer
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-MYUOYKYA.js";
|
|
6
6
|
import {
|
|
7
7
|
DEFAULT_CLOUD_URL,
|
|
8
8
|
PASTE_NOW,
|
|
@@ -14,14 +14,14 @@ import {
|
|
|
14
14
|
hashKey,
|
|
15
15
|
parseFallbackUrls,
|
|
16
16
|
runWithAuth
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-VX3MF2N3.js";
|
|
18
18
|
|
|
19
19
|
// src/http.ts
|
|
20
20
|
import { createHash, randomUUID } from "crypto";
|
|
21
21
|
import express from "express";
|
|
22
22
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
23
23
|
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
24
|
-
import
|
|
24
|
+
import rateLimit2 from "express-rate-limit";
|
|
25
25
|
|
|
26
26
|
// src/brand/logo-markup.ts
|
|
27
27
|
var SIZE_CLASSES = {
|
|
@@ -163,6 +163,9 @@ function send429(res, built) {
|
|
|
163
163
|
res.status(built.status).json(built.body);
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
// src/mcpRateLimit.ts
|
|
167
|
+
import rateLimit from "express-rate-limit";
|
|
168
|
+
|
|
166
169
|
// src/authLockout.ts
|
|
167
170
|
var AUTH_FAILURE_MAX = 10;
|
|
168
171
|
var AUTH_FAILURE_WINDOW_MS = 5 * 6e4;
|
|
@@ -238,6 +241,41 @@ function keyVerdictGate(verdict, failures, credentialId, now) {
|
|
|
238
241
|
return "unavailable";
|
|
239
242
|
}
|
|
240
243
|
|
|
244
|
+
// src/mcpRateLimit.ts
|
|
245
|
+
var MCP_KEY_LIMIT = 120;
|
|
246
|
+
var MCP_IP_CEILING = 1200;
|
|
247
|
+
function mcpRateKey(req) {
|
|
248
|
+
const auth = resolveBearerAuth(req.headers?.authorization);
|
|
249
|
+
return auth.status === "absent" ? `ip:${req.ip ?? ""}` : `key:${auth.credentialId}`;
|
|
250
|
+
}
|
|
251
|
+
function limiter(max, windowMs, keyGenerator) {
|
|
252
|
+
return rateLimit({
|
|
253
|
+
windowMs,
|
|
254
|
+
max,
|
|
255
|
+
standardHeaders: true,
|
|
256
|
+
legacyHeaders: false,
|
|
257
|
+
...keyGenerator ? { keyGenerator } : {},
|
|
258
|
+
handler: (req, res) => {
|
|
259
|
+
const r = req.rateLimit;
|
|
260
|
+
const reset = r?.resetTime?.getTime?.() ?? Date.now() + windowMs;
|
|
261
|
+
send429(res, buildRateLimitError({
|
|
262
|
+
retryAfterSeconds: Math.max(1, Math.ceil((reset - Date.now()) / 1e3)),
|
|
263
|
+
limit: r?.limit ?? max,
|
|
264
|
+
current: r?.used ?? 0,
|
|
265
|
+
id: req.body?.id ?? null
|
|
266
|
+
// mirror the JSON-RPC id when a POST body is present
|
|
267
|
+
}));
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
function mcpLimiters(opts = {}) {
|
|
272
|
+
const windowMs = opts.windowMs ?? 6e4;
|
|
273
|
+
return [
|
|
274
|
+
limiter(opts.ipCeiling ?? MCP_IP_CEILING, windowMs),
|
|
275
|
+
limiter(opts.keyLimit ?? MCP_KEY_LIMIT, windowMs, mcpRateKey)
|
|
276
|
+
];
|
|
277
|
+
}
|
|
278
|
+
|
|
241
279
|
// src/lib/oauthRedirectAllowlist.ts
|
|
242
280
|
var DEFAULT_OAUTH_REDIRECT_URIS = [
|
|
243
281
|
"https://claude.ai/api/mcp/auth_callback",
|
|
@@ -412,7 +450,7 @@ function logOAuthDoor(door, status, reason, uris) {
|
|
|
412
450
|
process.stderr.write(`[oauth] door=${door} status=${status} reason=${JSON.stringify(reason)}${list}
|
|
413
451
|
`);
|
|
414
452
|
}
|
|
415
|
-
var authLimiter =
|
|
453
|
+
var authLimiter = rateLimit2({
|
|
416
454
|
windowMs: 6e4,
|
|
417
455
|
max: 20,
|
|
418
456
|
standardHeaders: true,
|
|
@@ -1155,23 +1193,7 @@ app.post(
|
|
|
1155
1193
|
res.json(tokenResponse(pending.apiKey));
|
|
1156
1194
|
}
|
|
1157
1195
|
);
|
|
1158
|
-
var
|
|
1159
|
-
windowMs: 6e4,
|
|
1160
|
-
max: 120,
|
|
1161
|
-
standardHeaders: true,
|
|
1162
|
-
legacyHeaders: false,
|
|
1163
|
-
handler: (req, res) => {
|
|
1164
|
-
const r = req.rateLimit;
|
|
1165
|
-
const reset = r?.resetTime?.getTime?.() ?? Date.now() + 6e4;
|
|
1166
|
-
send429(res, buildRateLimitError({
|
|
1167
|
-
retryAfterSeconds: Math.max(1, Math.ceil((reset - Date.now()) / 1e3)),
|
|
1168
|
-
limit: r?.limit ?? 120,
|
|
1169
|
-
current: r?.used ?? 0,
|
|
1170
|
-
id: req.body?.id ?? null
|
|
1171
|
-
// mirror the JSON-RPC id when a POST body is present
|
|
1172
|
-
}));
|
|
1173
|
-
}
|
|
1174
|
-
});
|
|
1196
|
+
var mcpLimit = mcpLimiters();
|
|
1175
1197
|
var authFailures = /* @__PURE__ */ new Map();
|
|
1176
1198
|
app.get("/health", (_req, res) => {
|
|
1177
1199
|
res.json({ status: "ok", version: SERVER_VERSION, transport: "http" });
|
|
@@ -1237,7 +1259,7 @@ function logSessionLifecycle(event, sessionId, reason) {
|
|
|
1237
1259
|
process.stderr.write(`[HTTP] ${ts} ${event} session=${sessionId}${r}
|
|
1238
1260
|
`);
|
|
1239
1261
|
}
|
|
1240
|
-
app.post("/mcp",
|
|
1262
|
+
app.post("/mcp", ...mcpLimit, async (req, res) => {
|
|
1241
1263
|
const nowTs = Date.now();
|
|
1242
1264
|
const auth = resolveBearerAuth(req.headers?.authorization);
|
|
1243
1265
|
const lockout = authLockoutError(authFailures, auth, nowTs, req.body?.id ?? null);
|
|
@@ -1391,7 +1413,7 @@ app.post("/mcp", mcpLimiter, async (req, res) => {
|
|
|
1391
1413
|
}
|
|
1392
1414
|
}
|
|
1393
1415
|
});
|
|
1394
|
-
app.get("/mcp",
|
|
1416
|
+
app.get("/mcp", ...mcpLimit, async (req, res) => {
|
|
1395
1417
|
const nowTs = Date.now();
|
|
1396
1418
|
const auth = resolveBearerAuth(req.headers?.authorization);
|
|
1397
1419
|
const lockout = authLockoutError(authFailures, auth, nowTs, req.body?.id ?? null);
|
|
@@ -1438,7 +1460,7 @@ app.get("/mcp", mcpLimiter, async (req, res) => {
|
|
|
1438
1460
|
logRequest("GET", "error", sessionId);
|
|
1439
1461
|
}
|
|
1440
1462
|
});
|
|
1441
|
-
app.delete("/mcp",
|
|
1463
|
+
app.delete("/mcp", ...mcpLimit, async (req, res) => {
|
|
1442
1464
|
const nowTs = Date.now();
|
|
1443
1465
|
const auth = resolveBearerAuth(req.headers?.authorization);
|
|
1444
1466
|
const lockout = authLockoutError(authFailures, auth, nowTs, req.body?.id ?? null);
|