@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/http.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  PRODUCT_NAME,
3
3
  SERVER_VERSION,
4
4
  createProductBrainServer
5
- } from "./chunk-AHEM4ZWQ.js";
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-OVT3HPIV.js";
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 rateLimit from "express-rate-limit";
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 = rateLimit({
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 mcpLimiter = rateLimit({
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", mcpLimiter, async (req, res) => {
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", mcpLimiter, async (req, res) => {
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", mcpLimiter, async (req, res) => {
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);