@peac/mcp-server 0.16.4 → 0.16.5

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 CHANGED
@@ -102,7 +102,7 @@ const result = await handleVerify({
102
102
  input: { jws: 'eyJ...', public_key_base64url: '...' },
103
103
  policy,
104
104
  context: {
105
- version: '0.15.0',
105
+ version: '0.16.5',
106
106
  policyHash,
107
107
  protocolVersion: '2025-11-25',
108
108
  },
package/dist/cli.cjs CHANGED
@@ -4,6 +4,7 @@
4
4
  var crypto$1 = require('crypto');
5
5
  var streamableHttp_js = require('@modelcontextprotocol/sdk/server/streamableHttp.js');
6
6
  var http = require('http');
7
+ var types_js = require('@modelcontextprotocol/sdk/types.js');
7
8
  var promises = require('fs/promises');
8
9
  var commander = require('commander');
9
10
  var zod = require('zod');
@@ -49,7 +50,7 @@ var SERVER_NAME, SERVER_VERSION, MCP_PROTOCOL_VERSION;
49
50
  var init_constants = __esm({
50
51
  "src/infra/constants.ts"() {
51
52
  SERVER_NAME = "peac-mcp-server";
52
- SERVER_VERSION = "0.16.4";
53
+ SERVER_VERSION = "0.16.5";
53
54
  MCP_PROTOCOL_VERSION = "2025-11-25";
54
55
  }
55
56
  });
@@ -114,6 +115,11 @@ var init_session_manager = __esm({
114
115
  }
115
116
  return entry;
116
117
  }
118
+ /** Whether an unexpired session exists; unlike getSession(), does not refresh lastSeen. */
119
+ hasSession(sessionId) {
120
+ const entry = this.sessions.get(sessionId);
121
+ return entry !== void 0 && Date.now() - entry.lastSeen <= this.ttlMs;
122
+ }
117
123
  /** Terminate and clean up a specific session */
118
124
  async terminateSession(sessionId) {
119
125
  const entry = this.sessions.get(sessionId);
@@ -180,8 +186,17 @@ var init_session_manager = __esm({
180
186
  // src/http-transport.ts
181
187
  var http_transport_exports = {};
182
188
  __export(http_transport_exports, {
183
- createHttpTransport: () => createHttpTransport
189
+ MAX_RATE_LIMIT_KEYS: () => MAX_RATE_LIMIT_KEYS,
190
+ createHttpTransport: () => createHttpTransport,
191
+ rateLimitKeyFor: () => rateLimitKeyFor
184
192
  });
193
+ function checkProtocolVersion(req) {
194
+ const protocolVersion = req.headers["mcp-protocol-version"];
195
+ if (protocolVersion && !SUPPORTED_PROTOCOL_VERSIONS.includes(protocolVersion)) {
196
+ return `Unsupported MCP protocol version: ${protocolVersion} (supported versions: ${SUPPORTED_PROTOCOL_VERSIONS.join(", ")})`;
197
+ }
198
+ return null;
199
+ }
185
200
  function isValidSessionId(id) {
186
201
  if (id.length === 0 || id.length > MAX_SESSION_ID_LENGTH) return false;
187
202
  for (let i = 0; i < id.length; i++) {
@@ -190,6 +205,9 @@ function isValidSessionId(id) {
190
205
  }
191
206
  return true;
192
207
  }
208
+ function rateLimitKeyFor(sessionId, clientIp, isLiveSession) {
209
+ return sessionId !== void 0 && isValidSessionId(sessionId) && isLiveSession(sessionId) ? `session:${sessionId}` : `ip:${clientIp}`;
210
+ }
193
211
  function isLoopbackAddr(addr) {
194
212
  return addr.startsWith("127.") || addr === "::1" || addr === "::ffff:127.0.0.1";
195
213
  }
@@ -244,7 +262,12 @@ async function createHttpTransport(options) {
244
262
  const now = Date.now();
245
263
  let entry = rateLimits.get(key);
246
264
  if (!entry || now - entry.windowStart > ONE_MINUTE_MS) {
265
+ if (!entry && rateLimits.size >= MAX_RATE_LIMIT_KEYS) {
266
+ const oldest = rateLimits.keys().next().value;
267
+ if (oldest !== void 0) rateLimits.delete(oldest);
268
+ }
247
269
  entry = { count: 0, windowStart: now };
270
+ rateLimits.delete(key);
248
271
  rateLimits.set(key, entry);
249
272
  }
250
273
  entry.count++;
@@ -423,7 +446,11 @@ async function createHttpTransport(options) {
423
446
  }
424
447
  if (req.method === "POST") {
425
448
  const sessionId = req.headers["mcp-session-id"];
426
- const rateLimitKey = sessionId ?? `ip:${getClientIp(req)}`;
449
+ const rateLimitKey = rateLimitKeyFor(
450
+ sessionId,
451
+ getClientIp(req),
452
+ (id) => sessionManager.hasSession(id)
453
+ );
427
454
  if (!checkRateLimit(rateLimitKey)) {
428
455
  res.writeHead(429, { "Content-Type": "application/json", "Retry-After": "60" });
429
456
  res.end(JSON.stringify({ error: "Rate limit exceeded" }));
@@ -440,9 +467,9 @@ async function createHttpTransport(options) {
440
467
  }
441
468
  const isInit = typeof body === "object" && body !== null && "method" in body && body.method === "initialize";
442
469
  if (isInit) {
443
- const protocolVersion = req.headers["mcp-protocol-version"];
444
- if (protocolVersion && protocolVersion !== MCP_PROTOCOL_VERSION && protocolVersion !== "2025-03-26") {
445
- sendJson(res, 400, { error: `Unsupported MCP protocol version: ${protocolVersion}` });
470
+ const versionError2 = checkProtocolVersion(req);
471
+ if (versionError2) {
472
+ sendJson(res, 400, { error: versionError2 });
446
473
  return;
447
474
  }
448
475
  const accept = req.headers.accept;
@@ -463,6 +490,11 @@ async function createHttpTransport(options) {
463
490
  await entry2.transport.handleRequest(req, res, body);
464
491
  return;
465
492
  }
493
+ const versionError = checkProtocolVersion(req);
494
+ if (versionError) {
495
+ sendJson(res, 400, { error: versionError });
496
+ return;
497
+ }
466
498
  if (!sessionId) {
467
499
  sendJson(res, 400, { error: "Missing Mcp-Session-Id header" });
468
500
  return;
@@ -516,26 +548,27 @@ async function createHttpTransport(options) {
516
548
  ` Sessions: max ${options.maxSessions ?? 100} (${options.maxSessionsPerIp ?? 10}/IP), TTL ${(options.sessionTtlMs ?? 18e5) / 1e3}s
517
549
  `
518
550
  );
519
- process.stderr.write(` Rate limit: ${rateLimitRpm} req/min per session
551
+ process.stderr.write(` Rate limit: ${rateLimitRpm} req/min per session or client address
520
552
  `);
521
553
  process.stderr.write(
522
554
  ` CORS: ${corsOrigins.size > 0 ? [...corsOrigins].join(", ") : "deny all"}
523
555
  `
524
556
  );
525
557
  process.stderr.write(
526
- ` Mode: unprotected (token validation deferred to v0.11.x+; deployer auth via reverse proxy)
558
+ ` Mode: unprotected (no token validation; deploy behind an authenticating reverse proxy)
527
559
  `
528
560
  );
529
561
  resolve2({
530
562
  cleanup: async () => {
531
563
  await sessionManager.cleanup();
532
564
  await new Promise((res) => server.close(() => res()));
533
- }
565
+ },
566
+ rateLimitKeyCount: () => rateLimits.size
534
567
  });
535
568
  });
536
569
  });
537
570
  }
538
- var ONE_MINUTE_MS, DEFAULT_MAX_REQUEST_BYTES, DEFAULT_RATE_LIMIT_RPM, MAX_SESSION_ID_LENGTH;
571
+ var ONE_MINUTE_MS, DEFAULT_MAX_REQUEST_BYTES, DEFAULT_RATE_LIMIT_RPM, MAX_SESSION_ID_LENGTH, MAX_RATE_LIMIT_KEYS, SUPPORTED_PROTOCOL_VERSIONS;
539
572
  var init_http_transport = __esm({
540
573
  "src/http-transport.ts"() {
541
574
  init_session_manager();
@@ -544,6 +577,8 @@ var init_http_transport = __esm({
544
577
  DEFAULT_MAX_REQUEST_BYTES = 1048576;
545
578
  DEFAULT_RATE_LIMIT_RPM = 100;
546
579
  MAX_SESSION_ID_LENGTH = 128;
580
+ MAX_RATE_LIMIT_KEYS = 1e4;
581
+ SUPPORTED_PROTOCOL_VERSIONS = types_js.SUPPORTED_PROTOCOL_VERSIONS;
547
582
  }
548
583
  });
549
584
 
@@ -1990,6 +2025,7 @@ var BUNDLE_TOOL_ANNOTATIONS = {
1990
2025
  openWorldHint: true
1991
2026
  };
1992
2027
  var MAX_INPUT_DEPTH = 10;
2028
+ var MAX_TOOL_INPUT_ELEMENTS = 1024;
1993
2029
  function errorCallToolResult(meta, code, message) {
1994
2030
  const errorDef = kernel.ERRORS[code];
1995
2031
  return {
@@ -2098,7 +2134,10 @@ function createPeacMcpServer(options) {
2098
2134
  { name: SERVER_NAME, version },
2099
2135
  // listChanged: false -- tool set is static per server instance (determined
2100
2136
  // at startup from CLI flags). The server never emits notifications/tools/list_changed.
2101
- { capabilities: { tools: { listChanged: false } } }
2137
+ {
2138
+ capabilities: { tools: { listChanged: false } },
2139
+ maxToolInputElements: MAX_TOOL_INPUT_ELEMENTS
2140
+ }
2102
2141
  );
2103
2142
  const register = server.registerTool.bind(server);
2104
2143
  register(