@faapi/mcp 3.3.0 → 4.1.0

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/index.d.ts CHANGED
@@ -104,30 +104,52 @@ interface McpSession {
104
104
  /** 已订阅的资源 URI 集合(resources/subscribe 注册) */
105
105
  subscribedResources: Set<string>;
106
106
  }
107
+ /** SessionManager 构造选项 */
108
+ interface SessionManagerOptions {
109
+ /** 会话空闲超时(毫秒)。默认 30 分钟;0 表示永不过期 */
110
+ ttl?: number;
111
+ /**
112
+ * 会话数上限,超过时按 LRU(最久未活动)淘汰。默认 1000;0 表示不设上限。
113
+ * 淘汰在 create 时惰性执行(先清过期会话,仍超限才淘汰最久未活动的)
114
+ */
115
+ maxSessions?: number;
116
+ }
107
117
  /**
108
118
  * 内存会话管理器
109
119
  *
110
120
  * 生产环境如需多实例共享会话,可替换为 Redis 等外部存储实现。
111
- *
112
- * @param ttl 会话空闲超时(毫秒),超过此时间未活动的会话自动过期。默认 30 分钟。设为 0 表示永不过期。
113
121
  */
114
122
  declare class SessionManager {
115
123
  private sessions;
116
124
  private readonly ttl;
117
- constructor(ttl?: number);
125
+ private readonly maxSessions;
126
+ constructor(options?: number | SessionManagerOptions);
118
127
  /** 是否启用 TTL 过期检查(ttl > 0) */
119
128
  private get ttlEnabled();
129
+ /** 是否启用会话数上限(maxSessions > 0) */
130
+ private get capEnabled();
120
131
  /** 创建新会话,返回 session 对象 */
121
132
  create(): McpSession;
133
+ /** 淘汰最久未活动的会话;无会话可淘汰返回 null */
134
+ private evictOldest;
122
135
  /** 按 ID 获取会话(更新最后活动时间,过期返回 undefined) */
123
136
  get(id: string): McpSession | undefined;
124
137
  /** 是否存在(不更新活动时间,过期会话返回 false) */
125
138
  has(id: string): boolean;
139
+ /**
140
+ * 续期会话(仅刷新 lastActivity,不创建、不校验存在性以外的语义)
141
+ *
142
+ * SSE 心跳路径使用:只收推送不发请求的客户端依赖心跳维持会话活跃,
143
+ * 避免活跃长连接因空闲 TTL 被过期清理。
144
+ *
145
+ * @returns 会话存在且未过期返回 true;不存在或已过期返回 false(不复活过期会话)
146
+ */
147
+ touch(id: string): boolean;
126
148
  /** 销毁会话(关闭所有 SSE 订阅者) */
127
149
  delete(id: string): boolean;
128
150
  /** 当前会话数(含可能尚未清理的过期会话) */
129
151
  get size(): number;
130
- /** 获取所有 session ID 列表(用于全局广播通知) */
152
+ /** 获取所有 session ID 列表(用于全局广播通知)——先清扫过期会话 */
131
153
  allSessionIds(): string[];
132
154
  /** 清空所有会话 */
133
155
  clear(): void;
@@ -143,7 +165,7 @@ declare class SessionManager {
143
165
  subscribeResource(sessionId: string, uri: string): boolean;
144
166
  /** 取消资源订阅(从 session.subscribedResources 移除 uri) */
145
167
  unsubscribeResource(sessionId: string, uri: string): boolean;
146
- /** 找出所有订阅了指定 URI 的 session id 列表 */
168
+ /** 找出所有订阅了指定 URI 的 session id 列表——先清扫过期会话 */
147
169
  findSubscribersOfUri(uri: string): string[];
148
170
  /** 关闭 session 的所有订阅者(用于 session 销毁/过期) */
149
171
  private closeSubscribers;
@@ -180,6 +202,11 @@ interface McpServerOptions {
180
202
  instructions?: string;
181
203
  /** 会话空闲超时(毫秒),默认 30 分钟;设为 0 表示永不过期 */
182
204
  sessionTtl?: number;
205
+ /**
206
+ * 会话数上限,超过时按 LRU(最久未活动)淘汰,默认 1000;0 表示不设上限。
207
+ * 防 initialize 洪水在 TTL 窗口内无限堆内存
208
+ */
209
+ sessionMaxSessions?: number;
183
210
  /** GET SSE 流心跳间隔(毫秒),默认 30 秒 */
184
211
  sseHeartbeatMs?: number;
185
212
  /** list 方法默认每页项数,默认 100 */
@@ -413,14 +440,21 @@ declare class McpServer {
413
440
  * 删除已注册 resource
414
441
  *
415
442
  * - `resourcesListChanged: true` 时自动推送 `notifications/resources/list_changed`
443
+ * - `{ silent: true }` 跳过推送——批量重建场景(如 schemaServer 先清后建)在
444
+ * 末尾统一广播一次,避免 N 次删除产生 N 次广播风暴
416
445
  */
417
- removeResource(uri: string): boolean;
446
+ removeResource(uri: string, options?: {
447
+ silent?: boolean;
448
+ }): boolean;
418
449
  /**
419
450
  * 删除已注册 resource template
420
451
  *
421
452
  * - `resourcesListChanged: true` 时自动推送 `notifications/resources/list_changed`
453
+ * - `{ silent: true }` 跳过推送(批量重建场景,同 removeResource)
422
454
  */
423
- removeResourceTemplate(uriTemplate: string): boolean;
455
+ removeResourceTemplate(uriTemplate: string, options?: {
456
+ silent?: boolean;
457
+ }): boolean;
424
458
  /**
425
459
  * 删除已注册 prompt
426
460
  *
@@ -533,6 +567,19 @@ declare function createMcpServer(options: McpServerOptions): McpServer;
533
567
  * 实现:POST 返回 JSON,GET 返回 SSE 流(仅心跳,不推送业务消息),DELETE 销毁会话。
534
568
  */
535
569
 
570
+ /**
571
+ * Streamable HTTP transport 选项
572
+ */
573
+ interface McpHttpOptions {
574
+ /**
575
+ * 允许的 Origin 列表(MCP 规范安全要求:防 DNS rebinding 攻击)
576
+ *
577
+ * 请求携带 Origin 头且不在列表内 → 403;未携带 Origin(非浏览器客户端)放行。
578
+ * 未配置(undefined)时不校验 Origin——本地开发保持零配置;
579
+ * 生产环境暴露到网络时必须配置(浏览器是最常见的 MCP 客户端载体)。
580
+ */
581
+ allowedOrigins?: string[];
582
+ }
536
583
  /**
537
584
  * 处理 MCP HTTP 请求
538
585
  *
@@ -544,7 +591,7 @@ declare function createMcpServer(options: McpServerOptions): McpServer;
544
591
  * }
545
592
  * ```
546
593
  */
547
- declare function handleMcpRequest(request: Request, server: McpServer): Promise<Response>;
594
+ declare function handleMcpRequest(request: Request, server: McpServer, options?: McpHttpOptions): Promise<Response>;
548
595
 
549
596
  /**
550
597
  * faapi 适配器:把 MCP Server 挂载到 faapi 路由
@@ -574,8 +621,11 @@ declare function handleMcpRequest(request: Request, server: McpServer): Promise<
574
621
  *
575
622
  * 返回 { POST, GET, DELETE },可直接在 handler.ts 中导出。
576
623
  * faapi 按参数名注入 ctx,函数内通过 ctx.request 获取 Web Request。
624
+ *
625
+ * @param options 传输选项(如 `allowedOrigins`——MCP 规范建议校验 Origin 防 DNS
626
+ * rebinding;把 /mcp 暴露到生产网络时建议配置)
577
627
  */
578
- declare function createMcpHandler(mcp: McpServer): {
628
+ declare function createMcpHandler(mcp: McpServer, options?: McpHttpOptions): {
579
629
  POST: (ctx: {
580
630
  request: Request;
581
631
  }) => Promise<Response>;
@@ -594,7 +644,9 @@ declare function createMcpHandler(mcp: McpServer): {
594
644
  *
595
645
  * 使用 Node.js 原生 `Readable.toWeb(req)` 转换 body,正确处理 chunked transfer
596
646
  * (多 chunk 累积)、backpressure 和 stream error。
647
+ *
648
+ * @param options 传输选项(同 createMcpHandler)
597
649
  */
598
- declare function createMcpNodeHandler(mcp: McpServer): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
650
+ declare function createMcpNodeHandler(mcp: McpServer, options?: McpHttpOptions): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
599
651
 
600
652
  export { type CompletionContext, type CompletionHandler, type CompletionRef, type CompletionResult, ErrorCode, type JsonRpcError, type JsonRpcErrorResponse, type JsonRpcMessage, type JsonRpcNotification, JsonRpcParseError, type JsonRpcRequest, type JsonRpcResultResponse, type LoggingLevel, type McpPromptArgument, type McpPromptContent, type McpPromptDefinition, type McpPromptGetResult, type McpPromptMessage, type McpResourceContent, type McpResourceDefinition, type McpResourceReadResult, type McpResourceTemplateDefinition, McpServer, type McpServerOptions, type McpSession, type McpToolDefinition, type McpToolResult, type MethodHandler, type MethodHandlerResult, PROTOCOL_VERSION, type PromptGetExtra, type RequestExtra, type ResourceReadExtra, type ResourceTemplateReadExtra, SUPPORTED_PROTOCOL_VERSIONS, type SendLoggingFn, type SendProgressFn, SessionManager, type SseSubscriber, type ToolCallExtra, createErrorResponse, createMcpHandler, createMcpNodeHandler, createMcpServer, createResultResponse, handleMcpRequest, isErrorResponse, isNotification, isRequest, isResultResponse, parseJsonRpcMessage };
package/dist/index.js CHANGED
@@ -42,6 +42,23 @@ function parseJsonRpcMessage(data) {
42
42
  }
43
43
  return [parseSingleMessage(data)];
44
44
  }
45
+ function parseJsonRpcBatch(data) {
46
+ const messages = [];
47
+ const invalid = [];
48
+ for (const item of data) {
49
+ try {
50
+ messages.push(parseSingleMessage(item));
51
+ } catch (err) {
52
+ const reason = err instanceof Error ? err.message : String(err);
53
+ invalid.push({
54
+ jsonrpc: "2.0",
55
+ id: null,
56
+ error: { code: ErrorCode.ParseError, message: reason }
57
+ });
58
+ }
59
+ }
60
+ return { messages, invalid };
61
+ }
45
62
  var JsonRpcParseError = class extends Error {
46
63
  constructor(message) {
47
64
  super(message);
@@ -101,19 +118,33 @@ var LOGGING_LEVEL_ORDER = {
101
118
  emergency: 80
102
119
  };
103
120
  var DEFAULT_TTL = 30 * 60 * 1e3;
121
+ var DEFAULT_MAX_SESSIONS = 1e3;
104
122
  var SessionManager = class {
105
123
  sessions = /* @__PURE__ */ new Map();
106
124
  ttl;
107
- constructor(ttl = DEFAULT_TTL) {
108
- this.ttl = ttl;
125
+ maxSessions;
126
+ constructor(options = DEFAULT_TTL) {
127
+ const opts = typeof options === "number" ? { ttl: options } : options;
128
+ this.ttl = opts.ttl ?? DEFAULT_TTL;
129
+ this.maxSessions = opts.maxSessions ?? DEFAULT_MAX_SESSIONS;
109
130
  }
110
131
  /** 是否启用 TTL 过期检查(ttl > 0) */
111
132
  get ttlEnabled() {
112
133
  return this.ttl > 0;
113
134
  }
135
+ /** 是否启用会话数上限(maxSessions > 0) */
136
+ get capEnabled() {
137
+ return this.maxSessions > 0;
138
+ }
114
139
  /** 创建新会话,返回 session 对象 */
115
140
  create() {
116
141
  if (this.ttlEnabled) this.cleanupExpired();
142
+ if (this.capEnabled) {
143
+ while (this.sessions.size >= this.maxSessions) {
144
+ const oldest = this.evictOldest();
145
+ if (!oldest) break;
146
+ }
147
+ }
117
148
  const now = Date.now();
118
149
  const session = {
119
150
  id: randomUUID(),
@@ -128,6 +159,22 @@ var SessionManager = class {
128
159
  this.sessions.set(session.id, session);
129
160
  return session;
130
161
  }
162
+ /** 淘汰最久未活动的会话;无会话可淘汰返回 null */
163
+ evictOldest() {
164
+ let oldestId = null;
165
+ let oldestActivity = Infinity;
166
+ for (const [id, session] of this.sessions) {
167
+ if (session.lastActivity < oldestActivity) {
168
+ oldestActivity = session.lastActivity;
169
+ oldestId = id;
170
+ }
171
+ }
172
+ if (oldestId === null) return null;
173
+ const victim = this.sessions.get(oldestId);
174
+ if (victim) this.closeSubscribers(victim);
175
+ this.sessions.delete(oldestId);
176
+ return oldestId;
177
+ }
131
178
  /** 按 ID 获取会话(更新最后活动时间,过期返回 undefined) */
132
179
  get(id) {
133
180
  const session = this.sessions.get(id);
@@ -151,6 +198,25 @@ var SessionManager = class {
151
198
  }
152
199
  return true;
153
200
  }
201
+ /**
202
+ * 续期会话(仅刷新 lastActivity,不创建、不校验存在性以外的语义)
203
+ *
204
+ * SSE 心跳路径使用:只收推送不发请求的客户端依赖心跳维持会话活跃,
205
+ * 避免活跃长连接因空闲 TTL 被过期清理。
206
+ *
207
+ * @returns 会话存在且未过期返回 true;不存在或已过期返回 false(不复活过期会话)
208
+ */
209
+ touch(id) {
210
+ const session = this.sessions.get(id);
211
+ if (!session) return false;
212
+ if (this.ttlEnabled && Date.now() - session.lastActivity > this.ttl) {
213
+ this.closeSubscribers(session);
214
+ this.sessions.delete(id);
215
+ return false;
216
+ }
217
+ session.lastActivity = Date.now();
218
+ return true;
219
+ }
154
220
  /** 销毁会话(关闭所有 SSE 订阅者) */
155
221
  delete(id) {
156
222
  const session = this.sessions.get(id);
@@ -163,8 +229,9 @@ var SessionManager = class {
163
229
  get size() {
164
230
  return this.sessions.size;
165
231
  }
166
- /** 获取所有 session ID 列表(用于全局广播通知) */
232
+ /** 获取所有 session ID 列表(用于全局广播通知)——先清扫过期会话 */
167
233
  allSessionIds() {
234
+ this.cleanupExpired();
168
235
  return [...this.sessions.keys()];
169
236
  }
170
237
  /** 清空所有会话 */
@@ -193,6 +260,11 @@ var SessionManager = class {
193
260
  broadcastToSession(sessionId, data) {
194
261
  const session = this.sessions.get(sessionId);
195
262
  if (!session) return;
263
+ if (this.ttlEnabled && Date.now() - session.lastActivity > this.ttl) {
264
+ this.closeSubscribers(session);
265
+ this.sessions.delete(sessionId);
266
+ return;
267
+ }
196
268
  const encoder = new TextEncoder();
197
269
  for (const sub of session.subscribers) {
198
270
  try {
@@ -222,8 +294,9 @@ var SessionManager = class {
222
294
  session.subscribedResources.delete(uri);
223
295
  return true;
224
296
  }
225
- /** 找出所有订阅了指定 URI 的 session id 列表 */
297
+ /** 找出所有订阅了指定 URI 的 session id 列表——先清扫过期会话 */
226
298
  findSubscribersOfUri(uri) {
299
+ this.cleanupExpired();
227
300
  const result = [];
228
301
  for (const session of this.sessions.values()) {
229
302
  if (session.subscribedResources.has(uri)) {
@@ -350,7 +423,10 @@ function matchUriTemplate(regex, paramNames, uri) {
350
423
  var McpServer = class {
351
424
  constructor(options) {
352
425
  this.options = options;
353
- this.sessions = new SessionManager(this.options.sessionTtl);
426
+ this.sessions = new SessionManager({
427
+ ttl: this.options.sessionTtl,
428
+ maxSessions: this.options.sessionMaxSessions
429
+ });
354
430
  this.pageSize = this.options.defaultPageSize ?? DEFAULT_PAGE_SIZE;
355
431
  this.toolsListChanged = this.options.toolsListChanged ?? false;
356
432
  this.resourcesListChanged = this.options.resourcesListChanged ?? false;
@@ -482,10 +558,12 @@ var McpServer = class {
482
558
  * 删除已注册 resource
483
559
  *
484
560
  * - `resourcesListChanged: true` 时自动推送 `notifications/resources/list_changed`
561
+ * - `{ silent: true }` 跳过推送——批量重建场景(如 schemaServer 先清后建)在
562
+ * 末尾统一广播一次,避免 N 次删除产生 N 次广播风暴
485
563
  */
486
- removeResource(uri) {
564
+ removeResource(uri, options) {
487
565
  const deleted = this.resources.delete(uri);
488
- if (deleted && this.resourcesListChanged) {
566
+ if (deleted && this.resourcesListChanged && !options?.silent) {
489
567
  this.notifyResourcesListChanged();
490
568
  }
491
569
  return deleted;
@@ -494,10 +572,11 @@ var McpServer = class {
494
572
  * 删除已注册 resource template
495
573
  *
496
574
  * - `resourcesListChanged: true` 时自动推送 `notifications/resources/list_changed`
575
+ * - `{ silent: true }` 跳过推送(批量重建场景,同 removeResource)
497
576
  */
498
- removeResourceTemplate(uriTemplate) {
577
+ removeResourceTemplate(uriTemplate, options) {
499
578
  const deleted = this.resourceTemplates.delete(uriTemplate);
500
- if (deleted && this.resourcesListChanged) {
579
+ if (deleted && this.resourcesListChanged && !options?.silent) {
501
580
  this.notifyResourcesListChanged();
502
581
  }
503
582
  return deleted;
@@ -601,6 +680,13 @@ var McpServer = class {
601
680
  return null;
602
681
  }
603
682
  async handleRequest(request, session) {
683
+ if (session && !session.initialized && request.method !== "initialize") {
684
+ return createErrorResponse(
685
+ request.id,
686
+ ErrorCode.InvalidRequest,
687
+ 'Server not initialized: send "initialize" request and "notifications/initialized" notification first'
688
+ );
689
+ }
604
690
  try {
605
691
  switch (request.method) {
606
692
  case "initialize":
@@ -725,17 +811,22 @@ var McpServer = class {
725
811
  );
726
812
  }
727
813
  const args = params.arguments ?? {};
814
+ let argsForHandler = args;
728
815
  if (tool.inputSchema) {
729
816
  const parsed = tool.inputSchema.safeParse(args);
730
817
  if (!parsed.success) {
731
- return createErrorResponse(
732
- request.id,
733
- ErrorCode.InvalidParams,
734
- "Invalid tool arguments",
735
- parsed.error.issues
736
- );
818
+ const issueText = parsed.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
819
+ return createResultResponse(request.id, {
820
+ content: [
821
+ {
822
+ type: "text",
823
+ text: `Invalid tool arguments: ${issueText}`
824
+ }
825
+ ],
826
+ isError: true
827
+ });
737
828
  }
738
- Object.assign(args, parsed.data);
829
+ argsForHandler = parsed.data;
739
830
  }
740
831
  const progressToken = params._meta?.progressToken;
741
832
  const extra = {
@@ -749,7 +840,7 @@ var McpServer = class {
749
840
  };
750
841
  let result;
751
842
  try {
752
- result = await tool.definition.handler(args, extra);
843
+ result = await tool.definition.handler(argsForHandler, extra);
753
844
  } catch (err) {
754
845
  result = {
755
846
  content: [
@@ -1182,7 +1273,20 @@ function createMcpServer(options) {
1182
1273
 
1183
1274
  // src/streamableHttp.ts
1184
1275
  var DEFAULT_SSE_HEARTBEAT_MS = 3e4;
1185
- async function handleMcpRequest(request, server) {
1276
+ function checkOrigin(request, options) {
1277
+ const allowed = options?.allowedOrigins;
1278
+ if (!allowed || allowed.length === 0) return null;
1279
+ const origin = request.headers.get("origin");
1280
+ if (!origin) return null;
1281
+ if (allowed.includes(origin)) return null;
1282
+ return new Response(JSON.stringify({ error: "Origin not allowed" }), {
1283
+ status: 403,
1284
+ headers: { "Content-Type": "application/json" }
1285
+ });
1286
+ }
1287
+ async function handleMcpRequest(request, server, options) {
1288
+ const originRejected = checkOrigin(request, options);
1289
+ if (originRejected) return originRejected;
1186
1290
  switch (request.method) {
1187
1291
  case "POST":
1188
1292
  return handlePost(request, server);
@@ -1200,6 +1304,12 @@ function handleGet(server, request) {
1200
1304
  let interval;
1201
1305
  let subscriber;
1202
1306
  const sessionId = request.headers.get("mcp-session-id") ?? void 0;
1307
+ if (sessionId && !server.getSessionManager().has(sessionId)) {
1308
+ return jsonResponse(
1309
+ 404,
1310
+ createErrorResponse(null, ErrorCode.RequestTimeout, "Session not found")
1311
+ );
1312
+ }
1203
1313
  const stream = new ReadableStream({
1204
1314
  start(controller) {
1205
1315
  controller.enqueue(encoder.encode(": connected\n\n"));
@@ -1208,11 +1318,17 @@ function handleGet(server, request) {
1208
1318
  }
1209
1319
  interval = setInterval(() => {
1210
1320
  try {
1321
+ if (sessionId) {
1322
+ server.getSessionManager().touch(sessionId);
1323
+ }
1211
1324
  controller.enqueue(encoder.encode(`: keepalive ${Date.now()}
1212
1325
 
1213
1326
  `));
1214
1327
  } catch {
1215
1328
  if (interval) clearInterval(interval);
1329
+ if (subscriber) {
1330
+ server.getSessionManager().removeSubscriber(subscriber);
1331
+ }
1216
1332
  }
1217
1333
  }, heartbeatMs);
1218
1334
  },
@@ -1243,11 +1359,24 @@ async function handlePost(request, server) {
1243
1359
  );
1244
1360
  }
1245
1361
  let messages;
1246
- try {
1247
- messages = parseJsonRpcMessage(body);
1248
- } catch (err) {
1249
- const message = err instanceof JsonRpcParseError ? err.message : "Invalid JSON-RPC message";
1250
- return jsonResponse(400, createErrorResponse(null, ErrorCode.ParseError, message));
1362
+ let invalidResponses = [];
1363
+ if (Array.isArray(body)) {
1364
+ if (body.length === 0) {
1365
+ return jsonResponse(
1366
+ 400,
1367
+ createErrorResponse(null, ErrorCode.ParseError, "Invalid Request: empty batch")
1368
+ );
1369
+ }
1370
+ const batch = parseJsonRpcBatch(body);
1371
+ messages = batch.messages;
1372
+ invalidResponses = batch.invalid;
1373
+ } else {
1374
+ try {
1375
+ messages = parseJsonRpcMessage(body);
1376
+ } catch (err) {
1377
+ const message = err instanceof JsonRpcParseError ? err.message : "Invalid JSON-RPC message";
1378
+ return jsonResponse(400, createErrorResponse(null, ErrorCode.ParseError, message));
1379
+ }
1251
1380
  }
1252
1381
  const requests = messages.filter(isRequest);
1253
1382
  const notifications = messages.filter(isNotification);
@@ -1283,23 +1412,30 @@ async function handlePost(request, server) {
1283
1412
  );
1284
1413
  }
1285
1414
  session = sessionManager.create();
1286
- } else if (requests.length > 0 && !session && sessionId) {
1287
- return jsonResponse(
1288
- 404,
1289
- createErrorResponse(null, ErrorCode.RequestTimeout, "Session not found")
1290
- );
1415
+ } else if (requests.length > 0) {
1416
+ if (!sessionId) {
1417
+ return jsonResponse(
1418
+ 400,
1419
+ createErrorResponse(null, ErrorCode.InvalidRequest, "Missing Mcp-Session-Id header")
1420
+ );
1421
+ }
1422
+ if (!session) {
1423
+ return jsonResponse(
1424
+ 404,
1425
+ createErrorResponse(null, ErrorCode.RequestTimeout, "Session not found")
1426
+ );
1427
+ }
1291
1428
  }
1292
1429
  for (const notification of notifications) {
1293
1430
  await server.handleJsonRpc(notification, session);
1294
1431
  }
1295
- const responses = [];
1296
- for (const req of requests) {
1297
- const response = await server.handleJsonRpc(req, session);
1298
- if (response !== null) {
1299
- responses.push(response);
1300
- }
1301
- }
1302
- if (requests.length === 0) {
1432
+ const handled = await Promise.all(requests.map((req) => server.handleJsonRpc(req, session)));
1433
+ const responses = [
1434
+ ...invalidResponses,
1435
+ // 解析期产生的 error response(解析阶段语义,置于响应数组最前)
1436
+ ...handled.filter((r) => r !== null)
1437
+ ];
1438
+ if (requests.length === 0 && invalidResponses.length === 0) {
1303
1439
  return new Response(null, { status: 202 });
1304
1440
  }
1305
1441
  const responseHeaders = {
@@ -1341,13 +1477,13 @@ function jsonResponse(status, body) {
1341
1477
 
1342
1478
  // src/faapiAdapter.ts
1343
1479
  import { Readable } from "stream";
1344
- function createMcpHandler(mcp) {
1480
+ function createMcpHandler(mcp, options) {
1345
1481
  const handler = async (ctx) => {
1346
- return handleMcpRequest(ctx.request, mcp);
1482
+ return handleMcpRequest(ctx.request, mcp, options);
1347
1483
  };
1348
1484
  return { POST: handler, GET: handler, DELETE: handler };
1349
1485
  }
1350
- function createMcpNodeHandler(mcp) {
1486
+ function createMcpNodeHandler(mcp, options) {
1351
1487
  return async (req, res) => {
1352
1488
  const url = new URL(req.url ?? "/", "http://localhost");
1353
1489
  const headers = new Headers();
@@ -1366,7 +1502,7 @@ function createMcpNodeHandler(mcp) {
1366
1502
  headers,
1367
1503
  ...body && { body, duplex: "half" }
1368
1504
  });
1369
- const response = await handleMcpRequest(request, mcp);
1505
+ const response = await handleMcpRequest(request, mcp, options);
1370
1506
  res.statusCode = response.status;
1371
1507
  response.headers.forEach((value, key) => {
1372
1508
  res.setHeader(key, value);
@@ -1382,9 +1518,15 @@ function createMcpNodeHandler(mcp) {
1382
1518
  else resolve();
1383
1519
  };
1384
1520
  nodeStream.on("error", (err) => settle(err));
1385
- res.on("error", (err) => settle(err));
1521
+ res.on("error", () => {
1522
+ nodeStream.destroy();
1523
+ settle();
1524
+ });
1386
1525
  res.on("finish", () => settle());
1387
- res.on("close", () => settle());
1526
+ res.on("close", () => {
1527
+ if (!res.writableEnded) nodeStream.destroy();
1528
+ settle();
1529
+ });
1388
1530
  nodeStream.pipe(res);
1389
1531
  });
1390
1532
  return;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/mcpServer.ts","../src/jsonRpc.ts","../src/session.ts","../src/streamableHttp.ts","../src/faapiAdapter.ts"],"sourcesContent":["/**\n * MCP Server 核心:tool/resource/prompt 注册 + JSON-RPC 方法分发\n *\n * 实现 MCP 协议的核心方法:\n * - initialize:握手,返回 serverInfo + capabilities(根据注册情况动态生成)\n * - notifications/initialized:通知,无响应\n * - tools/list、tools/call:tool 列表与调用\n * - resources/list、resources/read:资源列表与读取\n * - prompts/list、prompts/get:提示列表与获取\n * - ping:心跳\n *\n * Tool 输入参数用 zod schema 声明,通过 zod v4 内置 toJSONSchema 转为 JSON Schema。\n */\n\nimport { z, toJSONSchema } from 'zod';\nimport type { JsonRpcRequest, JsonRpcMessage, JsonRpcErrorResponse } from './jsonRpc';\nimport { createResultResponse, createErrorResponse, ErrorCode, isNotification } from './jsonRpc';\nimport { SessionManager, type McpSession, type LoggingLevel } from './session';\n\n// ─── 协议常量 ───────────────────────────────────────────\n\n/** MCP 协议版本(当前实现遵循 2025-06-18 规范) */\nexport const PROTOCOL_VERSION = '2025-06-18';\n\n/** 支持的协议版本列表 */\nexport const SUPPORTED_PROTOCOL_VERSIONS = ['2025-06-18', '2025-03-26'] as const;\n\n// ─── 类型定义 ───────────────────────────────────────────\n\nexport interface McpServerOptions {\n /** Server 名称 */\n name: string;\n /** Server 版本 */\n version: string;\n /** 可选显示名称 */\n title?: string;\n /** 可选给客户端的指引说明 */\n instructions?: string;\n /** 会话空闲超时(毫秒),默认 30 分钟;设为 0 表示永不过期 */\n sessionTtl?: number;\n /** GET SSE 流心跳间隔(毫秒),默认 30 秒 */\n sseHeartbeatMs?: number;\n /** list 方法默认每页项数,默认 100 */\n defaultPageSize?: number;\n /** tools 列表是否可变(声明 listChanged: true + removeTool 时推送通知)。默认 false */\n toolsListChanged?: boolean;\n /** resources 列表是否可变(声明 listChanged: true + removeResource 时推送通知)。默认 false */\n resourcesListChanged?: boolean;\n /** prompts 列表是否可变(声明 listChanged: true + removePrompt 时推送通知)。默认 false */\n promptsListChanged?: boolean;\n}\n\nexport interface McpToolResult {\n /** 内容数组(text/image/audio/resource_link/resource) */\n content: Array<Record<string, unknown>>;\n /** 是否为 tool 执行错误(区别于协议错误) */\n isError?: boolean;\n /** 结构化输出(配合 outputSchema 使用) */\n structuredContent?: unknown;\n}\n\n/** sendLogging 函数类型——推送日志到 session 的 SSE 订阅者 */\nexport type SendLoggingFn = (level: LoggingLevel, data: unknown, logger?: string) => void;\n\n/** sendProgress 函数类型——推送进度到 session 的 SSE 订阅者 */\nexport type SendProgressFn = (progress: number, total?: number) => void;\n\n/** 自定义方法的请求 extra(与 tool/resource/prompt handler extra 共享 send* 能力) */\nexport interface RequestExtra {\n /** 会话 ID */\n sessionId: string;\n /** 推送日志到客户端 SSE 流(无订阅者或被级别过滤时静默丢弃) */\n sendLogging: SendLoggingFn;\n /** 推送进度到客户端 SSE 流(无 progressToken 或无订阅者时静默丢弃) */\n sendProgress: SendProgressFn;\n}\n\n/** 自定义方法 handler 返回值——对象作为 result,JsonRpcErrorResponse 作为错误 */\nexport type MethodHandlerResult = Record<string, unknown> | JsonRpcErrorResponse;\n\n/** 自定义 JSON-RPC 方法 handler 类型 */\nexport type MethodHandler = (\n params: unknown,\n session: McpSession | undefined,\n extra: RequestExtra,\n) => Promise<MethodHandlerResult> | MethodHandlerResult;\n\nexport interface ToolCallExtra {\n /** 会话 ID */\n sessionId: string;\n /** 推送日志到客户端 SSE 流(无订阅者或被级别过滤时静默丢弃) */\n sendLogging: SendLoggingFn;\n /** 推送进度到客户端 SSE 流(无 progressToken 或无订阅者时静默丢弃) */\n sendProgress: SendProgressFn;\n}\n\nexport interface McpToolDefinition {\n /** 工具描述 */\n description?: string;\n /** 输入参数 schema(zod raw shape,如 { name: z.string() }) */\n input?: Record<string, z.ZodType>;\n /** 工具注解(如 readOnlyHint、destructiveHint 等) */\n annotations?: Record<string, unknown>;\n /** 处理函数 */\n handler: (\n args: Record<string, unknown>,\n extra: ToolCallExtra,\n ) => Promise<McpToolResult> | McpToolResult;\n}\n\n// ─── resource 类型 ─────────────────────────────────────\n\nexport interface McpResourceContent {\n /** 资源 URI */\n uri: string;\n /** MIME 类型 */\n mimeType?: string;\n /** 文本内容(与 blob 二选一) */\n text?: string;\n /** 二进制内容(Base64 编码,与 text 二选一) */\n blob?: string;\n}\n\nexport interface McpResourceReadResult {\n /** 资源内容数组(支持一个 URI 返回多段内容) */\n contents: McpResourceContent[];\n}\n\nexport interface ResourceReadExtra {\n /** 会话 ID */\n sessionId: string;\n /** 推送日志到客户端 SSE 流(无订阅者或被级别过滤时静默丢弃) */\n sendLogging: SendLoggingFn;\n /** 推送进度到客户端 SSE 流(无 progressToken 或无订阅者时静默丢弃) */\n sendProgress: SendProgressFn;\n}\n\nexport interface McpResourceDefinition {\n /** 资源名称(必填) */\n name: string;\n /** 资源描述 */\n description?: string;\n /** MIME 类型 */\n mimeType?: string;\n /** 读取 handler(必填) */\n read: (\n uri: string,\n extra: ResourceReadExtra,\n ) => Promise<McpResourceReadResult> | McpResourceReadResult;\n}\n\ninterface RegisteredResource {\n uri: string;\n definition: McpResourceDefinition;\n}\n\n// ─── resource template 类型 ─────────────────────────\n\nexport interface ResourceTemplateReadExtra {\n /** 会话 ID */\n sessionId: string;\n /** 推送日志到客户端 SSE 流(无订阅者或被级别过滤时静默丢弃) */\n sendLogging: SendLoggingFn;\n /** 推送进度到客户端 SSE 流(无 progressToken 或无订阅者时静默丢弃) */\n sendProgress: SendProgressFn;\n}\n\nexport interface McpResourceTemplateDefinition {\n /** 资源名称(必填) */\n name: string;\n /** 资源描述 */\n description?: string;\n /** MIME 类型 */\n mimeType?: string;\n /** 读取 handler(必填):接收实际 URI 和从 URI 模板提取的 params */\n read: (\n uri: string,\n params: Record<string, string>,\n extra: ResourceTemplateReadExtra,\n ) => Promise<McpResourceReadResult> | McpResourceReadResult;\n}\n\ninterface RegisteredResourceTemplate {\n uriTemplate: string;\n /** 编译后的正则表达式(用于匹配 URI) */\n regex: RegExp;\n /** 模板中的变量名列表(按出现顺序) */\n paramNames: string[];\n definition: McpResourceTemplateDefinition;\n}\n\n// ─── prompt 类型 ───────────────────────────────────────\n\nexport interface McpPromptArgument {\n /** 参数名称 */\n name: string;\n /** 参数描述 */\n description?: string;\n /** 是否必填 */\n required?: boolean;\n}\n\nexport interface McpPromptContent {\n /** 内容类型:text 或 image */\n type: 'text' | 'image';\n /** 文本内容(type=text 时) */\n text?: string;\n /** Base64 编码的图片数据(type=image 时) */\n data?: string;\n /** 图片 MIME 类型(type=image 时) */\n mimeType?: string;\n}\n\nexport interface McpPromptMessage {\n /** 角色 */\n role: 'user' | 'assistant';\n /** 内容 */\n content: McpPromptContent;\n}\n\nexport interface McpPromptGetResult {\n /** 提示消息列表 */\n messages: McpPromptMessage[];\n}\n\nexport interface PromptGetExtra {\n /** 会话 ID */\n sessionId: string;\n /** 推送日志到客户端 SSE 流(无订阅者或被级别过滤时静默丢弃) */\n sendLogging: SendLoggingFn;\n /** 推送进度到客户端 SSE 流(无 progressToken 或无订阅者时静默丢弃) */\n sendProgress: SendProgressFn;\n}\n\nexport interface McpPromptDefinition {\n /** 提示描述 */\n description?: string;\n /** 参数定义 */\n arguments?: McpPromptArgument[];\n /** 获取 handler(必填) */\n get: (\n args: Record<string, string>,\n extra: PromptGetExtra,\n ) => Promise<McpPromptGetResult> | McpPromptGetResult;\n}\n\ninterface RegisteredPrompt {\n name: string;\n definition: McpPromptDefinition;\n}\n\ninterface RegisteredTool {\n name: string;\n definition: McpToolDefinition;\n /** 包装后的 z.object schema(用于 safeParse) */\n inputSchema: z.ZodObject<Record<string, z.ZodType>> | undefined;\n /** JSON Schema 表示(用于 tools/list 响应) */\n jsonSchema: Record<string, unknown> | undefined;\n}\n\n// ─── completion 类型 ──────────────────────────────────\n\n/** completion 引用——指向某个 prompt 或 resource template */\nexport type CompletionRef =\n | { type: 'ref/prompt'; name: string }\n | { type: 'ref/resource'; uri: string };\n\n/** completion 调用上下文——传递给 handler */\nexport interface CompletionContext {\n /** 客户端已填写的其他参数(部分填充,key 为参数名,value 为已填值) */\n arguments: Record<string, string>;\n}\n\n/** completion 返回结果 */\nexport interface CompletionResult {\n /** 候选值数组 */\n values: string[];\n /** 总数(可选,用于客户端提示\"还有 N 项\") */\n total?: number;\n /** 是否还有更多(可选,提示客户端可分页或继续输入) */\n hasMore?: boolean;\n}\n\n/** completion handler 函数类型 */\nexport type CompletionHandler = (\n value: string,\n context: CompletionContext,\n) => Promise<CompletionResult> | CompletionResult;\n\ninterface RegisteredCompletion {\n /** 引用类型 'ref/prompt' | 'ref/resource' */\n refType: 'ref/prompt' | 'ref/resource';\n /** 引用标识(prompt name 或 resource template uri) */\n refId: string;\n /** 参数名 */\n argumentName: string;\n /** 补全 handler */\n handler: CompletionHandler;\n}\n\ninterface RegisteredMethod {\n name: string;\n handler: MethodHandler;\n}\n\n/** MCP 内置方法——禁止业务方注册同名方法,避免覆盖协议行为 */\nconst BUILTIN_METHODS = new Set<string>([\n 'initialize',\n 'ping',\n 'notifications/initialized',\n 'notifications/cancelled',\n 'tools/list',\n 'tools/call',\n 'resources/list',\n 'resources/read',\n 'resources/templates/list',\n 'resources/subscribe',\n 'resources/unsubscribe',\n 'prompts/list',\n 'prompts/get',\n 'logging/setLevel',\n 'completion/complete',\n]);\n\n// ─── 分页工具 ───────────────────────────────────────────\n\n/** 默认每页项数 */\nconst DEFAULT_PAGE_SIZE = 100;\n\n/** 将偏移量编码为不透明 cursor 字符串 */\nfunction encodeCursor(offset: number): string {\n return Buffer.from(String(offset)).toString('base64');\n}\n\n/** 解码 cursor,返回偏移量;无效时抛错 */\nfunction decodeCursor(cursor: string): number {\n const decoded = Buffer.from(cursor, 'base64').toString('utf-8');\n const offset = Number.parseInt(decoded, 10);\n if (!Number.isFinite(offset) || offset < 0 || decoded !== String(offset)) {\n throw new Error(`Invalid cursor: ${cursor}`);\n }\n return offset;\n}\n\n/** 对数组切片分页,返回当前页数据和可选的 nextCursor */\nfunction paginate<T>(\n items: T[],\n cursor: string | undefined,\n pageSize: number,\n): { items: T[]; nextCursor?: string } {\n const offset = cursor ? decodeCursor(cursor) : 0;\n if (offset > items.length) {\n throw new Error(`cursor out of range: offset ${offset} > total ${items.length}`);\n }\n const slice = items.slice(offset, offset + pageSize);\n const nextOffset = offset + pageSize;\n const nextCursor = nextOffset < items.length ? encodeCursor(nextOffset) : undefined;\n return { items: slice, nextCursor };\n}\n\n// ─── URI 模板编译/匹配 ─────────────────────────────────\n\n/** 变量名合法字符:[A-Za-z_][A-Za-z0-9_]* */\nconst VAR_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/** 占位符正则:匹配 {varName} */\nconst PLACEHOLDER_RE = /\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g;\n\n/**\n * 编译 URI 模板为正则 + 变量名列表\n *\n * - `file://docs/{path}` → regex `^file://docs/(.+)$`, paramNames `['path']`\n * - `git://repo/{owner}/{repo}` → regex `^git://repo/([^/]+)/([^/]+)$`, paramNames `['owner', 'repo']`\n *\n * 约束:\n * - 模板必须有至少一个 `{var}` 占位符\n * - 不支持 RFC 6570 操作符前缀(`{+var}`、`{?var}`、`{/var}` 等)\n * - 单变量贪婪匹配到 URI 结尾(允许包含 `/`)\n * - 多变量非贪婪匹配(不含 `/`),避免歧义\n *\n * @throws Error 模板格式无效\n */\nfunction compileUriTemplate(uriTemplate: string): { regex: RegExp; paramNames: string[] } {\n const paramNames: string[] = [];\n // 收集变量名并校验\n let match: RegExpExecArray | null;\n PLACEHOLDER_RE.lastIndex = 0;\n while ((match = PLACEHOLDER_RE.exec(uriTemplate)) !== null) {\n const name = match[1]!;\n if (!VAR_NAME_RE.test(name)) {\n throw new Error(`Invalid URI template variable name: ${name}`);\n }\n paramNames.push(name);\n }\n if (paramNames.length === 0) {\n throw new Error(`URI template must contain at least one {var} placeholder: ${uriTemplate}`);\n }\n\n // 检查重复变量名\n const seen = new Set<string>();\n for (const name of paramNames) {\n if (seen.has(name)) {\n throw new Error(`Duplicate URI template variable: ${name}`);\n }\n seen.add(name);\n }\n\n // 构建正则:转义字面字符,替换占位符\n // 单变量时贪婪(允许 /),多变量时非贪婪(不含 /)\n const isSingle = paramNames.length === 1;\n // 分段构建:按 {var} 分割,字面部分转义,占位符替换为捕获组\n const parts: string[] = [];\n let lastIdx = 0;\n PLACEHOLDER_RE.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = PLACEHOLDER_RE.exec(uriTemplate)) !== null) {\n // 转义字面部分\n const literal = uriTemplate.slice(lastIdx, m.index);\n parts.push(escapeRegex(literal));\n // 替换占位符为捕获组\n parts.push(isSingle ? '(.+)' : '([^/]+)');\n lastIdx = m.index + m[0]!.length;\n }\n // 尾部字面部分\n parts.push(escapeRegex(uriTemplate.slice(lastIdx)));\n const regex = new RegExp(`^${parts.join('')}$`);\n return { regex, paramNames };\n}\n\n/** 转义正则字面字符 */\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * 用编译后的模板匹配 URI,提取参数\n *\n * @returns 匹配成功返回 params 对象,失败返回 null\n */\nfunction matchUriTemplate(\n regex: RegExp,\n paramNames: string[],\n uri: string,\n): Record<string, string> | null {\n const m = regex.exec(uri);\n if (!m) return null;\n const params: Record<string, string> = {};\n for (let i = 0; i < paramNames.length; i++) {\n params[paramNames[i]!] = m[i + 1]!;\n }\n return params;\n}\n\n// ─── McpServer ──────────────────────────────────────────\n\nexport class McpServer {\n private tools = new Map<string, RegisteredTool>();\n private resources = new Map<string, RegisteredResource>();\n private resourceTemplates = new Map<string, RegisteredResourceTemplate>();\n private prompts = new Map<string, RegisteredPrompt>();\n private completions = new Map<string, RegisteredCompletion>();\n private methods = new Map<string, RegisteredMethod>();\n private sessions: SessionManager;\n private readonly pageSize: number;\n private readonly toolsListChanged: boolean;\n private readonly resourcesListChanged: boolean;\n private readonly promptsListChanged: boolean;\n\n constructor(private options: McpServerOptions) {\n this.sessions = new SessionManager(this.options.sessionTtl);\n this.pageSize = this.options.defaultPageSize ?? DEFAULT_PAGE_SIZE;\n this.toolsListChanged = this.options.toolsListChanged ?? false;\n this.resourcesListChanged = this.options.resourcesListChanged ?? false;\n this.promptsListChanged = this.options.promptsListChanged ?? false;\n }\n\n /** 注册 tool */\n tool(name: string, definition: McpToolDefinition): void {\n if (this.tools.has(name)) {\n throw new Error(`Tool \"${name}\" is already registered`);\n }\n let inputSchema: z.ZodObject<Record<string, z.ZodType>> | undefined;\n let jsonSchema: Record<string, unknown> | undefined;\n\n if (definition.input) {\n // 将 raw shape 包装为 z.object\n inputSchema = buildZodObject(definition.input);\n // 转为 JSON Schema(MCP 协议要求)\n jsonSchema = toJSONSchema(inputSchema, { target: 'draft-7' }) as Record<string, unknown>;\n // 移除 $schema 字段(MCP 规范不需要)\n delete jsonSchema.$schema;\n // 移除 additionalProperties: false(MCP 客户端兼容性)\n delete jsonSchema.additionalProperties;\n }\n\n this.tools.set(name, { name, definition, inputSchema, jsonSchema });\n }\n\n /** 注册 resource */\n resource(uri: string, definition: McpResourceDefinition): void {\n if (this.resources.has(uri)) {\n throw new Error(`Resource \"${uri}\" is already registered`);\n }\n this.resources.set(uri, { uri, definition });\n }\n\n /** 注册 resource template(RFC 6570 URI 模板) */\n resourceTemplate(uriTemplate: string, definition: McpResourceTemplateDefinition): void {\n if (this.resourceTemplates.has(uriTemplate)) {\n throw new Error(`Resource template \"${uriTemplate}\" is already registered`);\n }\n const { regex, paramNames } = compileUriTemplate(uriTemplate);\n this.resourceTemplates.set(uriTemplate, {\n uriTemplate,\n regex,\n paramNames,\n definition,\n });\n }\n\n /** 注册 prompt */\n prompt(name: string, definition: McpPromptDefinition): void {\n if (this.prompts.has(name)) {\n throw new Error(`Prompt \"${name}\" is already registered`);\n }\n this.prompts.set(name, { name, definition });\n }\n\n /**\n * 注册参数补全 handler\n *\n * - `ref`:`{ type: 'ref/prompt', name }` 或 `{ type: 'ref/resource', uri }`(对资源模板,uri 是模板字符串)\n * - `argumentName`:补全的参数名(对应 prompt arguments 中的 name,或 resource template URI 模板中的变量名)\n * - 同一 (ref, argumentName) 重复注册抛错\n */\n completion(ref: CompletionRef, argumentName: string, handler: CompletionHandler): void {\n const refType = ref.type;\n const refId = refType === 'ref/prompt' ? ref.name : ref.uri;\n const key = completionKey(refType, refId, argumentName);\n if (this.completions.has(key)) {\n throw new Error(\n `Completion for ${refType} \"${refId}\" argument \"${argumentName}\" is already registered`,\n );\n }\n this.completions.set(key, { refType, refId, argumentName, handler });\n }\n\n /**\n * 注册自定义 JSON-RPC 方法 handler(业务拓展)\n *\n * - 方法名建议使用 `appName/action` 格式,避免与 MCP 标准方法冲突\n * - 与 MCP 内置方法(initialize/ping/tools/* 等)冲突时抛错\n * - 重复注册同名方法抛错\n *\n * handler 返回值规则:\n * - 返回普通对象:作为 JSON-RPC result 字段\n * - 返回 JsonRpcErrorResponse(含 error 字段):作为错误响应\n */\n method(name: string, handler: MethodHandler): void {\n if (BUILTIN_METHODS.has(name)) {\n throw new Error(`Cannot register built-in method \"${name}\": it is reserved by MCP protocol`);\n }\n if (this.methods.has(name)) {\n throw new Error(`Method \"${name}\" is already registered`);\n }\n this.methods.set(name, { name, handler });\n }\n\n /** 获取已注册 tool 名称列表 */\n listTools(): string[] {\n return [...this.tools.keys()];\n }\n\n /** 获取已注册 resource URI 列表 */\n listResources(): string[] {\n return [...this.resources.keys()];\n }\n\n /** 获取已注册 prompt 名称列表 */\n listPrompts(): string[] {\n return [...this.prompts.keys()];\n }\n\n /** 获取已注册自定义方法名列表(业务拓展) */\n listMethods(): string[] {\n return [...this.methods.keys()];\n }\n\n // ─── remove 方法(运行时删除注册项) ───────────────────\n\n /**\n * 删除已注册 tool\n *\n * - `toolsListChanged: true` 时自动推送 `notifications/tools/list_changed`\n * - `toolsListChanged: false` 时静默删除(用于 dev 热替换,客户端不感知)\n */\n removeTool(name: string): boolean {\n const deleted = this.tools.delete(name);\n if (deleted && this.toolsListChanged) {\n this.notifyToolsListChanged();\n }\n return deleted;\n }\n\n /**\n * 删除已注册 resource\n *\n * - `resourcesListChanged: true` 时自动推送 `notifications/resources/list_changed`\n */\n removeResource(uri: string): boolean {\n const deleted = this.resources.delete(uri);\n if (deleted && this.resourcesListChanged) {\n this.notifyResourcesListChanged();\n }\n return deleted;\n }\n\n /**\n * 删除已注册 resource template\n *\n * - `resourcesListChanged: true` 时自动推送 `notifications/resources/list_changed`\n */\n removeResourceTemplate(uriTemplate: string): boolean {\n const deleted = this.resourceTemplates.delete(uriTemplate);\n if (deleted && this.resourcesListChanged) {\n this.notifyResourcesListChanged();\n }\n return deleted;\n }\n\n /**\n * 删除已注册 prompt\n *\n * - `promptsListChanged: true` 时自动推送 `notifications/prompts/list_changed`\n */\n removePrompt(name: string): boolean {\n const deleted = this.prompts.delete(name);\n if (deleted && this.promptsListChanged) {\n this.notifyPromptsListChanged();\n }\n return deleted;\n }\n\n /**\n * 删除已注册 completion handler\n *\n * - completion 无 list_changed 通知机制(客户端按需请求,无需感知列表变化)\n */\n removeCompletion(ref: CompletionRef, argumentName: string): boolean {\n const refType = ref.type;\n const refId = refType === 'ref/prompt' ? ref.name : ref.uri;\n const key = completionKey(refType, refId, argumentName);\n return this.completions.delete(key);\n }\n\n /** 删除已注册自定义方法 */\n removeMethod(name: string): boolean {\n return this.methods.delete(name);\n }\n\n // ─── list_changed 通知 ───────────────────────────────\n\n /**\n * 推送 `notifications/tools/list_changed` 到所有 session 的 SSE 订阅者\n *\n * 客户端收到后应重新调用 `tools/list` 拉取最新列表。\n * `removeTool` 在 `toolsListChanged: true` 时自动调用本方法,\n * 业务方也可手动调用(如批量删除后只推送一次)。\n */\n notifyToolsListChanged(): void {\n this.broadcastNotificationToAllSessions('notifications/tools/list_changed');\n }\n\n /** 推送 `notifications/resources/list_changed` 到所有 session 的 SSE 订阅者 */\n notifyResourcesListChanged(): void {\n this.broadcastNotificationToAllSessions('notifications/resources/list_changed');\n }\n\n /** 推送 `notifications/prompts/list_changed` 到所有 session 的 SSE 订阅者 */\n notifyPromptsListChanged(): void {\n this.broadcastNotificationToAllSessions('notifications/prompts/list_changed');\n }\n\n /**\n * 向所有 session 的所有 SSE 订阅者广播通知(内部工具方法)\n *\n * 用于 list_changed 这种\"全局广播\"语义——所有 session 都应感知列表变化。\n */\n private broadcastNotificationToAllSessions(method: string, params?: unknown): void {\n const notification: { jsonrpc: '2.0'; method: string; params?: unknown } = {\n jsonrpc: '2.0',\n method,\n };\n if (params !== undefined) {\n notification.params = params;\n }\n const sseData = `data: ${JSON.stringify(notification)}\\n\\n`;\n for (const sessionId of this.sessions.allSessionIds()) {\n this.sessions.broadcastToSession(sessionId, sseData);\n }\n }\n\n /** 获取会话管理器 */\n getSessionManager(): SessionManager {\n return this.sessions;\n }\n\n /** 获取 GET SSE 流心跳间隔(毫秒) */\n getSseHeartbeatMs(): number {\n return this.options.sseHeartbeatMs ?? 30_000;\n }\n\n /**\n * 处理 JSON-RPC 请求,返回响应消息\n *\n * 通知(无 id)不返回响应,返回 null。\n */\n async handleJsonRpc(\n message: JsonRpcMessage,\n session: McpSession | undefined,\n ): Promise<JsonRpcMessage | null> {\n // 通知:无响应\n if (isNotification(message)) {\n return this.handleNotification(message, session);\n }\n\n // 请求:必须有 id\n if ('id' in message && 'method' in message && !('result' in message) && !('error' in message)) {\n return this.handleRequest(message, session);\n }\n\n // 其他消息类型(response 等):忽略\n return null;\n }\n\n private handleNotification(\n message: { method: string; params?: unknown },\n session: McpSession | undefined,\n ): null {\n switch (message.method) {\n case 'notifications/initialized':\n if (session) session.initialized = true;\n break;\n case 'notifications/cancelled':\n // 请求取消——v1 不实现取消逻辑,仅接收\n break;\n }\n return null;\n }\n\n private async handleRequest(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): Promise<JsonRpcMessage> {\n try {\n switch (request.method) {\n case 'initialize':\n return this.handleInitialize(request, session);\n\n case 'ping':\n return createResultResponse(request.id, {});\n\n case 'tools/list':\n return this.handleToolsList(request);\n\n case 'tools/call':\n return await this.handleToolsCall(request, session);\n\n case 'resources/list':\n return this.handleResourcesList(request);\n\n case 'resources/read':\n return await this.handleResourcesRead(request, session);\n\n case 'prompts/list':\n return this.handlePromptsList(request);\n\n case 'prompts/get':\n return await this.handlePromptsGet(request, session);\n\n case 'logging/setLevel':\n return this.handleLoggingSetLevel(request, session);\n\n case 'resources/subscribe':\n return this.handleResourcesSubscribe(request, session);\n\n case 'resources/unsubscribe':\n return this.handleResourcesUnsubscribe(request, session);\n\n case 'resources/templates/list':\n return this.handleResourcesTemplatesList(request);\n\n case 'completion/complete':\n return await this.handleCompletionComplete(request);\n\n default:\n // 自定义方法分发(业务拓展)\n if (this.methods.has(request.method)) {\n return await this.handleCustomMethod(request, session);\n }\n return createErrorResponse(\n request.id,\n ErrorCode.MethodNotFound,\n `Method not found: ${request.method}`,\n );\n }\n } catch (err) {\n return createErrorResponse(\n request.id,\n ErrorCode.InternalError,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n\n // ─── initialize ──────────────────────────────────────\n\n private handleInitialize(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): JsonRpcMessage {\n const params = (request.params ?? {}) as {\n protocolVersion?: string;\n clientInfo?: { name: string; version: string };\n };\n\n // 协议版本协商\n const requestedVersion = params.protocolVersion ?? PROTOCOL_VERSION;\n const protocolVersion = (SUPPORTED_PROTOCOL_VERSIONS as readonly string[]).includes(\n requestedVersion,\n )\n ? requestedVersion\n : PROTOCOL_VERSION;\n\n // 如果没有 session(直接调用 handleJsonRpc,非 transport 场景),创建一个\n // transport 层已创建 session 时则复用\n if (!session) {\n session = this.sessions.create();\n }\n session.protocolVersion = protocolVersion;\n session.clientInfo = params.clientInfo;\n\n // capability 根据实际注册情况动态生成\n const capabilities: Record<string, unknown> = {\n tools: { listChanged: this.toolsListChanged },\n // logging capability 始终声明——服务端内置日志推送能力\n logging: {},\n };\n if (this.resources.size > 0 || this.resourceTemplates.size > 0) {\n // 注册了 resource 或 resource template 时声明 resources capability + subscribe 子能力\n capabilities.resources = { listChanged: this.resourcesListChanged, subscribe: true };\n }\n if (this.prompts.size > 0) {\n capabilities.prompts = { listChanged: this.promptsListChanged };\n }\n\n const result = {\n protocolVersion,\n capabilities,\n serverInfo: {\n name: this.options.name,\n version: this.options.version,\n ...(this.options.title && { title: this.options.title }),\n },\n ...(this.options.instructions && { instructions: this.options.instructions }),\n };\n\n return createResultResponse(request.id, result);\n }\n\n // ─── tools/list ──────────────────────────────────────\n\n private handleToolsList(request: JsonRpcRequest): JsonRpcMessage {\n const params = (request.params ?? {}) as { cursor?: string };\n const allTools = Array.from(this.tools.values()).map((tool) => ({\n name: tool.name,\n ...(tool.definition.description && { description: tool.definition.description }),\n inputSchema: tool.jsonSchema ?? { type: 'object', properties: {} },\n ...(tool.definition.annotations && { annotations: tool.definition.annotations }),\n }));\n\n let paged: { items: typeof allTools; nextCursor?: string };\n try {\n paged = paginate(allTools, params.cursor, this.pageSize);\n } catch (err) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n err instanceof Error ? err.message : 'Invalid cursor',\n );\n }\n\n return createResultResponse(request.id, {\n tools: paged.items,\n ...(paged.nextCursor && { nextCursor: paged.nextCursor }),\n });\n }\n\n // ─── tools/call ──────────────────────────────────────\n\n private async handleToolsCall(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): Promise<JsonRpcMessage> {\n const params = (request.params ?? {}) as {\n name?: string;\n arguments?: Record<string, unknown>;\n _meta?: { progressToken?: unknown };\n };\n\n if (!params.name) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Missing required parameter: name',\n );\n }\n\n const tool = this.tools.get(params.name);\n if (!tool) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n `Unknown tool: ${params.name}`,\n );\n }\n\n // 校验输入参数\n const args = params.arguments ?? {};\n if (tool.inputSchema) {\n const parsed = tool.inputSchema.safeParse(args);\n if (!parsed.success) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Invalid tool arguments',\n parsed.error.issues,\n );\n }\n // 使用解析后的值(含默认值、coerce 等)\n Object.assign(args, parsed.data);\n }\n\n // 从请求 _meta.progressToken 提取进度 token(任意 JSON 值)\n const progressToken = params._meta?.progressToken;\n\n // 调用 handler\n const extra: ToolCallExtra = {\n sessionId: session?.id ?? '',\n sendLogging: (level, data, logger) => {\n if (session) this.sendLogging(session.id, level, data, logger);\n },\n sendProgress: (progress, total) => {\n if (session) this.sendProgress(session.id, progressToken, progress, total);\n },\n };\n\n let result: McpToolResult;\n try {\n result = await tool.definition.handler(args as Record<string, unknown>, extra);\n } catch (err) {\n // tool 执行错误——返回 isError 而非协议错误\n result = {\n content: [\n {\n type: 'text',\n text: `Tool execution failed: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n\n return createResultResponse(request.id, result);\n }\n\n // ─── resources/list ─────────────────────────────────\n\n private handleResourcesList(request: JsonRpcRequest): JsonRpcMessage {\n const params = (request.params ?? {}) as { cursor?: string };\n const allResources = Array.from(this.resources.values()).map((r) => ({\n uri: r.uri,\n name: r.definition.name,\n ...(r.definition.description && { description: r.definition.description }),\n ...(r.definition.mimeType && { mimeType: r.definition.mimeType }),\n }));\n\n let paged: { items: typeof allResources; nextCursor?: string };\n try {\n paged = paginate(allResources, params.cursor, this.pageSize);\n } catch (err) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n err instanceof Error ? err.message : 'Invalid cursor',\n );\n }\n\n return createResultResponse(request.id, {\n resources: paged.items,\n ...(paged.nextCursor && { nextCursor: paged.nextCursor }),\n });\n }\n\n // ─── resources/read ─────────────────────────────────\n\n private async handleResourcesRead(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): Promise<JsonRpcMessage> {\n const params = (request.params ?? {}) as {\n uri?: string;\n _meta?: { progressToken?: unknown };\n };\n\n if (!params.uri) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Missing required parameter: uri',\n );\n }\n\n // 从请求 _meta.progressToken 提取进度 token(任意 JSON 值)\n const progressToken = params._meta?.progressToken;\n\n // sendProgress 闭包(精确匹配与模板匹配共用,捕获 progressToken)\n const sendProgressFn: SendProgressFn = (progress, total) => {\n if (session) this.sendProgress(session.id, progressToken, progress, total);\n };\n\n const extra: ResourceReadExtra = {\n sessionId: session?.id ?? '',\n sendLogging: (level, data, logger) => {\n if (session) this.sendLogging(session.id, level, data, logger);\n },\n sendProgress: sendProgressFn,\n };\n\n // 1. 先在 resources Map 中查找精确匹配\n const resource = this.resources.get(params.uri);\n if (resource) {\n const result = await resource.definition.read(params.uri, extra);\n return createResultResponse(request.id, result);\n }\n\n // 2. 找不到时,遍历 resourceTemplates 用 URI 模板匹配\n for (const tpl of this.resourceTemplates.values()) {\n const params2 = matchUriTemplate(tpl.regex, tpl.paramNames, params.uri);\n if (params2) {\n const tplExtra: ResourceTemplateReadExtra = {\n sessionId: session?.id ?? '',\n sendLogging: (level, data, logger) => {\n if (session) this.sendLogging(session.id, level, data, logger);\n },\n sendProgress: sendProgressFn,\n };\n const result = await tpl.definition.read(params.uri, params2, tplExtra);\n return createResultResponse(request.id, result);\n }\n }\n\n // 3. 都不匹配\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n `Unknown resource: ${params.uri}`,\n );\n }\n\n // ─── resources/templates/list ──────────────────────\n\n private handleResourcesTemplatesList(request: JsonRpcRequest): JsonRpcMessage {\n const params = (request.params ?? {}) as { cursor?: string };\n const allTemplates = Array.from(this.resourceTemplates.values()).map((t) => ({\n uriTemplate: t.uriTemplate,\n name: t.definition.name,\n ...(t.definition.description && { description: t.definition.description }),\n ...(t.definition.mimeType && { mimeType: t.definition.mimeType }),\n }));\n\n let paged: { items: typeof allTemplates; nextCursor?: string };\n try {\n paged = paginate(allTemplates, params.cursor, this.pageSize);\n } catch (err) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n err instanceof Error ? err.message : 'Invalid cursor',\n );\n }\n\n return createResultResponse(request.id, {\n resourceTemplates: paged.items,\n ...(paged.nextCursor && { nextCursor: paged.nextCursor }),\n });\n }\n\n // ─── prompts/list ───────────────────────────────────\n\n private handlePromptsList(request: JsonRpcRequest): JsonRpcMessage {\n const params = (request.params ?? {}) as { cursor?: string };\n const allPrompts = Array.from(this.prompts.values()).map((p) => ({\n name: p.name,\n ...(p.definition.description && { description: p.definition.description }),\n ...(p.definition.arguments && { arguments: p.definition.arguments }),\n }));\n\n let paged: { items: typeof allPrompts; nextCursor?: string };\n try {\n paged = paginate(allPrompts, params.cursor, this.pageSize);\n } catch (err) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n err instanceof Error ? err.message : 'Invalid cursor',\n );\n }\n\n return createResultResponse(request.id, {\n prompts: paged.items,\n ...(paged.nextCursor && { nextCursor: paged.nextCursor }),\n });\n }\n\n // ─── prompts/get ────────────────────────────────────\n\n private async handlePromptsGet(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): Promise<JsonRpcMessage> {\n const params = (request.params ?? {}) as {\n name?: string;\n arguments?: Record<string, string>;\n _meta?: { progressToken?: unknown };\n };\n\n if (!params.name) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Missing required parameter: name',\n );\n }\n\n const prompt = this.prompts.get(params.name);\n if (!prompt) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n `Unknown prompt: ${params.name}`,\n );\n }\n\n // 从请求 _meta.progressToken 提取进度 token(任意 JSON 值)\n const progressToken = params._meta?.progressToken;\n\n const extra: PromptGetExtra = {\n sessionId: session?.id ?? '',\n sendLogging: (level, data, logger) => {\n if (session) this.sendLogging(session.id, level, data, logger);\n },\n sendProgress: (progress, total) => {\n if (session) this.sendProgress(session.id, progressToken, progress, total);\n },\n };\n\n const args = params.arguments ?? {};\n const result = await prompt.definition.get(args, extra);\n return createResultResponse(request.id, result);\n }\n\n // ─── logging/setLevel ──────────────────────────────\n\n private handleLoggingSetLevel(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): JsonRpcMessage {\n const params = (request.params ?? {}) as { level?: string };\n if (!params.level) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Missing required parameter: level',\n );\n }\n const validLevels: readonly LoggingLevel[] = [\n 'debug',\n 'info',\n 'notice',\n 'warning',\n 'error',\n 'critical',\n 'alert',\n 'emergency',\n ];\n if (!validLevels.includes(params.level as LoggingLevel)) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n `Invalid logging level: ${params.level}`,\n );\n }\n if (session) {\n session.loggingLevel = params.level as LoggingLevel;\n }\n return createResultResponse(request.id, {});\n }\n\n // ─── resources/subscribe ───────────────────────────\n\n private handleResourcesSubscribe(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): JsonRpcMessage {\n const params = (request.params ?? {}) as { uri?: string };\n if (!params.uri) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Missing required parameter: uri',\n );\n }\n if (!session) {\n return createErrorResponse(request.id, ErrorCode.InvalidRequest, 'No session for subscribe');\n }\n this.sessions.subscribeResource(session.id, params.uri);\n return createResultResponse(request.id, {});\n }\n\n // ─── resources/unsubscribe ─────────────────────────\n\n private handleResourcesUnsubscribe(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): JsonRpcMessage {\n const params = (request.params ?? {}) as { uri?: string };\n if (!params.uri) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Missing required parameter: uri',\n );\n }\n if (!session) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidRequest,\n 'No session for unsubscribe',\n );\n }\n this.sessions.unsubscribeResource(session.id, params.uri);\n return createResultResponse(request.id, {});\n }\n\n // ─── completion/complete ───────────────────────────\n\n private async handleCompletionComplete(request: JsonRpcRequest): Promise<JsonRpcMessage> {\n const params = (request.params ?? {}) as {\n ref?: { type: string; name?: string; uri?: string };\n argument?: { name?: string; value?: string };\n arguments?: Record<string, string>;\n };\n\n if (!params.ref || !params.argument) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Missing required parameter: ref and argument',\n );\n }\n\n const ref = params.ref;\n let refType: 'ref/prompt' | 'ref/resource';\n let refId: string;\n if (ref.type === 'ref/prompt' && typeof ref.name === 'string') {\n refType = 'ref/prompt';\n refId = ref.name;\n } else if (ref.type === 'ref/resource' && typeof ref.uri === 'string') {\n refType = 'ref/resource';\n refId = ref.uri;\n } else {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n `Invalid ref: ${JSON.stringify(ref)}`,\n );\n }\n\n if (typeof params.argument.name !== 'string') {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Missing required parameter: argument.name',\n );\n }\n\n const key = completionKey(refType, refId, params.argument.name);\n const completion = this.completions.get(key);\n if (!completion) {\n return createErrorResponse(\n request.id,\n ErrorCode.MethodNotFound,\n `No completion handler for ${refType} \"${refId}\" argument \"${params.argument.name}\"`,\n );\n }\n\n const value = params.argument.value ?? '';\n const context: CompletionContext = {\n arguments: params.arguments ?? {},\n };\n\n let result: CompletionResult;\n try {\n result = await completion.handler(value, context);\n } catch (err) {\n return createErrorResponse(\n request.id,\n ErrorCode.InternalError,\n err instanceof Error ? err.message : String(err),\n );\n }\n\n return createResultResponse(request.id, { completion: result });\n }\n\n // ─── 自定义方法分发(业务拓展) ───────────────────────\n\n private async handleCustomMethod(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): Promise<JsonRpcMessage> {\n const registered = this.methods.get(request.method);\n if (!registered) {\n // 双重检查(并发场景下可能在 default 分支后已被 remove)\n return createErrorResponse(\n request.id,\n ErrorCode.MethodNotFound,\n `Method not found: ${request.method}`,\n );\n }\n\n const extra: RequestExtra = {\n sessionId: session?.id ?? '',\n sendLogging: (level, data, logger) => {\n if (session) this.sendLogging(session.id, level, data, logger);\n },\n sendProgress: (progress, total) => {\n // 自定义方法无 progressToken 来源,始终传 undefined(业务方可直接用 server.sendProgress)\n if (session) this.sendProgress(session.id, undefined, progress, total);\n },\n };\n\n let result: MethodHandlerResult;\n try {\n result = await registered.handler(request.params, session, extra);\n } catch (err) {\n return createErrorResponse(\n request.id,\n ErrorCode.InternalError,\n err instanceof Error ? err.message : String(err),\n );\n }\n\n // 返回 JsonRpcErrorResponse 表示业务错误\n if (\n result !== null &&\n typeof result === 'object' &&\n 'error' in result &&\n result.error !== null &&\n typeof result.error === 'object'\n ) {\n // 已是错误响应格式——补全 id 后返回\n const errResp = result as unknown as JsonRpcErrorResponse;\n return { ...errResp, jsonrpc: '2.0', id: request.id };\n }\n\n // 普通对象——作为 result 返回\n return createResultResponse(request.id, result);\n }\n\n // ─── sendLogging(应用级 + handler extra 共用) ─────\n\n /**\n * 推送 notifications/message 到 session 的所有 SSE 订阅者\n *\n * - 无 session 或无订阅者:静默丢弃\n * - level 低于 session.loggingLevel:静默丢弃\n * - SSE 行格式:`data: ${JSON.stringify(notification)}\\n\\n`\n */\n sendLogging(sessionId: string, level: LoggingLevel, data: unknown, logger?: string): void {\n if (!this.sessions.shouldLog(sessionId, level)) return;\n const notification: { jsonrpc: '2.0'; method: string; params: Record<string, unknown> } = {\n jsonrpc: '2.0',\n method: 'notifications/message',\n params: { level, data },\n };\n if (logger !== undefined) {\n notification.params.logger = logger;\n }\n const sseData = `data: ${JSON.stringify(notification)}\\n\\n`;\n this.sessions.broadcastToSession(sessionId, sseData);\n }\n\n /**\n * 推送 notifications/resources/updated 到所有订阅了该 URI 的 session\n *\n * - 找出所有 subscribedResources 包含该 URI 的 session\n * - 对每个 session 的所有 SSE 订阅者推送通知\n * - 无订阅者的 session 静默跳过\n */\n sendResourceUpdated(uri: string): void {\n const sessionIds = this.sessions.findSubscribersOfUri(uri);\n if (sessionIds.length === 0) return;\n const notification = {\n jsonrpc: '2.0' as const,\n method: 'notifications/resources/updated',\n params: { uri },\n };\n const sseData = `data: ${JSON.stringify(notification)}\\n\\n`;\n for (const sessionId of sessionIds) {\n this.sessions.broadcastToSession(sessionId, sseData);\n }\n }\n\n /**\n * 推送 notifications/progress 到 session 的所有 SSE 订阅者\n *\n * - 无 session 或无订阅者:静默丢弃\n * - progressToken 为 undefined/null:静默丢弃(无法关联进度与请求)\n *\n * @param sessionId 会话 ID\n * @param progressToken 客户端在请求 _meta.progressToken 中传入的 token(任意 JSON 值)\n * @param progress 当前进度(数值)\n * @param total 总数(可选)\n */\n sendProgress(sessionId: string, progressToken: unknown, progress: number, total?: number): void {\n if (progressToken === undefined || progressToken === null) return;\n const params: Record<string, unknown> = { progressToken, progress };\n if (total !== undefined) {\n params.total = total;\n }\n const notification = {\n jsonrpc: '2.0' as const,\n method: 'notifications/progress',\n params,\n };\n const sseData = `data: ${JSON.stringify(notification)}\\n\\n`;\n this.sessions.broadcastToSession(sessionId, sseData);\n }\n\n /**\n * 通用通知推送(业务拓展)——向指定 session 的所有 SSE 订阅者推送任意通知\n *\n * - 无 session 或无订阅者:静默丢弃\n * - 不校验 method 是否符合 MCP 规范——业务方自行负责\n * - SSE 行格式:`data: ${JSON.stringify(notification)}\\n\\n`\n *\n * @param sessionId 目标 session ID\n * @param method 通知方法名(如 `notifications/myapp/sync`)\n * @param params 通知参数(可选)\n */\n sendNotification(sessionId: string, method: string, params?: unknown): void {\n const notification: { jsonrpc: '2.0'; method: string; params?: unknown } = {\n jsonrpc: '2.0',\n method,\n };\n if (params !== undefined) {\n notification.params = params;\n }\n const sseData = `data: ${JSON.stringify(notification)}\\n\\n`;\n this.sessions.broadcastToSession(sessionId, sseData);\n }\n}\n\n// ─── 工具函数 ───────────────────────────────────────────\n\n/**\n * 将 raw shape({ key: z.ZodType })包装为 z.object\n */\nfunction buildZodObject(shape: Record<string, z.ZodType>): z.ZodObject<Record<string, z.ZodType>> {\n return z.object(shape);\n}\n\n/**\n * 生成 completion Map 的键\n *\n * 形如 `ref/prompt:greet:userName` 或 `ref/resource:file://docs/{path}:path`\n */\nfunction completionKey(\n refType: 'ref/prompt' | 'ref/resource',\n refId: string,\n argumentName: string,\n): string {\n return `${refType}:${refId}:${argumentName}`;\n}\n\n// ─── 工厂函数 ───────────────────────────────────────────\n\nexport function createMcpServer(options: McpServerOptions): McpServer {\n return new McpServer(options);\n}\n","/**\n * JSON-RPC 2.0 协议消息类型、解析和响应构建\n *\n * MCP 协议基于 JSON-RPC 2.0,所有通信都是 JSON-RPC 消息。\n * 本模块提供类型定义、消息判定函数和响应构建工具。\n */\n\n// ─── 消息类型 ───────────────────────────────────────────\n\nexport interface JsonRpcRequest {\n jsonrpc: '2.0';\n id: string | number;\n method: string;\n params?: unknown;\n}\n\nexport interface JsonRpcNotification {\n jsonrpc: '2.0';\n method: string;\n params?: unknown;\n}\n\nexport interface JsonRpcResultResponse {\n jsonrpc: '2.0';\n id: string | number;\n result: unknown;\n}\n\nexport interface JsonRpcErrorResponse {\n jsonrpc: '2.0';\n id: string | number | null;\n error: JsonRpcError;\n}\n\nexport interface JsonRpcError {\n code: number;\n message: string;\n data?: unknown;\n}\n\nexport type JsonRpcMessage =\n | JsonRpcRequest\n | JsonRpcNotification\n | JsonRpcResultResponse\n | JsonRpcErrorResponse;\n\n// ─── 错误码 ─────────────────────────────────────────────\n\nexport const ErrorCode = {\n // JSON-RPC 标准错误码\n ParseError: -32700,\n InvalidRequest: -32600,\n MethodNotFound: -32601,\n InvalidParams: -32602,\n InternalError: -32603,\n // MCP 扩展错误码\n ConnectionClosed: -32000,\n RequestTimeout: -32001,\n} as const;\n\n// ─── 消息判定 ───────────────────────────────────────────\n\nexport function isRequest(msg: JsonRpcMessage): msg is JsonRpcRequest {\n return 'method' in msg && 'id' in msg && !('result' in msg) && !('error' in msg);\n}\n\nexport function isNotification(msg: JsonRpcMessage): msg is JsonRpcNotification {\n return 'method' in msg && !('id' in msg);\n}\n\nexport function isResultResponse(msg: JsonRpcMessage): msg is JsonRpcResultResponse {\n return 'result' in msg && 'id' in msg;\n}\n\nexport function isErrorResponse(msg: JsonRpcMessage): msg is JsonRpcErrorResponse {\n return 'error' in msg && 'id' in msg;\n}\n\n// ─── 响应构建 ───────────────────────────────────────────\n\nexport function createResultResponse(id: string | number, result: unknown): JsonRpcResultResponse {\n return { jsonrpc: '2.0', id, result };\n}\n\nexport function createErrorResponse(\n id: string | number | null,\n code: number,\n message: string,\n data?: unknown,\n): JsonRpcErrorResponse {\n const error: JsonRpcError = { code, message };\n if (data !== undefined) error.data = data;\n return { jsonrpc: '2.0', id, error };\n}\n\n// ─── 消息解析 ───────────────────────────────────────────\n\n/**\n * 解析 JSON-RPC 消息(单条或批量)\n *\n * @throws ParseError(返回 Error 对象,不抛异常)\n * @returns 消息数组(单条也包装为数组)\n */\nexport function parseJsonRpcMessage(data: unknown): JsonRpcMessage[] {\n if (Array.isArray(data)) {\n if (data.length === 0) {\n throw new JsonRpcParseError('Invalid Request: empty batch');\n }\n return data.map((item) => parseSingleMessage(item));\n }\n return [parseSingleMessage(data)];\n}\n\nclass JsonRpcParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'JsonRpcParseError';\n }\n}\n\nexport { JsonRpcParseError };\n\nfunction parseSingleMessage(data: unknown): JsonRpcMessage {\n if (typeof data !== 'object' || data === null) {\n throw new JsonRpcParseError('Invalid Request: not an object');\n }\n const obj = data as Record<string, unknown>;\n if (obj.jsonrpc !== '2.0') {\n throw new JsonRpcParseError('Invalid Request: jsonrpc must be \"2.0\"');\n }\n // Request: has method + id\n if (\n typeof obj.method === 'string' &&\n (typeof obj.id === 'string' || typeof obj.id === 'number')\n ) {\n return {\n jsonrpc: '2.0',\n id: obj.id,\n method: obj.method,\n ...(obj.params !== undefined && { params: obj.params }),\n } as JsonRpcRequest;\n }\n // Notification: has method, no id\n if (typeof obj.method === 'string' && obj.id === undefined) {\n return {\n jsonrpc: '2.0',\n method: obj.method,\n ...(obj.params !== undefined && { params: obj.params }),\n } as JsonRpcNotification;\n }\n // Response: has result or error + id\n if ('result' in obj && (typeof obj.id === 'string' || typeof obj.id === 'number')) {\n return {\n jsonrpc: '2.0',\n id: obj.id,\n result: obj.result,\n } as JsonRpcResultResponse;\n }\n if ('error' in obj && obj.error !== null && typeof obj.error === 'object') {\n return {\n jsonrpc: '2.0',\n id: (obj.id as string | number | null) ?? null,\n error: obj.error as JsonRpcError,\n } as JsonRpcErrorResponse;\n }\n throw new JsonRpcParseError('Invalid Request: unknown message shape');\n}\n","/**\n * MCP 会话管理\n *\n * Streamable HTTP transport 通过 Mcp-Session-Id header 维持会话。\n * 会话在 initialize 请求时创建,在 DELETE 请求时销毁。\n *\n * 内置 TTL 机制:超过空闲时间的会话自动过期,防止内存泄漏。\n * 惰性清理:在 get/create 时检查过期会话,无需定时器。\n *\n * 每个 session 维护 SSE subscriber 集合,用于服务端主动推送\n * (logging/message, resources/updated, progress 等通知)。\n */\n\nimport { randomUUID } from 'node:crypto';\n\n/** 日志级别(syslog 严重度,从低到高) */\nexport type LoggingLevel =\n | 'debug'\n | 'info'\n | 'notice'\n | 'warning'\n | 'error'\n | 'critical'\n | 'alert'\n | 'emergency';\n\n/** 日志级别数值(越大越严重) */\nconst LOGGING_LEVEL_ORDER: Record<LoggingLevel, number> = {\n debug: 10,\n info: 20,\n notice: 30,\n warning: 40,\n error: 50,\n critical: 60,\n alert: 70,\n emergency: 80,\n};\n\n/** SSE 流订阅者(controller + 关联的 session id) */\nexport interface SseSubscriber {\n /** SSE 流控制器 */\n controller: ReadableStreamDefaultController<Uint8Array>;\n /** 关联的 session id */\n sessionId: string;\n}\n\nexport interface McpSession {\n /** 会话 ID(cryptographically secure UUID) */\n id: string;\n /** 是否已完成 initialize 握手 */\n initialized: boolean;\n /** 协商的协议版本 */\n protocolVersion: string;\n /** 客户端信息(来自 initialize 请求) */\n clientInfo?: { name: string; version: string };\n /** 创建时间戳 */\n createdAt: number;\n /** 最后活动时间戳(每次 get 时更新) */\n lastActivity: number;\n /** 当前日志级别(低于此级别的日志不推送),默认 info */\n loggingLevel: LoggingLevel;\n /** SSE 流订阅者集合(GET 流注册,用于服务端主动推送) */\n subscribers: Set<SseSubscriber>;\n /** 已订阅的资源 URI 集合(resources/subscribe 注册) */\n subscribedResources: Set<string>;\n}\n\n/** 默认会话空闲超时:30 分钟 */\nconst DEFAULT_TTL = 30 * 60 * 1000;\n\n/**\n * 内存会话管理器\n *\n * 生产环境如需多实例共享会话,可替换为 Redis 等外部存储实现。\n *\n * @param ttl 会话空闲超时(毫秒),超过此时间未活动的会话自动过期。默认 30 分钟。设为 0 表示永不过期。\n */\nexport class SessionManager {\n private sessions = new Map<string, McpSession>();\n private readonly ttl: number;\n\n constructor(ttl: number = DEFAULT_TTL) {\n this.ttl = ttl;\n }\n\n /** 是否启用 TTL 过期检查(ttl > 0) */\n private get ttlEnabled(): boolean {\n return this.ttl > 0;\n }\n\n /** 创建新会话,返回 session 对象 */\n create(): McpSession {\n // 惰性清理:创建新会话时顺便清理过期会话\n if (this.ttlEnabled) this.cleanupExpired();\n\n const now = Date.now();\n const session: McpSession = {\n id: randomUUID(),\n initialized: false,\n protocolVersion: '',\n createdAt: now,\n lastActivity: now,\n loggingLevel: 'info',\n subscribers: new Set(),\n subscribedResources: new Set(),\n };\n this.sessions.set(session.id, session);\n return session;\n }\n\n /** 按 ID 获取会话(更新最后活动时间,过期返回 undefined) */\n get(id: string): McpSession | undefined {\n const session = this.sessions.get(id);\n if (!session) return undefined;\n\n // 检查是否过期(ttl=0 时跳过)\n if (this.ttlEnabled && Date.now() - session.lastActivity > this.ttl) {\n this.closeSubscribers(session);\n this.sessions.delete(id);\n return undefined;\n }\n\n // 更新最后活动时间\n session.lastActivity = Date.now();\n return session;\n }\n\n /** 是否存在(不更新活动时间,过期会话返回 false) */\n has(id: string): boolean {\n const session = this.sessions.get(id);\n if (!session) return false;\n if (this.ttlEnabled && Date.now() - session.lastActivity > this.ttl) {\n this.closeSubscribers(session);\n this.sessions.delete(id);\n return false;\n }\n return true;\n }\n\n /** 销毁会话(关闭所有 SSE 订阅者) */\n delete(id: string): boolean {\n const session = this.sessions.get(id);\n if (session) {\n this.closeSubscribers(session);\n }\n return this.sessions.delete(id);\n }\n\n /** 当前会话数(含可能尚未清理的过期会话) */\n get size(): number {\n return this.sessions.size;\n }\n\n /** 获取所有 session ID 列表(用于全局广播通知) */\n allSessionIds(): string[] {\n return [...this.sessions.keys()];\n }\n\n /** 清空所有会话 */\n clear(): void {\n for (const session of this.sessions.values()) {\n this.closeSubscribers(session);\n }\n this.sessions.clear();\n }\n\n /** 注册 SSE 订阅者到 session */\n addSubscriber(\n sessionId: string,\n controller: ReadableStreamDefaultController<Uint8Array>,\n ): SseSubscriber | undefined {\n const session = this.get(sessionId);\n if (!session) return undefined;\n const subscriber: SseSubscriber = { controller, sessionId };\n session.subscribers.add(subscriber);\n return subscriber;\n }\n\n /** 注销 SSE 订阅者 */\n removeSubscriber(subscriber: SseSubscriber): void {\n const session = this.sessions.get(subscriber.sessionId);\n if (session) {\n session.subscribers.delete(subscriber);\n }\n }\n\n /** 向 session 的所有订阅者推送 SSE 数据 */\n broadcastToSession(sessionId: string, data: string): void {\n const session = this.sessions.get(sessionId);\n if (!session) return;\n const encoder = new TextEncoder();\n for (const sub of session.subscribers) {\n try {\n sub.controller.enqueue(encoder.encode(data));\n } catch {\n // controller 已关闭,移除订阅者\n session.subscribers.delete(sub);\n }\n }\n }\n\n /** 判断指定级别的日志是否应该推送(>= session.loggingLevel) */\n shouldLog(sessionId: string, level: LoggingLevel): boolean {\n const session = this.sessions.get(sessionId);\n if (!session) return false;\n return LOGGING_LEVEL_ORDER[level] >= LOGGING_LEVEL_ORDER[session.loggingLevel];\n }\n\n /** 添加资源订阅(将 uri 加入 session.subscribedResources) */\n subscribeResource(sessionId: string, uri: string): boolean {\n const session = this.sessions.get(sessionId);\n if (!session) return false;\n session.subscribedResources.add(uri);\n return true;\n }\n\n /** 取消资源订阅(从 session.subscribedResources 移除 uri) */\n unsubscribeResource(sessionId: string, uri: string): boolean {\n const session = this.sessions.get(sessionId);\n if (!session) return false;\n session.subscribedResources.delete(uri);\n return true;\n }\n\n /** 找出所有订阅了指定 URI 的 session id 列表 */\n findSubscribersOfUri(uri: string): string[] {\n const result: string[] = [];\n for (const session of this.sessions.values()) {\n if (session.subscribedResources.has(uri)) {\n result.push(session.id);\n }\n }\n return result;\n }\n\n /** 关闭 session 的所有订阅者(用于 session 销毁/过期) */\n private closeSubscribers(session: McpSession): void {\n for (const sub of session.subscribers) {\n try {\n sub.controller.close();\n } catch {\n // controller 已关闭,忽略\n }\n }\n session.subscribers.clear();\n }\n\n /** 清理所有过期会话 */\n private cleanupExpired(): void {\n if (!this.ttlEnabled) return;\n const now = Date.now();\n for (const [id, session] of this.sessions) {\n if (now - session.lastActivity > this.ttl) {\n this.closeSubscribers(session);\n this.sessions.delete(id);\n }\n }\n }\n}\n","/**\n * Streamable HTTP transport:Web Request → JSON-RPC → Response\n *\n * MCP Streamable HTTP transport 规范:\n * - POST:发送 JSON-RPC 消息,返回 JSON 响应或 SSE 流\n * - GET:打开 SSE 流接收服务端推送\n * - DELETE:终止会话\n *\n * 会话管理通过 Mcp-Session-Id header。\n * 实现:POST 返回 JSON,GET 返回 SSE 流(仅心跳,不推送业务消息),DELETE 销毁会话。\n */\n\nimport type { McpServer } from './mcpServer';\nimport {\n type JsonRpcMessage,\n isRequest,\n isNotification,\n createErrorResponse,\n ErrorCode,\n parseJsonRpcMessage,\n JsonRpcParseError,\n} from './jsonRpc';\n\n/** 默认 SSE 心跳间隔 */\nconst DEFAULT_SSE_HEARTBEAT_MS = 30_000;\n\n/**\n * 处理 MCP HTTP 请求\n *\n * 接收 Web API Request,返回 Web API Response。\n * 可直接在 faapi handler 中使用:\n * ```ts\n * export function POST(ctx) {\n * return mcp.handleWebRequest(ctx.request);\n * }\n * ```\n */\nexport async function handleMcpRequest(request: Request, server: McpServer): Promise<Response> {\n switch (request.method) {\n case 'POST':\n return handlePost(request, server);\n case 'GET':\n return handleGet(server, request);\n case 'DELETE':\n return handleDelete(request, server);\n default:\n return new Response(null, { status: 405 });\n }\n}\n\n/**\n * 处理 GET 请求:打开 SSE 流,定期推送心跳维持连接\n *\n * 若携带 Mcp-Session-Id 头,会在 stream start 时注册 SSE 订阅者,\n * 服务端 sendLogging 等推送方法可通过 broadcastToSession 推送到客户端。\n *\n * 客户端断开时 stream cancel 触发,清理定时器 + 注销订阅者避免泄漏。\n */\nfunction handleGet(server: McpServer, request: Request): Response {\n const heartbeatMs = server.getSseHeartbeatMs() ?? DEFAULT_SSE_HEARTBEAT_MS;\n const encoder = new TextEncoder();\n let interval: ReturnType<typeof setInterval> | undefined;\n let subscriber:\n | { controller: ReadableStreamDefaultController<Uint8Array>; sessionId: string }\n | undefined;\n\n const sessionId = request.headers.get('mcp-session-id') ?? undefined;\n\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n // 初始连接确认\n controller.enqueue(encoder.encode(': connected\\n\\n'));\n\n // 若有 session id,注册订阅者(用于服务端推送)\n if (sessionId) {\n subscriber = server.getSessionManager().addSubscriber(sessionId, controller);\n }\n\n // 心跳定时器\n interval = setInterval(() => {\n try {\n controller.enqueue(encoder.encode(`: keepalive ${Date.now()}\\n\\n`));\n } catch {\n // controller 已关闭,清理定时器\n if (interval) clearInterval(interval);\n }\n }, heartbeatMs);\n },\n cancel() {\n // 客户端断开,清理定时器 + 注销订阅者避免泄漏\n if (interval) clearInterval(interval);\n if (subscriber) {\n server.getSessionManager().removeSubscriber(subscriber);\n }\n },\n });\n\n return new Response(stream, {\n status: 200,\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n}\n\nasync function handlePost(request: Request, server: McpServer): Promise<Response> {\n // 解析请求体\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return jsonResponse(\n 400,\n createErrorResponse(null, ErrorCode.ParseError, 'Parse error: Invalid JSON'),\n );\n }\n\n // 解析 JSON-RPC 消息\n let messages: JsonRpcMessage[];\n try {\n messages = parseJsonRpcMessage(body);\n } catch (err) {\n const message = err instanceof JsonRpcParseError ? err.message : 'Invalid JSON-RPC message';\n return jsonResponse(400, createErrorResponse(null, ErrorCode.ParseError, message));\n }\n\n // 分离请求和通知\n const requests = messages.filter(isRequest);\n const notifications = messages.filter(isNotification);\n\n // initialize 请求豁免 Accept 头校验(首次握手)\n const hasInitialize = requests.some((r) => r.method === 'initialize');\n if (!hasInitialize) {\n // MCP 2025-06-18 规范:Accept 头必须同时包含 application/json 和 text/event-stream\n const acceptHeader = request.headers.get('accept') ?? '';\n const accepts = acceptHeader\n .split(',')\n .map((s) => s.trim().toLowerCase())\n .filter((s) => s.length > 0);\n const hasJson = accepts.some(\n (s) => s === 'application/json' || s.startsWith('application/json;'),\n );\n const hasSse = accepts.some(\n (s) => s === 'text/event-stream' || s.startsWith('text/event-stream;'),\n );\n if (!hasJson || !hasSse) {\n return jsonResponse(\n 400,\n createErrorResponse(\n null,\n ErrorCode.InvalidRequest,\n 'Accept header must include both application/json and text/event-stream',\n ),\n );\n }\n }\n\n // 查找 session(非 initialize 请求需要 session)\n const sessionId = request.headers.get('mcp-session-id') ?? undefined;\n const sessionManager = server.getSessionManager();\n let session = sessionId ? sessionManager.get(sessionId) : undefined;\n\n if (hasInitialize) {\n if (session) {\n // 已有 session 却再次 initialize\n return jsonResponse(\n 400,\n createErrorResponse(null, ErrorCode.InvalidRequest, 'Server already initialized'),\n );\n }\n // 先创建 session,handleJsonRpc 中会填充 protocolVersion 等信息\n session = sessionManager.create();\n } else if (requests.length > 0 && !session && sessionId) {\n // 有 session ID 但找不到\n return jsonResponse(\n 404,\n createErrorResponse(null, ErrorCode.RequestTimeout, 'Session not found'),\n );\n }\n\n // 处理通知(无响应)\n for (const notification of notifications) {\n await server.handleJsonRpc(notification, session);\n }\n\n // 处理请求\n const responses: JsonRpcMessage[] = [];\n for (const req of requests) {\n const response = await server.handleJsonRpc(req, session);\n if (response !== null) {\n responses.push(response);\n }\n }\n\n // 构建响应\n if (requests.length === 0) {\n // 全是通知 → 202 Accepted\n return new Response(null, { status: 202 });\n }\n\n const responseHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n // initialize 响应需带 Mcp-Session-Id header\n if (hasInitialize && session) {\n responseHeaders['Mcp-Session-Id'] = session.id;\n }\n\n const responseBody = responses.length === 1 ? responses[0] : responses;\n return new Response(JSON.stringify(responseBody), {\n status: 200,\n headers: responseHeaders,\n });\n}\n\nfunction handleDelete(request: Request, server: McpServer): Promise<Response> {\n const sessionId = request.headers.get('mcp-session-id');\n if (!sessionId) {\n return Promise.resolve(\n jsonResponse(\n 400,\n createErrorResponse(null, ErrorCode.InvalidRequest, 'Missing Mcp-Session-Id header'),\n ),\n );\n }\n\n const deleted = server.getSessionManager().delete(sessionId);\n if (!deleted) {\n return Promise.resolve(\n jsonResponse(404, createErrorResponse(null, ErrorCode.RequestTimeout, 'Session not found')),\n );\n }\n\n return Promise.resolve(new Response(null, { status: 200 }));\n}\n\nfunction jsonResponse(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n}\n","/**\n * faapi 适配器:把 MCP Server 挂载到 faapi 路由\n *\n * 两种集成方式:\n *\n * 1. handler 风格(推荐)——在 handler.ts 中使用:\n * ```ts\n * // api/mcp/handler.ts\n * import { createMcpServer, createMcpHandler } from '@faapi/mcp';\n * const mcp = createMcpServer({ name: 'my-app', version: '1.0.0' });\n * mcp.tool('hello', { ... });\n * export const { POST, GET, DELETE } = createMcpHandler(mcp);\n * ```\n *\n * 2. 插件风格——在 faapi.config.ts 中声明:\n * ```ts\n * plugins: [['@faapi/mcp', { path: '/mcp' }]]\n * ```\n * 需配合 mcp.tool() 注册(通常在 lifecycle.onReady 中)。\n *\n * 函数即接口:MCP endpoint 就是 faapi 的一个路由。\n */\n\nimport { Readable } from 'node:stream';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { McpServer } from './mcpServer';\nimport { handleMcpRequest } from './streamableHttp';\n\n/**\n * 创建 faapi handler 函数\n *\n * 返回 { POST, GET, DELETE },可直接在 handler.ts 中导出。\n * faapi 按参数名注入 ctx,函数内通过 ctx.request 获取 Web Request。\n */\nexport function createMcpHandler(mcp: McpServer): {\n POST: (ctx: { request: Request }) => Promise<Response>;\n GET: (ctx: { request: Request }) => Promise<Response>;\n DELETE: (ctx: { request: Request }) => Promise<Response>;\n} {\n const handler = async (ctx: { request: Request }): Promise<Response> => {\n return handleMcpRequest(ctx.request, mcp);\n };\n return { POST: handler, GET: handler, DELETE: handler };\n}\n\n/**\n * 创建 Node.js 请求处理函数(供 wrapHandler 使用)\n *\n * 用于 faapi 插件场景:插件通过 wrapHandler 拦截指定路径,\n * 将 Node.js IncomingMessage/ServerResponse 转为 Web Request 处理。\n *\n * 使用 Node.js 原生 `Readable.toWeb(req)` 转换 body,正确处理 chunked transfer\n * (多 chunk 累积)、backpressure 和 stream error。\n */\nexport function createMcpNodeHandler(\n mcp: McpServer,\n): (req: IncomingMessage, res: ServerResponse) => Promise<void> {\n return async (req, res) => {\n const url = new URL(req.url ?? '/', 'http://localhost');\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (Array.isArray(value)) {\n for (const v of value) headers.set(key, v);\n } else if (value !== undefined) {\n headers.set(key, value);\n }\n }\n\n const method = req.method ?? 'GET';\n // GET/HEAD/DELETE 无 body(DELETE 可能有 body 但 MCP 协议中 DELETE 用于销毁会话,无 body)\n const hasBody = method !== 'GET' && method !== 'HEAD' && method !== 'DELETE';\n\n // 使用 Node.js 原生 Readable.toWeb 转换,正确处理多 chunk、backpressure、error\n const body = hasBody ? (Readable.toWeb(req) as ReadableStream<Uint8Array>) : undefined;\n\n const request = new Request(url, {\n method,\n headers,\n ...(body && { body, duplex: 'half' as const }),\n } as RequestInit);\n\n const response = await handleMcpRequest(request, mcp);\n\n res.statusCode = response.status;\n response.headers.forEach((value, key) => {\n res.setHeader(key, value);\n });\n\n if (response.body) {\n // 用 pipe 处理流式响应(SSE 流),避免 text() 阻塞\n // 注意:SSE 流不主动结束,监听 'close'(客户端断开或 res 销毁)而非仅 'finish'\n const nodeStream = Readable.fromWeb(response.body as never);\n await new Promise<void>((resolve, reject) => {\n let settled = false;\n const settle = (err?: Error) => {\n if (settled) return;\n settled = true;\n if (err) reject(err);\n else resolve();\n };\n nodeStream.on('error', (err) => settle(err));\n res.on('error', (err: Error) => settle(err));\n res.on('finish', () => settle());\n res.on('close', () => settle());\n nodeStream.pipe(res);\n });\n return;\n }\n\n res.end();\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/mcpServer.ts","../src/jsonRpc.ts","../src/session.ts","../src/streamableHttp.ts","../src/faapiAdapter.ts"],"sourcesContent":["/**\n * MCP Server 核心:tool/resource/prompt 注册 + JSON-RPC 方法分发\n *\n * 实现 MCP 协议的核心方法:\n * - initialize:握手,返回 serverInfo + capabilities(根据注册情况动态生成)\n * - notifications/initialized:通知,无响应\n * - tools/list、tools/call:tool 列表与调用\n * - resources/list、resources/read:资源列表与读取\n * - prompts/list、prompts/get:提示列表与获取\n * - ping:心跳\n *\n * Tool 输入参数用 zod schema 声明,通过 zod v4 内置 toJSONSchema 转为 JSON Schema。\n */\n\nimport { z, toJSONSchema } from 'zod';\nimport type { JsonRpcRequest, JsonRpcMessage, JsonRpcErrorResponse } from './jsonRpc';\nimport { createResultResponse, createErrorResponse, ErrorCode, isNotification } from './jsonRpc';\nimport { SessionManager, type McpSession, type LoggingLevel } from './session';\n\n// ─── 协议常量 ───────────────────────────────────────────\n\n/** MCP 协议版本(当前实现遵循 2025-06-18 规范) */\nexport const PROTOCOL_VERSION = '2025-06-18';\n\n/** 支持的协议版本列表 */\nexport const SUPPORTED_PROTOCOL_VERSIONS = ['2025-06-18', '2025-03-26'] as const;\n\n// ─── 类型定义 ───────────────────────────────────────────\n\nexport interface McpServerOptions {\n /** Server 名称 */\n name: string;\n /** Server 版本 */\n version: string;\n /** 可选显示名称 */\n title?: string;\n /** 可选给客户端的指引说明 */\n instructions?: string;\n /** 会话空闲超时(毫秒),默认 30 分钟;设为 0 表示永不过期 */\n sessionTtl?: number;\n /**\n * 会话数上限,超过时按 LRU(最久未活动)淘汰,默认 1000;0 表示不设上限。\n * 防 initialize 洪水在 TTL 窗口内无限堆内存\n */\n sessionMaxSessions?: number;\n /** GET SSE 流心跳间隔(毫秒),默认 30 秒 */\n sseHeartbeatMs?: number;\n /** list 方法默认每页项数,默认 100 */\n defaultPageSize?: number;\n /** tools 列表是否可变(声明 listChanged: true + removeTool 时推送通知)。默认 false */\n toolsListChanged?: boolean;\n /** resources 列表是否可变(声明 listChanged: true + removeResource 时推送通知)。默认 false */\n resourcesListChanged?: boolean;\n /** prompts 列表是否可变(声明 listChanged: true + removePrompt 时推送通知)。默认 false */\n promptsListChanged?: boolean;\n}\n\nexport interface McpToolResult {\n /** 内容数组(text/image/audio/resource_link/resource) */\n content: Array<Record<string, unknown>>;\n /** 是否为 tool 执行错误(区别于协议错误) */\n isError?: boolean;\n /** 结构化输出(配合 outputSchema 使用) */\n structuredContent?: unknown;\n}\n\n/** sendLogging 函数类型——推送日志到 session 的 SSE 订阅者 */\nexport type SendLoggingFn = (level: LoggingLevel, data: unknown, logger?: string) => void;\n\n/** sendProgress 函数类型——推送进度到 session 的 SSE 订阅者 */\nexport type SendProgressFn = (progress: number, total?: number) => void;\n\n/** 自定义方法的请求 extra(与 tool/resource/prompt handler extra 共享 send* 能力) */\nexport interface RequestExtra {\n /** 会话 ID */\n sessionId: string;\n /** 推送日志到客户端 SSE 流(无订阅者或被级别过滤时静默丢弃) */\n sendLogging: SendLoggingFn;\n /** 推送进度到客户端 SSE 流(无 progressToken 或无订阅者时静默丢弃) */\n sendProgress: SendProgressFn;\n}\n\n/** 自定义方法 handler 返回值——对象作为 result,JsonRpcErrorResponse 作为错误 */\nexport type MethodHandlerResult = Record<string, unknown> | JsonRpcErrorResponse;\n\n/** 自定义 JSON-RPC 方法 handler 类型 */\nexport type MethodHandler = (\n params: unknown,\n session: McpSession | undefined,\n extra: RequestExtra,\n) => Promise<MethodHandlerResult> | MethodHandlerResult;\n\nexport interface ToolCallExtra {\n /** 会话 ID */\n sessionId: string;\n /** 推送日志到客户端 SSE 流(无订阅者或被级别过滤时静默丢弃) */\n sendLogging: SendLoggingFn;\n /** 推送进度到客户端 SSE 流(无 progressToken 或无订阅者时静默丢弃) */\n sendProgress: SendProgressFn;\n}\n\nexport interface McpToolDefinition {\n /** 工具描述 */\n description?: string;\n /** 输入参数 schema(zod raw shape,如 { name: z.string() }) */\n input?: Record<string, z.ZodType>;\n /** 工具注解(如 readOnlyHint、destructiveHint 等) */\n annotations?: Record<string, unknown>;\n /** 处理函数 */\n handler: (\n args: Record<string, unknown>,\n extra: ToolCallExtra,\n ) => Promise<McpToolResult> | McpToolResult;\n}\n\n// ─── resource 类型 ─────────────────────────────────────\n\nexport interface McpResourceContent {\n /** 资源 URI */\n uri: string;\n /** MIME 类型 */\n mimeType?: string;\n /** 文本内容(与 blob 二选一) */\n text?: string;\n /** 二进制内容(Base64 编码,与 text 二选一) */\n blob?: string;\n}\n\nexport interface McpResourceReadResult {\n /** 资源内容数组(支持一个 URI 返回多段内容) */\n contents: McpResourceContent[];\n}\n\nexport interface ResourceReadExtra {\n /** 会话 ID */\n sessionId: string;\n /** 推送日志到客户端 SSE 流(无订阅者或被级别过滤时静默丢弃) */\n sendLogging: SendLoggingFn;\n /** 推送进度到客户端 SSE 流(无 progressToken 或无订阅者时静默丢弃) */\n sendProgress: SendProgressFn;\n}\n\nexport interface McpResourceDefinition {\n /** 资源名称(必填) */\n name: string;\n /** 资源描述 */\n description?: string;\n /** MIME 类型 */\n mimeType?: string;\n /** 读取 handler(必填) */\n read: (\n uri: string,\n extra: ResourceReadExtra,\n ) => Promise<McpResourceReadResult> | McpResourceReadResult;\n}\n\ninterface RegisteredResource {\n uri: string;\n definition: McpResourceDefinition;\n}\n\n// ─── resource template 类型 ─────────────────────────\n\nexport interface ResourceTemplateReadExtra {\n /** 会话 ID */\n sessionId: string;\n /** 推送日志到客户端 SSE 流(无订阅者或被级别过滤时静默丢弃) */\n sendLogging: SendLoggingFn;\n /** 推送进度到客户端 SSE 流(无 progressToken 或无订阅者时静默丢弃) */\n sendProgress: SendProgressFn;\n}\n\nexport interface McpResourceTemplateDefinition {\n /** 资源名称(必填) */\n name: string;\n /** 资源描述 */\n description?: string;\n /** MIME 类型 */\n mimeType?: string;\n /** 读取 handler(必填):接收实际 URI 和从 URI 模板提取的 params */\n read: (\n uri: string,\n params: Record<string, string>,\n extra: ResourceTemplateReadExtra,\n ) => Promise<McpResourceReadResult> | McpResourceReadResult;\n}\n\ninterface RegisteredResourceTemplate {\n uriTemplate: string;\n /** 编译后的正则表达式(用于匹配 URI) */\n regex: RegExp;\n /** 模板中的变量名列表(按出现顺序) */\n paramNames: string[];\n definition: McpResourceTemplateDefinition;\n}\n\n// ─── prompt 类型 ───────────────────────────────────────\n\nexport interface McpPromptArgument {\n /** 参数名称 */\n name: string;\n /** 参数描述 */\n description?: string;\n /** 是否必填 */\n required?: boolean;\n}\n\nexport interface McpPromptContent {\n /** 内容类型:text 或 image */\n type: 'text' | 'image';\n /** 文本内容(type=text 时) */\n text?: string;\n /** Base64 编码的图片数据(type=image 时) */\n data?: string;\n /** 图片 MIME 类型(type=image 时) */\n mimeType?: string;\n}\n\nexport interface McpPromptMessage {\n /** 角色 */\n role: 'user' | 'assistant';\n /** 内容 */\n content: McpPromptContent;\n}\n\nexport interface McpPromptGetResult {\n /** 提示消息列表 */\n messages: McpPromptMessage[];\n}\n\nexport interface PromptGetExtra {\n /** 会话 ID */\n sessionId: string;\n /** 推送日志到客户端 SSE 流(无订阅者或被级别过滤时静默丢弃) */\n sendLogging: SendLoggingFn;\n /** 推送进度到客户端 SSE 流(无 progressToken 或无订阅者时静默丢弃) */\n sendProgress: SendProgressFn;\n}\n\nexport interface McpPromptDefinition {\n /** 提示描述 */\n description?: string;\n /** 参数定义 */\n arguments?: McpPromptArgument[];\n /** 获取 handler(必填) */\n get: (\n args: Record<string, string>,\n extra: PromptGetExtra,\n ) => Promise<McpPromptGetResult> | McpPromptGetResult;\n}\n\ninterface RegisteredPrompt {\n name: string;\n definition: McpPromptDefinition;\n}\n\ninterface RegisteredTool {\n name: string;\n definition: McpToolDefinition;\n /** 包装后的 z.object schema(用于 safeParse) */\n inputSchema: z.ZodObject<Record<string, z.ZodType>> | undefined;\n /** JSON Schema 表示(用于 tools/list 响应) */\n jsonSchema: Record<string, unknown> | undefined;\n}\n\n// ─── completion 类型 ──────────────────────────────────\n\n/** completion 引用——指向某个 prompt 或 resource template */\nexport type CompletionRef =\n | { type: 'ref/prompt'; name: string }\n | { type: 'ref/resource'; uri: string };\n\n/** completion 调用上下文——传递给 handler */\nexport interface CompletionContext {\n /** 客户端已填写的其他参数(部分填充,key 为参数名,value 为已填值) */\n arguments: Record<string, string>;\n}\n\n/** completion 返回结果 */\nexport interface CompletionResult {\n /** 候选值数组 */\n values: string[];\n /** 总数(可选,用于客户端提示\"还有 N 项\") */\n total?: number;\n /** 是否还有更多(可选,提示客户端可分页或继续输入) */\n hasMore?: boolean;\n}\n\n/** completion handler 函数类型 */\nexport type CompletionHandler = (\n value: string,\n context: CompletionContext,\n) => Promise<CompletionResult> | CompletionResult;\n\ninterface RegisteredCompletion {\n /** 引用类型 'ref/prompt' | 'ref/resource' */\n refType: 'ref/prompt' | 'ref/resource';\n /** 引用标识(prompt name 或 resource template uri) */\n refId: string;\n /** 参数名 */\n argumentName: string;\n /** 补全 handler */\n handler: CompletionHandler;\n}\n\ninterface RegisteredMethod {\n name: string;\n handler: MethodHandler;\n}\n\n/** MCP 内置方法——禁止业务方注册同名方法,避免覆盖协议行为 */\nconst BUILTIN_METHODS = new Set<string>([\n 'initialize',\n 'ping',\n 'notifications/initialized',\n 'notifications/cancelled',\n 'tools/list',\n 'tools/call',\n 'resources/list',\n 'resources/read',\n 'resources/templates/list',\n 'resources/subscribe',\n 'resources/unsubscribe',\n 'prompts/list',\n 'prompts/get',\n 'logging/setLevel',\n 'completion/complete',\n]);\n\n// ─── 分页工具 ───────────────────────────────────────────\n\n/** 默认每页项数 */\nconst DEFAULT_PAGE_SIZE = 100;\n\n/** 将偏移量编码为不透明 cursor 字符串 */\nfunction encodeCursor(offset: number): string {\n return Buffer.from(String(offset)).toString('base64');\n}\n\n/** 解码 cursor,返回偏移量;无效时抛错 */\nfunction decodeCursor(cursor: string): number {\n const decoded = Buffer.from(cursor, 'base64').toString('utf-8');\n const offset = Number.parseInt(decoded, 10);\n if (!Number.isFinite(offset) || offset < 0 || decoded !== String(offset)) {\n throw new Error(`Invalid cursor: ${cursor}`);\n }\n return offset;\n}\n\n/** 对数组切片分页,返回当前页数据和可选的 nextCursor */\nfunction paginate<T>(\n items: T[],\n cursor: string | undefined,\n pageSize: number,\n): { items: T[]; nextCursor?: string } {\n const offset = cursor ? decodeCursor(cursor) : 0;\n if (offset > items.length) {\n throw new Error(`cursor out of range: offset ${offset} > total ${items.length}`);\n }\n const slice = items.slice(offset, offset + pageSize);\n const nextOffset = offset + pageSize;\n const nextCursor = nextOffset < items.length ? encodeCursor(nextOffset) : undefined;\n return { items: slice, nextCursor };\n}\n\n// ─── URI 模板编译/匹配 ─────────────────────────────────\n\n/** 变量名合法字符:[A-Za-z_][A-Za-z0-9_]* */\nconst VAR_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/** 占位符正则:匹配 {varName} */\nconst PLACEHOLDER_RE = /\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g;\n\n/**\n * 编译 URI 模板为正则 + 变量名列表\n *\n * - `file://docs/{path}` → regex `^file://docs/(.+)$`, paramNames `['path']`\n * - `git://repo/{owner}/{repo}` → regex `^git://repo/([^/]+)/([^/]+)$`, paramNames `['owner', 'repo']`\n *\n * 约束:\n * - 模板必须有至少一个 `{var}` 占位符\n * - 不支持 RFC 6570 操作符前缀(`{+var}`、`{?var}`、`{/var}` 等)\n * - 单变量贪婪匹配到 URI 结尾(允许包含 `/`)\n * - 多变量非贪婪匹配(不含 `/`),避免歧义\n *\n * @throws Error 模板格式无效\n */\nfunction compileUriTemplate(uriTemplate: string): { regex: RegExp; paramNames: string[] } {\n const paramNames: string[] = [];\n // 收集变量名并校验\n let match: RegExpExecArray | null;\n PLACEHOLDER_RE.lastIndex = 0;\n while ((match = PLACEHOLDER_RE.exec(uriTemplate)) !== null) {\n const name = match[1]!;\n if (!VAR_NAME_RE.test(name)) {\n throw new Error(`Invalid URI template variable name: ${name}`);\n }\n paramNames.push(name);\n }\n if (paramNames.length === 0) {\n throw new Error(`URI template must contain at least one {var} placeholder: ${uriTemplate}`);\n }\n\n // 检查重复变量名\n const seen = new Set<string>();\n for (const name of paramNames) {\n if (seen.has(name)) {\n throw new Error(`Duplicate URI template variable: ${name}`);\n }\n seen.add(name);\n }\n\n // 构建正则:转义字面字符,替换占位符\n // 单变量时贪婪(允许 /),多变量时非贪婪(不含 /)\n const isSingle = paramNames.length === 1;\n // 分段构建:按 {var} 分割,字面部分转义,占位符替换为捕获组\n const parts: string[] = [];\n let lastIdx = 0;\n PLACEHOLDER_RE.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = PLACEHOLDER_RE.exec(uriTemplate)) !== null) {\n // 转义字面部分\n const literal = uriTemplate.slice(lastIdx, m.index);\n parts.push(escapeRegex(literal));\n // 替换占位符为捕获组\n parts.push(isSingle ? '(.+)' : '([^/]+)');\n lastIdx = m.index + m[0]!.length;\n }\n // 尾部字面部分\n parts.push(escapeRegex(uriTemplate.slice(lastIdx)));\n const regex = new RegExp(`^${parts.join('')}$`);\n return { regex, paramNames };\n}\n\n/** 转义正则字面字符 */\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * 用编译后的模板匹配 URI,提取参数\n *\n * @returns 匹配成功返回 params 对象,失败返回 null\n */\nfunction matchUriTemplate(\n regex: RegExp,\n paramNames: string[],\n uri: string,\n): Record<string, string> | null {\n const m = regex.exec(uri);\n if (!m) return null;\n const params: Record<string, string> = {};\n for (let i = 0; i < paramNames.length; i++) {\n params[paramNames[i]!] = m[i + 1]!;\n }\n return params;\n}\n\n// ─── McpServer ──────────────────────────────────────────\n\nexport class McpServer {\n private tools = new Map<string, RegisteredTool>();\n private resources = new Map<string, RegisteredResource>();\n private resourceTemplates = new Map<string, RegisteredResourceTemplate>();\n private prompts = new Map<string, RegisteredPrompt>();\n private completions = new Map<string, RegisteredCompletion>();\n private methods = new Map<string, RegisteredMethod>();\n private sessions: SessionManager;\n private readonly pageSize: number;\n private readonly toolsListChanged: boolean;\n private readonly resourcesListChanged: boolean;\n private readonly promptsListChanged: boolean;\n\n constructor(private options: McpServerOptions) {\n this.sessions = new SessionManager({\n ttl: this.options.sessionTtl,\n maxSessions: this.options.sessionMaxSessions,\n });\n this.pageSize = this.options.defaultPageSize ?? DEFAULT_PAGE_SIZE;\n this.toolsListChanged = this.options.toolsListChanged ?? false;\n this.resourcesListChanged = this.options.resourcesListChanged ?? false;\n this.promptsListChanged = this.options.promptsListChanged ?? false;\n }\n\n /** 注册 tool */\n tool(name: string, definition: McpToolDefinition): void {\n if (this.tools.has(name)) {\n throw new Error(`Tool \"${name}\" is already registered`);\n }\n let inputSchema: z.ZodObject<Record<string, z.ZodType>> | undefined;\n let jsonSchema: Record<string, unknown> | undefined;\n\n if (definition.input) {\n // 将 raw shape 包装为 z.object\n inputSchema = buildZodObject(definition.input);\n // 转为 JSON Schema(MCP 协议要求)\n jsonSchema = toJSONSchema(inputSchema, { target: 'draft-7' }) as Record<string, unknown>;\n // 移除 $schema 字段(MCP 规范不需要)\n delete jsonSchema.$schema;\n // 移除 additionalProperties: false(MCP 客户端兼容性)\n delete jsonSchema.additionalProperties;\n }\n\n this.tools.set(name, { name, definition, inputSchema, jsonSchema });\n }\n\n /** 注册 resource */\n resource(uri: string, definition: McpResourceDefinition): void {\n if (this.resources.has(uri)) {\n throw new Error(`Resource \"${uri}\" is already registered`);\n }\n this.resources.set(uri, { uri, definition });\n }\n\n /** 注册 resource template(RFC 6570 URI 模板) */\n resourceTemplate(uriTemplate: string, definition: McpResourceTemplateDefinition): void {\n if (this.resourceTemplates.has(uriTemplate)) {\n throw new Error(`Resource template \"${uriTemplate}\" is already registered`);\n }\n const { regex, paramNames } = compileUriTemplate(uriTemplate);\n this.resourceTemplates.set(uriTemplate, {\n uriTemplate,\n regex,\n paramNames,\n definition,\n });\n }\n\n /** 注册 prompt */\n prompt(name: string, definition: McpPromptDefinition): void {\n if (this.prompts.has(name)) {\n throw new Error(`Prompt \"${name}\" is already registered`);\n }\n this.prompts.set(name, { name, definition });\n }\n\n /**\n * 注册参数补全 handler\n *\n * - `ref`:`{ type: 'ref/prompt', name }` 或 `{ type: 'ref/resource', uri }`(对资源模板,uri 是模板字符串)\n * - `argumentName`:补全的参数名(对应 prompt arguments 中的 name,或 resource template URI 模板中的变量名)\n * - 同一 (ref, argumentName) 重复注册抛错\n */\n completion(ref: CompletionRef, argumentName: string, handler: CompletionHandler): void {\n const refType = ref.type;\n const refId = refType === 'ref/prompt' ? ref.name : ref.uri;\n const key = completionKey(refType, refId, argumentName);\n if (this.completions.has(key)) {\n throw new Error(\n `Completion for ${refType} \"${refId}\" argument \"${argumentName}\" is already registered`,\n );\n }\n this.completions.set(key, { refType, refId, argumentName, handler });\n }\n\n /**\n * 注册自定义 JSON-RPC 方法 handler(业务拓展)\n *\n * - 方法名建议使用 `appName/action` 格式,避免与 MCP 标准方法冲突\n * - 与 MCP 内置方法(initialize/ping/tools/* 等)冲突时抛错\n * - 重复注册同名方法抛错\n *\n * handler 返回值规则:\n * - 返回普通对象:作为 JSON-RPC result 字段\n * - 返回 JsonRpcErrorResponse(含 error 字段):作为错误响应\n */\n method(name: string, handler: MethodHandler): void {\n if (BUILTIN_METHODS.has(name)) {\n throw new Error(`Cannot register built-in method \"${name}\": it is reserved by MCP protocol`);\n }\n if (this.methods.has(name)) {\n throw new Error(`Method \"${name}\" is already registered`);\n }\n this.methods.set(name, { name, handler });\n }\n\n /** 获取已注册 tool 名称列表 */\n listTools(): string[] {\n return [...this.tools.keys()];\n }\n\n /** 获取已注册 resource URI 列表 */\n listResources(): string[] {\n return [...this.resources.keys()];\n }\n\n /** 获取已注册 prompt 名称列表 */\n listPrompts(): string[] {\n return [...this.prompts.keys()];\n }\n\n /** 获取已注册自定义方法名列表(业务拓展) */\n listMethods(): string[] {\n return [...this.methods.keys()];\n }\n\n // ─── remove 方法(运行时删除注册项) ───────────────────\n\n /**\n * 删除已注册 tool\n *\n * - `toolsListChanged: true` 时自动推送 `notifications/tools/list_changed`\n * - `toolsListChanged: false` 时静默删除(用于 dev 热替换,客户端不感知)\n */\n removeTool(name: string): boolean {\n const deleted = this.tools.delete(name);\n if (deleted && this.toolsListChanged) {\n this.notifyToolsListChanged();\n }\n return deleted;\n }\n\n /**\n * 删除已注册 resource\n *\n * - `resourcesListChanged: true` 时自动推送 `notifications/resources/list_changed`\n * - `{ silent: true }` 跳过推送——批量重建场景(如 schemaServer 先清后建)在\n * 末尾统一广播一次,避免 N 次删除产生 N 次广播风暴\n */\n removeResource(uri: string, options?: { silent?: boolean }): boolean {\n const deleted = this.resources.delete(uri);\n if (deleted && this.resourcesListChanged && !options?.silent) {\n this.notifyResourcesListChanged();\n }\n return deleted;\n }\n\n /**\n * 删除已注册 resource template\n *\n * - `resourcesListChanged: true` 时自动推送 `notifications/resources/list_changed`\n * - `{ silent: true }` 跳过推送(批量重建场景,同 removeResource)\n */\n removeResourceTemplate(uriTemplate: string, options?: { silent?: boolean }): boolean {\n const deleted = this.resourceTemplates.delete(uriTemplate);\n if (deleted && this.resourcesListChanged && !options?.silent) {\n this.notifyResourcesListChanged();\n }\n return deleted;\n }\n\n /**\n * 删除已注册 prompt\n *\n * - `promptsListChanged: true` 时自动推送 `notifications/prompts/list_changed`\n */\n removePrompt(name: string): boolean {\n const deleted = this.prompts.delete(name);\n if (deleted && this.promptsListChanged) {\n this.notifyPromptsListChanged();\n }\n return deleted;\n }\n\n /**\n * 删除已注册 completion handler\n *\n * - completion 无 list_changed 通知机制(客户端按需请求,无需感知列表变化)\n */\n removeCompletion(ref: CompletionRef, argumentName: string): boolean {\n const refType = ref.type;\n const refId = refType === 'ref/prompt' ? ref.name : ref.uri;\n const key = completionKey(refType, refId, argumentName);\n return this.completions.delete(key);\n }\n\n /** 删除已注册自定义方法 */\n removeMethod(name: string): boolean {\n return this.methods.delete(name);\n }\n\n // ─── list_changed 通知 ───────────────────────────────\n\n /**\n * 推送 `notifications/tools/list_changed` 到所有 session 的 SSE 订阅者\n *\n * 客户端收到后应重新调用 `tools/list` 拉取最新列表。\n * `removeTool` 在 `toolsListChanged: true` 时自动调用本方法,\n * 业务方也可手动调用(如批量删除后只推送一次)。\n */\n notifyToolsListChanged(): void {\n this.broadcastNotificationToAllSessions('notifications/tools/list_changed');\n }\n\n /** 推送 `notifications/resources/list_changed` 到所有 session 的 SSE 订阅者 */\n notifyResourcesListChanged(): void {\n this.broadcastNotificationToAllSessions('notifications/resources/list_changed');\n }\n\n /** 推送 `notifications/prompts/list_changed` 到所有 session 的 SSE 订阅者 */\n notifyPromptsListChanged(): void {\n this.broadcastNotificationToAllSessions('notifications/prompts/list_changed');\n }\n\n /**\n * 向所有 session 的所有 SSE 订阅者广播通知(内部工具方法)\n *\n * 用于 list_changed 这种\"全局广播\"语义——所有 session 都应感知列表变化。\n */\n private broadcastNotificationToAllSessions(method: string, params?: unknown): void {\n const notification: { jsonrpc: '2.0'; method: string; params?: unknown } = {\n jsonrpc: '2.0',\n method,\n };\n if (params !== undefined) {\n notification.params = params;\n }\n const sseData = `data: ${JSON.stringify(notification)}\\n\\n`;\n for (const sessionId of this.sessions.allSessionIds()) {\n this.sessions.broadcastToSession(sessionId, sseData);\n }\n }\n\n /** 获取会话管理器 */\n getSessionManager(): SessionManager {\n return this.sessions;\n }\n\n /** 获取 GET SSE 流心跳间隔(毫秒) */\n getSseHeartbeatMs(): number {\n return this.options.sseHeartbeatMs ?? 30_000;\n }\n\n /**\n * 处理 JSON-RPC 请求,返回响应消息\n *\n * 通知(无 id)不返回响应,返回 null。\n */\n async handleJsonRpc(\n message: JsonRpcMessage,\n session: McpSession | undefined,\n ): Promise<JsonRpcMessage | null> {\n // 通知:无响应\n if (isNotification(message)) {\n return this.handleNotification(message, session);\n }\n\n // 请求:必须有 id\n if ('id' in message && 'method' in message && !('result' in message) && !('error' in message)) {\n return this.handleRequest(message, session);\n }\n\n // 其他消息类型(response 等):忽略\n return null;\n }\n\n private handleNotification(\n message: { method: string; params?: unknown },\n session: McpSession | undefined,\n ): null {\n switch (message.method) {\n case 'notifications/initialized':\n if (session) session.initialized = true;\n break;\n case 'notifications/cancelled':\n // 请求取消——v1 不实现取消逻辑,仅接收\n break;\n }\n return null;\n }\n\n private async handleRequest(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): Promise<JsonRpcMessage> {\n // initialize 之外的请求要求已完成握手(客户端必须先发 notifications/initialized)\n // 与官方 SDK 行为一致,防止客户端跳过能力协商直接调用 tool\n if (session && !session.initialized && request.method !== 'initialize') {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidRequest,\n 'Server not initialized: send \"initialize\" request and \"notifications/initialized\" notification first',\n );\n }\n try {\n switch (request.method) {\n case 'initialize':\n return this.handleInitialize(request, session);\n\n case 'ping':\n return createResultResponse(request.id, {});\n\n case 'tools/list':\n return this.handleToolsList(request);\n\n case 'tools/call':\n return await this.handleToolsCall(request, session);\n\n case 'resources/list':\n return this.handleResourcesList(request);\n\n case 'resources/read':\n return await this.handleResourcesRead(request, session);\n\n case 'prompts/list':\n return this.handlePromptsList(request);\n\n case 'prompts/get':\n return await this.handlePromptsGet(request, session);\n\n case 'logging/setLevel':\n return this.handleLoggingSetLevel(request, session);\n\n case 'resources/subscribe':\n return this.handleResourcesSubscribe(request, session);\n\n case 'resources/unsubscribe':\n return this.handleResourcesUnsubscribe(request, session);\n\n case 'resources/templates/list':\n return this.handleResourcesTemplatesList(request);\n\n case 'completion/complete':\n return await this.handleCompletionComplete(request);\n\n default:\n // 自定义方法分发(业务拓展)\n if (this.methods.has(request.method)) {\n return await this.handleCustomMethod(request, session);\n }\n return createErrorResponse(\n request.id,\n ErrorCode.MethodNotFound,\n `Method not found: ${request.method}`,\n );\n }\n } catch (err) {\n return createErrorResponse(\n request.id,\n ErrorCode.InternalError,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n\n // ─── initialize ──────────────────────────────────────\n\n private handleInitialize(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): JsonRpcMessage {\n const params = (request.params ?? {}) as {\n protocolVersion?: string;\n clientInfo?: { name: string; version: string };\n };\n\n // 协议版本协商\n const requestedVersion = params.protocolVersion ?? PROTOCOL_VERSION;\n const protocolVersion = (SUPPORTED_PROTOCOL_VERSIONS as readonly string[]).includes(\n requestedVersion,\n )\n ? requestedVersion\n : PROTOCOL_VERSION;\n\n // 如果没有 session(直接调用 handleJsonRpc,非 transport 场景),创建一个\n // transport 层已创建 session 时则复用\n if (!session) {\n session = this.sessions.create();\n }\n session.protocolVersion = protocolVersion;\n session.clientInfo = params.clientInfo;\n\n // capability 根据实际注册情况动态生成\n const capabilities: Record<string, unknown> = {\n tools: { listChanged: this.toolsListChanged },\n // logging capability 始终声明——服务端内置日志推送能力\n logging: {},\n };\n if (this.resources.size > 0 || this.resourceTemplates.size > 0) {\n // 注册了 resource 或 resource template 时声明 resources capability + subscribe 子能力\n capabilities.resources = { listChanged: this.resourcesListChanged, subscribe: true };\n }\n if (this.prompts.size > 0) {\n capabilities.prompts = { listChanged: this.promptsListChanged };\n }\n\n const result = {\n protocolVersion,\n capabilities,\n serverInfo: {\n name: this.options.name,\n version: this.options.version,\n ...(this.options.title && { title: this.options.title }),\n },\n ...(this.options.instructions && { instructions: this.options.instructions }),\n };\n\n return createResultResponse(request.id, result);\n }\n\n // ─── tools/list ──────────────────────────────────────\n\n private handleToolsList(request: JsonRpcRequest): JsonRpcMessage {\n const params = (request.params ?? {}) as { cursor?: string };\n const allTools = Array.from(this.tools.values()).map((tool) => ({\n name: tool.name,\n ...(tool.definition.description && { description: tool.definition.description }),\n inputSchema: tool.jsonSchema ?? { type: 'object', properties: {} },\n ...(tool.definition.annotations && { annotations: tool.definition.annotations }),\n }));\n\n let paged: { items: typeof allTools; nextCursor?: string };\n try {\n paged = paginate(allTools, params.cursor, this.pageSize);\n } catch (err) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n err instanceof Error ? err.message : 'Invalid cursor',\n );\n }\n\n return createResultResponse(request.id, {\n tools: paged.items,\n ...(paged.nextCursor && { nextCursor: paged.nextCursor }),\n });\n }\n\n // ─── tools/call ──────────────────────────────────────\n\n private async handleToolsCall(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): Promise<JsonRpcMessage> {\n const params = (request.params ?? {}) as {\n name?: string;\n arguments?: Record<string, unknown>;\n _meta?: { progressToken?: unknown };\n };\n\n if (!params.name) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Missing required parameter: name',\n );\n }\n\n const tool = this.tools.get(params.name);\n if (!tool) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n `Unknown tool: ${params.name}`,\n );\n }\n\n // 校验输入参数\n const args = params.arguments ?? {};\n // 无 inputSchema 的 tool:原样透传;有 inputSchema 且校验通过:替换为解析后的值\n let argsForHandler: Record<string, unknown> = args;\n if (tool.inputSchema) {\n const parsed = tool.inputSchema.safeParse(args);\n if (!parsed.success) {\n // 参数校验失败按 MCP 规范放入 result.isError=true(tool 层错误,LLM 可据此自纠),\n // 而非协议层 -32602——协议错误会让部分客户端终止调用而非把错误喂回模型重试\n const issueText = parsed.error.issues\n .map((i) => `${i.path.join('.') || '(root)'}: ${i.message}`)\n .join('; ');\n return createResultResponse(request.id, {\n content: [\n {\n type: 'text',\n text: `Invalid tool arguments: ${issueText}`,\n },\n ],\n isError: true,\n });\n }\n // 使用解析后的值(含默认值、coerce、未知字段 strip 等)——\n // 不能 Object.assign 回原对象:那会把 schema 未声明的任意字段原样透传给\n // handler,inputSchema 的 strip 语义失效(原型污染/隐藏开关类注入面)\n argsForHandler = parsed.data;\n }\n\n // 从请求 _meta.progressToken 提取进度 token(任意 JSON 值)\n const progressToken = params._meta?.progressToken;\n\n // 调用 handler\n const extra: ToolCallExtra = {\n sessionId: session?.id ?? '',\n sendLogging: (level, data, logger) => {\n if (session) this.sendLogging(session.id, level, data, logger);\n },\n sendProgress: (progress, total) => {\n if (session) this.sendProgress(session.id, progressToken, progress, total);\n },\n };\n\n let result: McpToolResult;\n try {\n result = await tool.definition.handler(argsForHandler, extra);\n } catch (err) {\n // tool 执行错误——返回 isError 而非协议错误\n result = {\n content: [\n {\n type: 'text',\n text: `Tool execution failed: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n isError: true,\n };\n }\n\n return createResultResponse(request.id, result);\n }\n\n // ─── resources/list ─────────────────────────────────\n\n private handleResourcesList(request: JsonRpcRequest): JsonRpcMessage {\n const params = (request.params ?? {}) as { cursor?: string };\n const allResources = Array.from(this.resources.values()).map((r) => ({\n uri: r.uri,\n name: r.definition.name,\n ...(r.definition.description && { description: r.definition.description }),\n ...(r.definition.mimeType && { mimeType: r.definition.mimeType }),\n }));\n\n let paged: { items: typeof allResources; nextCursor?: string };\n try {\n paged = paginate(allResources, params.cursor, this.pageSize);\n } catch (err) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n err instanceof Error ? err.message : 'Invalid cursor',\n );\n }\n\n return createResultResponse(request.id, {\n resources: paged.items,\n ...(paged.nextCursor && { nextCursor: paged.nextCursor }),\n });\n }\n\n // ─── resources/read ─────────────────────────────────\n\n private async handleResourcesRead(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): Promise<JsonRpcMessage> {\n const params = (request.params ?? {}) as {\n uri?: string;\n _meta?: { progressToken?: unknown };\n };\n\n if (!params.uri) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Missing required parameter: uri',\n );\n }\n\n // 从请求 _meta.progressToken 提取进度 token(任意 JSON 值)\n const progressToken = params._meta?.progressToken;\n\n // sendProgress 闭包(精确匹配与模板匹配共用,捕获 progressToken)\n const sendProgressFn: SendProgressFn = (progress, total) => {\n if (session) this.sendProgress(session.id, progressToken, progress, total);\n };\n\n const extra: ResourceReadExtra = {\n sessionId: session?.id ?? '',\n sendLogging: (level, data, logger) => {\n if (session) this.sendLogging(session.id, level, data, logger);\n },\n sendProgress: sendProgressFn,\n };\n\n // 1. 先在 resources Map 中查找精确匹配\n const resource = this.resources.get(params.uri);\n if (resource) {\n const result = await resource.definition.read(params.uri, extra);\n return createResultResponse(request.id, result);\n }\n\n // 2. 找不到时,遍历 resourceTemplates 用 URI 模板匹配\n for (const tpl of this.resourceTemplates.values()) {\n const params2 = matchUriTemplate(tpl.regex, tpl.paramNames, params.uri);\n if (params2) {\n const tplExtra: ResourceTemplateReadExtra = {\n sessionId: session?.id ?? '',\n sendLogging: (level, data, logger) => {\n if (session) this.sendLogging(session.id, level, data, logger);\n },\n sendProgress: sendProgressFn,\n };\n const result = await tpl.definition.read(params.uri, params2, tplExtra);\n return createResultResponse(request.id, result);\n }\n }\n\n // 3. 都不匹配\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n `Unknown resource: ${params.uri}`,\n );\n }\n\n // ─── resources/templates/list ──────────────────────\n\n private handleResourcesTemplatesList(request: JsonRpcRequest): JsonRpcMessage {\n const params = (request.params ?? {}) as { cursor?: string };\n const allTemplates = Array.from(this.resourceTemplates.values()).map((t) => ({\n uriTemplate: t.uriTemplate,\n name: t.definition.name,\n ...(t.definition.description && { description: t.definition.description }),\n ...(t.definition.mimeType && { mimeType: t.definition.mimeType }),\n }));\n\n let paged: { items: typeof allTemplates; nextCursor?: string };\n try {\n paged = paginate(allTemplates, params.cursor, this.pageSize);\n } catch (err) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n err instanceof Error ? err.message : 'Invalid cursor',\n );\n }\n\n return createResultResponse(request.id, {\n resourceTemplates: paged.items,\n ...(paged.nextCursor && { nextCursor: paged.nextCursor }),\n });\n }\n\n // ─── prompts/list ───────────────────────────────────\n\n private handlePromptsList(request: JsonRpcRequest): JsonRpcMessage {\n const params = (request.params ?? {}) as { cursor?: string };\n const allPrompts = Array.from(this.prompts.values()).map((p) => ({\n name: p.name,\n ...(p.definition.description && { description: p.definition.description }),\n ...(p.definition.arguments && { arguments: p.definition.arguments }),\n }));\n\n let paged: { items: typeof allPrompts; nextCursor?: string };\n try {\n paged = paginate(allPrompts, params.cursor, this.pageSize);\n } catch (err) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n err instanceof Error ? err.message : 'Invalid cursor',\n );\n }\n\n return createResultResponse(request.id, {\n prompts: paged.items,\n ...(paged.nextCursor && { nextCursor: paged.nextCursor }),\n });\n }\n\n // ─── prompts/get ────────────────────────────────────\n\n private async handlePromptsGet(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): Promise<JsonRpcMessage> {\n const params = (request.params ?? {}) as {\n name?: string;\n arguments?: Record<string, string>;\n _meta?: { progressToken?: unknown };\n };\n\n if (!params.name) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Missing required parameter: name',\n );\n }\n\n const prompt = this.prompts.get(params.name);\n if (!prompt) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n `Unknown prompt: ${params.name}`,\n );\n }\n\n // 从请求 _meta.progressToken 提取进度 token(任意 JSON 值)\n const progressToken = params._meta?.progressToken;\n\n const extra: PromptGetExtra = {\n sessionId: session?.id ?? '',\n sendLogging: (level, data, logger) => {\n if (session) this.sendLogging(session.id, level, data, logger);\n },\n sendProgress: (progress, total) => {\n if (session) this.sendProgress(session.id, progressToken, progress, total);\n },\n };\n\n const args = params.arguments ?? {};\n const result = await prompt.definition.get(args, extra);\n return createResultResponse(request.id, result);\n }\n\n // ─── logging/setLevel ──────────────────────────────\n\n private handleLoggingSetLevel(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): JsonRpcMessage {\n const params = (request.params ?? {}) as { level?: string };\n if (!params.level) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Missing required parameter: level',\n );\n }\n const validLevels: readonly LoggingLevel[] = [\n 'debug',\n 'info',\n 'notice',\n 'warning',\n 'error',\n 'critical',\n 'alert',\n 'emergency',\n ];\n if (!validLevels.includes(params.level as LoggingLevel)) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n `Invalid logging level: ${params.level}`,\n );\n }\n if (session) {\n session.loggingLevel = params.level as LoggingLevel;\n }\n return createResultResponse(request.id, {});\n }\n\n // ─── resources/subscribe ───────────────────────────\n\n private handleResourcesSubscribe(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): JsonRpcMessage {\n const params = (request.params ?? {}) as { uri?: string };\n if (!params.uri) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Missing required parameter: uri',\n );\n }\n if (!session) {\n return createErrorResponse(request.id, ErrorCode.InvalidRequest, 'No session for subscribe');\n }\n this.sessions.subscribeResource(session.id, params.uri);\n return createResultResponse(request.id, {});\n }\n\n // ─── resources/unsubscribe ─────────────────────────\n\n private handleResourcesUnsubscribe(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): JsonRpcMessage {\n const params = (request.params ?? {}) as { uri?: string };\n if (!params.uri) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Missing required parameter: uri',\n );\n }\n if (!session) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidRequest,\n 'No session for unsubscribe',\n );\n }\n this.sessions.unsubscribeResource(session.id, params.uri);\n return createResultResponse(request.id, {});\n }\n\n // ─── completion/complete ───────────────────────────\n\n private async handleCompletionComplete(request: JsonRpcRequest): Promise<JsonRpcMessage> {\n const params = (request.params ?? {}) as {\n ref?: { type: string; name?: string; uri?: string };\n argument?: { name?: string; value?: string };\n arguments?: Record<string, string>;\n };\n\n if (!params.ref || !params.argument) {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Missing required parameter: ref and argument',\n );\n }\n\n const ref = params.ref;\n let refType: 'ref/prompt' | 'ref/resource';\n let refId: string;\n if (ref.type === 'ref/prompt' && typeof ref.name === 'string') {\n refType = 'ref/prompt';\n refId = ref.name;\n } else if (ref.type === 'ref/resource' && typeof ref.uri === 'string') {\n refType = 'ref/resource';\n refId = ref.uri;\n } else {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n `Invalid ref: ${JSON.stringify(ref)}`,\n );\n }\n\n if (typeof params.argument.name !== 'string') {\n return createErrorResponse(\n request.id,\n ErrorCode.InvalidParams,\n 'Missing required parameter: argument.name',\n );\n }\n\n const key = completionKey(refType, refId, params.argument.name);\n const completion = this.completions.get(key);\n if (!completion) {\n return createErrorResponse(\n request.id,\n ErrorCode.MethodNotFound,\n `No completion handler for ${refType} \"${refId}\" argument \"${params.argument.name}\"`,\n );\n }\n\n const value = params.argument.value ?? '';\n const context: CompletionContext = {\n arguments: params.arguments ?? {},\n };\n\n let result: CompletionResult;\n try {\n result = await completion.handler(value, context);\n } catch (err) {\n return createErrorResponse(\n request.id,\n ErrorCode.InternalError,\n err instanceof Error ? err.message : String(err),\n );\n }\n\n return createResultResponse(request.id, { completion: result });\n }\n\n // ─── 自定义方法分发(业务拓展) ───────────────────────\n\n private async handleCustomMethod(\n request: JsonRpcRequest,\n session: McpSession | undefined,\n ): Promise<JsonRpcMessage> {\n const registered = this.methods.get(request.method);\n if (!registered) {\n // 双重检查(并发场景下可能在 default 分支后已被 remove)\n return createErrorResponse(\n request.id,\n ErrorCode.MethodNotFound,\n `Method not found: ${request.method}`,\n );\n }\n\n const extra: RequestExtra = {\n sessionId: session?.id ?? '',\n sendLogging: (level, data, logger) => {\n if (session) this.sendLogging(session.id, level, data, logger);\n },\n sendProgress: (progress, total) => {\n // 自定义方法无 progressToken 来源,始终传 undefined(业务方可直接用 server.sendProgress)\n if (session) this.sendProgress(session.id, undefined, progress, total);\n },\n };\n\n let result: MethodHandlerResult;\n try {\n result = await registered.handler(request.params, session, extra);\n } catch (err) {\n return createErrorResponse(\n request.id,\n ErrorCode.InternalError,\n err instanceof Error ? err.message : String(err),\n );\n }\n\n // 返回 JsonRpcErrorResponse 表示业务错误\n if (\n result !== null &&\n typeof result === 'object' &&\n 'error' in result &&\n result.error !== null &&\n typeof result.error === 'object'\n ) {\n // 已是错误响应格式——补全 id 后返回\n const errResp = result as unknown as JsonRpcErrorResponse;\n return { ...errResp, jsonrpc: '2.0', id: request.id };\n }\n\n // 普通对象——作为 result 返回\n return createResultResponse(request.id, result);\n }\n\n // ─── sendLogging(应用级 + handler extra 共用) ─────\n\n /**\n * 推送 notifications/message 到 session 的所有 SSE 订阅者\n *\n * - 无 session 或无订阅者:静默丢弃\n * - level 低于 session.loggingLevel:静默丢弃\n * - SSE 行格式:`data: ${JSON.stringify(notification)}\\n\\n`\n */\n sendLogging(sessionId: string, level: LoggingLevel, data: unknown, logger?: string): void {\n if (!this.sessions.shouldLog(sessionId, level)) return;\n const notification: { jsonrpc: '2.0'; method: string; params: Record<string, unknown> } = {\n jsonrpc: '2.0',\n method: 'notifications/message',\n params: { level, data },\n };\n if (logger !== undefined) {\n notification.params.logger = logger;\n }\n const sseData = `data: ${JSON.stringify(notification)}\\n\\n`;\n this.sessions.broadcastToSession(sessionId, sseData);\n }\n\n /**\n * 推送 notifications/resources/updated 到所有订阅了该 URI 的 session\n *\n * - 找出所有 subscribedResources 包含该 URI 的 session\n * - 对每个 session 的所有 SSE 订阅者推送通知\n * - 无订阅者的 session 静默跳过\n */\n sendResourceUpdated(uri: string): void {\n const sessionIds = this.sessions.findSubscribersOfUri(uri);\n if (sessionIds.length === 0) return;\n const notification = {\n jsonrpc: '2.0' as const,\n method: 'notifications/resources/updated',\n params: { uri },\n };\n const sseData = `data: ${JSON.stringify(notification)}\\n\\n`;\n for (const sessionId of sessionIds) {\n this.sessions.broadcastToSession(sessionId, sseData);\n }\n }\n\n /**\n * 推送 notifications/progress 到 session 的所有 SSE 订阅者\n *\n * - 无 session 或无订阅者:静默丢弃\n * - progressToken 为 undefined/null:静默丢弃(无法关联进度与请求)\n *\n * @param sessionId 会话 ID\n * @param progressToken 客户端在请求 _meta.progressToken 中传入的 token(任意 JSON 值)\n * @param progress 当前进度(数值)\n * @param total 总数(可选)\n */\n sendProgress(sessionId: string, progressToken: unknown, progress: number, total?: number): void {\n if (progressToken === undefined || progressToken === null) return;\n const params: Record<string, unknown> = { progressToken, progress };\n if (total !== undefined) {\n params.total = total;\n }\n const notification = {\n jsonrpc: '2.0' as const,\n method: 'notifications/progress',\n params,\n };\n const sseData = `data: ${JSON.stringify(notification)}\\n\\n`;\n this.sessions.broadcastToSession(sessionId, sseData);\n }\n\n /**\n * 通用通知推送(业务拓展)——向指定 session 的所有 SSE 订阅者推送任意通知\n *\n * - 无 session 或无订阅者:静默丢弃\n * - 不校验 method 是否符合 MCP 规范——业务方自行负责\n * - SSE 行格式:`data: ${JSON.stringify(notification)}\\n\\n`\n *\n * @param sessionId 目标 session ID\n * @param method 通知方法名(如 `notifications/myapp/sync`)\n * @param params 通知参数(可选)\n */\n sendNotification(sessionId: string, method: string, params?: unknown): void {\n const notification: { jsonrpc: '2.0'; method: string; params?: unknown } = {\n jsonrpc: '2.0',\n method,\n };\n if (params !== undefined) {\n notification.params = params;\n }\n const sseData = `data: ${JSON.stringify(notification)}\\n\\n`;\n this.sessions.broadcastToSession(sessionId, sseData);\n }\n}\n\n// ─── 工具函数 ───────────────────────────────────────────\n\n/**\n * 将 raw shape({ key: z.ZodType })包装为 z.object\n */\nfunction buildZodObject(shape: Record<string, z.ZodType>): z.ZodObject<Record<string, z.ZodType>> {\n return z.object(shape);\n}\n\n/**\n * 生成 completion Map 的键\n *\n * 形如 `ref/prompt:greet:userName` 或 `ref/resource:file://docs/{path}:path`\n */\nfunction completionKey(\n refType: 'ref/prompt' | 'ref/resource',\n refId: string,\n argumentName: string,\n): string {\n return `${refType}:${refId}:${argumentName}`;\n}\n\n// ─── 工厂函数 ───────────────────────────────────────────\n\nexport function createMcpServer(options: McpServerOptions): McpServer {\n return new McpServer(options);\n}\n","/**\n * JSON-RPC 2.0 协议消息类型、解析和响应构建\n *\n * MCP 协议基于 JSON-RPC 2.0,所有通信都是 JSON-RPC 消息。\n * 本模块提供类型定义、消息判定函数和响应构建工具。\n */\n\n// ─── 消息类型 ───────────────────────────────────────────\n\nexport interface JsonRpcRequest {\n jsonrpc: '2.0';\n id: string | number;\n method: string;\n params?: unknown;\n}\n\nexport interface JsonRpcNotification {\n jsonrpc: '2.0';\n method: string;\n params?: unknown;\n}\n\nexport interface JsonRpcResultResponse {\n jsonrpc: '2.0';\n id: string | number;\n result: unknown;\n}\n\nexport interface JsonRpcErrorResponse {\n jsonrpc: '2.0';\n id: string | number | null;\n error: JsonRpcError;\n}\n\nexport interface JsonRpcError {\n code: number;\n message: string;\n data?: unknown;\n}\n\nexport type JsonRpcMessage =\n | JsonRpcRequest\n | JsonRpcNotification\n | JsonRpcResultResponse\n | JsonRpcErrorResponse;\n\n// ─── 错误码 ─────────────────────────────────────────────\n\nexport const ErrorCode = {\n // JSON-RPC 标准错误码\n ParseError: -32700,\n InvalidRequest: -32600,\n MethodNotFound: -32601,\n InvalidParams: -32602,\n InternalError: -32603,\n // MCP 扩展错误码\n ConnectionClosed: -32000,\n RequestTimeout: -32001,\n} as const;\n\n// ─── 消息判定 ───────────────────────────────────────────\n\nexport function isRequest(msg: JsonRpcMessage): msg is JsonRpcRequest {\n return 'method' in msg && 'id' in msg && !('result' in msg) && !('error' in msg);\n}\n\nexport function isNotification(msg: JsonRpcMessage): msg is JsonRpcNotification {\n return 'method' in msg && !('id' in msg);\n}\n\nexport function isResultResponse(msg: JsonRpcMessage): msg is JsonRpcResultResponse {\n return 'result' in msg && 'id' in msg;\n}\n\nexport function isErrorResponse(msg: JsonRpcMessage): msg is JsonRpcErrorResponse {\n return 'error' in msg && 'id' in msg;\n}\n\n// ─── 响应构建 ───────────────────────────────────────────\n\nexport function createResultResponse(id: string | number, result: unknown): JsonRpcResultResponse {\n return { jsonrpc: '2.0', id, result };\n}\n\nexport function createErrorResponse(\n id: string | number | null,\n code: number,\n message: string,\n data?: unknown,\n): JsonRpcErrorResponse {\n const error: JsonRpcError = { code, message };\n if (data !== undefined) error.data = data;\n return { jsonrpc: '2.0', id, error };\n}\n\n// ─── 消息解析 ───────────────────────────────────────────\n\n/**\n * 解析 JSON-RPC 消息(单条或批量)\n *\n * @throws ParseError(返回 Error 对象,不抛异常)\n * @returns 消息数组(单条也包装为数组)\n */\nexport function parseJsonRpcMessage(data: unknown): JsonRpcMessage[] {\n if (Array.isArray(data)) {\n if (data.length === 0) {\n throw new JsonRpcParseError('Invalid Request: empty batch');\n }\n return data.map((item) => parseSingleMessage(item));\n }\n return [parseSingleMessage(data)];\n}\n\n/**\n * 解析 JSON-RPC 批量消息(JSON-RPC 2.0 规范:批内单条无效仅对该条生成\n * ParseError error object(id:null),不整批失败)\n *\n * @returns messages(有效消息)+ invalid(解析失败的条目,以错误响应形态给出,\n * transport 层并入批响应数组)\n */\nexport function parseJsonRpcBatch(data: unknown[]): {\n messages: JsonRpcMessage[];\n invalid: JsonRpcMessage[];\n} {\n const messages: JsonRpcMessage[] = [];\n const invalid: JsonRpcMessage[] = [];\n for (const item of data) {\n try {\n messages.push(parseSingleMessage(item));\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n invalid.push({\n jsonrpc: '2.0',\n id: null,\n error: { code: ErrorCode.ParseError, message: reason },\n } as JsonRpcErrorResponse);\n }\n }\n return { messages, invalid };\n}\n\nclass JsonRpcParseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'JsonRpcParseError';\n }\n}\n\nexport { JsonRpcParseError };\n\nfunction parseSingleMessage(data: unknown): JsonRpcMessage {\n if (typeof data !== 'object' || data === null) {\n throw new JsonRpcParseError('Invalid Request: not an object');\n }\n const obj = data as Record<string, unknown>;\n if (obj.jsonrpc !== '2.0') {\n throw new JsonRpcParseError('Invalid Request: jsonrpc must be \"2.0\"');\n }\n // Request: has method + id\n if (\n typeof obj.method === 'string' &&\n (typeof obj.id === 'string' || typeof obj.id === 'number')\n ) {\n return {\n jsonrpc: '2.0',\n id: obj.id,\n method: obj.method,\n ...(obj.params !== undefined && { params: obj.params }),\n } as JsonRpcRequest;\n }\n // Notification: has method, no id\n if (typeof obj.method === 'string' && obj.id === undefined) {\n return {\n jsonrpc: '2.0',\n method: obj.method,\n ...(obj.params !== undefined && { params: obj.params }),\n } as JsonRpcNotification;\n }\n // Response: has result or error + id\n if ('result' in obj && (typeof obj.id === 'string' || typeof obj.id === 'number')) {\n return {\n jsonrpc: '2.0',\n id: obj.id,\n result: obj.result,\n } as JsonRpcResultResponse;\n }\n if ('error' in obj && obj.error !== null && typeof obj.error === 'object') {\n return {\n jsonrpc: '2.0',\n id: (obj.id as string | number | null) ?? null,\n error: obj.error as JsonRpcError,\n } as JsonRpcErrorResponse;\n }\n throw new JsonRpcParseError('Invalid Request: unknown message shape');\n}\n","/**\n * MCP 会话管理\n *\n * Streamable HTTP transport 通过 Mcp-Session-Id header 维持会话。\n * 会话在 initialize 请求时创建,在 DELETE 请求时销毁。\n *\n * 内置 TTL 机制:超过空闲时间的会话自动过期,防止内存泄漏。\n * 惰性清理:在 get/create 时检查过期会话,无需定时器。\n *\n * 每个 session 维护 SSE subscriber 集合,用于服务端主动推送\n * (logging/message, resources/updated, progress 等通知)。\n */\n\nimport { randomUUID } from 'node:crypto';\n\n/** 日志级别(syslog 严重度,从低到高) */\nexport type LoggingLevel =\n | 'debug'\n | 'info'\n | 'notice'\n | 'warning'\n | 'error'\n | 'critical'\n | 'alert'\n | 'emergency';\n\n/** 日志级别数值(越大越严重) */\nconst LOGGING_LEVEL_ORDER: Record<LoggingLevel, number> = {\n debug: 10,\n info: 20,\n notice: 30,\n warning: 40,\n error: 50,\n critical: 60,\n alert: 70,\n emergency: 80,\n};\n\n/** SSE 流订阅者(controller + 关联的 session id) */\nexport interface SseSubscriber {\n /** SSE 流控制器 */\n controller: ReadableStreamDefaultController<Uint8Array>;\n /** 关联的 session id */\n sessionId: string;\n}\n\nexport interface McpSession {\n /** 会话 ID(cryptographically secure UUID) */\n id: string;\n /** 是否已完成 initialize 握手 */\n initialized: boolean;\n /** 协商的协议版本 */\n protocolVersion: string;\n /** 客户端信息(来自 initialize 请求) */\n clientInfo?: { name: string; version: string };\n /** 创建时间戳 */\n createdAt: number;\n /** 最后活动时间戳(每次 get 时更新) */\n lastActivity: number;\n /** 当前日志级别(低于此级别的日志不推送),默认 info */\n loggingLevel: LoggingLevel;\n /** SSE 流订阅者集合(GET 流注册,用于服务端主动推送) */\n subscribers: Set<SseSubscriber>;\n /** 已订阅的资源 URI 集合(resources/subscribe 注册) */\n subscribedResources: Set<string>;\n}\n\n/** 默认会话空闲超时:30 分钟 */\nconst DEFAULT_TTL = 30 * 60 * 1000;\n\n/** 默认会话数上限:initialize 洪水在 TTL 窗口内可无限堆内存,LRU 兜底 */\nconst DEFAULT_MAX_SESSIONS = 1000;\n\n/** SessionManager 构造选项 */\nexport interface SessionManagerOptions {\n /** 会话空闲超时(毫秒)。默认 30 分钟;0 表示永不过期 */\n ttl?: number;\n /**\n * 会话数上限,超过时按 LRU(最久未活动)淘汰。默认 1000;0 表示不设上限。\n * 淘汰在 create 时惰性执行(先清过期会话,仍超限才淘汰最久未活动的)\n */\n maxSessions?: number;\n}\n\n/**\n * 内存会话管理器\n *\n * 生产环境如需多实例共享会话,可替换为 Redis 等外部存储实现。\n */\nexport class SessionManager {\n private sessions = new Map<string, McpSession>();\n private readonly ttl: number;\n private readonly maxSessions: number;\n\n constructor(options: number | SessionManagerOptions = DEFAULT_TTL) {\n // 兼容旧签名:constructor(ttl: number)\n const opts = typeof options === 'number' ? { ttl: options } : options;\n this.ttl = opts.ttl ?? DEFAULT_TTL;\n this.maxSessions = opts.maxSessions ?? DEFAULT_MAX_SESSIONS;\n }\n\n /** 是否启用 TTL 过期检查(ttl > 0) */\n private get ttlEnabled(): boolean {\n return this.ttl > 0;\n }\n\n /** 是否启用会话数上限(maxSessions > 0) */\n private get capEnabled(): boolean {\n return this.maxSessions > 0;\n }\n\n /** 创建新会话,返回 session 对象 */\n create(): McpSession {\n // 惰性清理:创建新会话时顺便清理过期会话\n if (this.ttlEnabled) this.cleanupExpired();\n\n // 会话数上限:超限时按 LRU(最久未活动)淘汰,给新会话腾位\n if (this.capEnabled) {\n while (this.sessions.size >= this.maxSessions) {\n const oldest = this.evictOldest();\n if (!oldest) break; // 理论不可达(size >= max 必有成员),防御死循环\n }\n }\n\n const now = Date.now();\n const session: McpSession = {\n id: randomUUID(),\n initialized: false,\n protocolVersion: '',\n createdAt: now,\n lastActivity: now,\n loggingLevel: 'info',\n subscribers: new Set(),\n subscribedResources: new Set(),\n };\n this.sessions.set(session.id, session);\n return session;\n }\n\n /** 淘汰最久未活动的会话;无会话可淘汰返回 null */\n private evictOldest(): string | null {\n let oldestId: string | null = null;\n let oldestActivity = Infinity;\n for (const [id, session] of this.sessions) {\n if (session.lastActivity < oldestActivity) {\n oldestActivity = session.lastActivity;\n oldestId = id;\n }\n }\n if (oldestId === null) return null;\n const victim = this.sessions.get(oldestId);\n if (victim) this.closeSubscribers(victim);\n this.sessions.delete(oldestId);\n return oldestId;\n }\n\n /** 按 ID 获取会话(更新最后活动时间,过期返回 undefined) */\n get(id: string): McpSession | undefined {\n const session = this.sessions.get(id);\n if (!session) return undefined;\n\n // 检查是否过期(ttl=0 时跳过)\n if (this.ttlEnabled && Date.now() - session.lastActivity > this.ttl) {\n this.closeSubscribers(session);\n this.sessions.delete(id);\n return undefined;\n }\n\n // 更新最后活动时间\n session.lastActivity = Date.now();\n return session;\n }\n\n /** 是否存在(不更新活动时间,过期会话返回 false) */\n has(id: string): boolean {\n const session = this.sessions.get(id);\n if (!session) return false;\n if (this.ttlEnabled && Date.now() - session.lastActivity > this.ttl) {\n this.closeSubscribers(session);\n this.sessions.delete(id);\n return false;\n }\n return true;\n }\n\n /**\n * 续期会话(仅刷新 lastActivity,不创建、不校验存在性以外的语义)\n *\n * SSE 心跳路径使用:只收推送不发请求的客户端依赖心跳维持会话活跃,\n * 避免活跃长连接因空闲 TTL 被过期清理。\n *\n * @returns 会话存在且未过期返回 true;不存在或已过期返回 false(不复活过期会话)\n */\n touch(id: string): boolean {\n const session = this.sessions.get(id);\n if (!session) return false;\n if (this.ttlEnabled && Date.now() - session.lastActivity > this.ttl) {\n this.closeSubscribers(session);\n this.sessions.delete(id);\n return false;\n }\n session.lastActivity = Date.now();\n return true;\n }\n\n /** 销毁会话(关闭所有 SSE 订阅者) */\n delete(id: string): boolean {\n const session = this.sessions.get(id);\n if (session) {\n this.closeSubscribers(session);\n }\n return this.sessions.delete(id);\n }\n\n /** 当前会话数(含可能尚未清理的过期会话) */\n get size(): number {\n return this.sessions.size;\n }\n\n /** 获取所有 session ID 列表(用于全局广播通知)——先清扫过期会话 */\n allSessionIds(): string[] {\n this.cleanupExpired();\n return [...this.sessions.keys()];\n }\n\n /** 清空所有会话 */\n clear(): void {\n for (const session of this.sessions.values()) {\n this.closeSubscribers(session);\n }\n this.sessions.clear();\n }\n\n /** 注册 SSE 订阅者到 session */\n addSubscriber(\n sessionId: string,\n controller: ReadableStreamDefaultController<Uint8Array>,\n ): SseSubscriber | undefined {\n const session = this.get(sessionId);\n if (!session) return undefined;\n const subscriber: SseSubscriber = { controller, sessionId };\n session.subscribers.add(subscriber);\n return subscriber;\n }\n\n /** 注销 SSE 订阅者 */\n removeSubscriber(subscriber: SseSubscriber): void {\n const session = this.sessions.get(subscriber.sessionId);\n if (session) {\n session.subscribers.delete(subscriber);\n }\n }\n\n /** 向 session 的所有订阅者推送 SSE 数据 */\n broadcastToSession(sessionId: string, data: string): void {\n const session = this.sessions.get(sessionId);\n if (!session) return;\n // 过期会话不投递:同步清扫(关闭订阅者 + 删除),幽灵会话不再常驻内存收广播\n if (this.ttlEnabled && Date.now() - session.lastActivity > this.ttl) {\n this.closeSubscribers(session);\n this.sessions.delete(sessionId);\n return;\n }\n const encoder = new TextEncoder();\n for (const sub of session.subscribers) {\n try {\n sub.controller.enqueue(encoder.encode(data));\n } catch {\n // controller 已关闭,移除订阅者\n session.subscribers.delete(sub);\n }\n }\n }\n\n /** 判断指定级别的日志是否应该推送(>= session.loggingLevel) */\n shouldLog(sessionId: string, level: LoggingLevel): boolean {\n const session = this.sessions.get(sessionId);\n if (!session) return false;\n return LOGGING_LEVEL_ORDER[level] >= LOGGING_LEVEL_ORDER[session.loggingLevel];\n }\n\n /** 添加资源订阅(将 uri 加入 session.subscribedResources) */\n subscribeResource(sessionId: string, uri: string): boolean {\n const session = this.sessions.get(sessionId);\n if (!session) return false;\n session.subscribedResources.add(uri);\n return true;\n }\n\n /** 取消资源订阅(从 session.subscribedResources 移除 uri) */\n unsubscribeResource(sessionId: string, uri: string): boolean {\n const session = this.sessions.get(sessionId);\n if (!session) return false;\n session.subscribedResources.delete(uri);\n return true;\n }\n\n /** 找出所有订阅了指定 URI 的 session id 列表——先清扫过期会话 */\n findSubscribersOfUri(uri: string): string[] {\n this.cleanupExpired();\n const result: string[] = [];\n for (const session of this.sessions.values()) {\n if (session.subscribedResources.has(uri)) {\n result.push(session.id);\n }\n }\n return result;\n }\n\n /** 关闭 session 的所有订阅者(用于 session 销毁/过期) */\n private closeSubscribers(session: McpSession): void {\n for (const sub of session.subscribers) {\n try {\n sub.controller.close();\n } catch {\n // controller 已关闭,忽略\n }\n }\n session.subscribers.clear();\n }\n\n /** 清理所有过期会话 */\n private cleanupExpired(): void {\n if (!this.ttlEnabled) return;\n const now = Date.now();\n for (const [id, session] of this.sessions) {\n if (now - session.lastActivity > this.ttl) {\n this.closeSubscribers(session);\n this.sessions.delete(id);\n }\n }\n }\n}\n","/**\n * Streamable HTTP transport:Web Request → JSON-RPC → Response\n *\n * MCP Streamable HTTP transport 规范:\n * - POST:发送 JSON-RPC 消息,返回 JSON 响应或 SSE 流\n * - GET:打开 SSE 流接收服务端推送\n * - DELETE:终止会话\n *\n * 会话管理通过 Mcp-Session-Id header。\n * 实现:POST 返回 JSON,GET 返回 SSE 流(仅心跳,不推送业务消息),DELETE 销毁会话。\n */\n\nimport type { McpServer } from './mcpServer';\nimport {\n type JsonRpcMessage,\n isRequest,\n isNotification,\n createErrorResponse,\n ErrorCode,\n parseJsonRpcMessage,\n parseJsonRpcBatch,\n JsonRpcParseError,\n} from './jsonRpc';\n\n/** 默认 SSE 心跳间隔 */\nconst DEFAULT_SSE_HEARTBEAT_MS = 30_000;\n\n/**\n * Streamable HTTP transport 选项\n */\nexport interface McpHttpOptions {\n /**\n * 允许的 Origin 列表(MCP 规范安全要求:防 DNS rebinding 攻击)\n *\n * 请求携带 Origin 头且不在列表内 → 403;未携带 Origin(非浏览器客户端)放行。\n * 未配置(undefined)时不校验 Origin——本地开发保持零配置;\n * 生产环境暴露到网络时必须配置(浏览器是最常见的 MCP 客户端载体)。\n */\n allowedOrigins?: string[];\n}\n\n/**\n * 校验 Origin 头(配置了 allowedOrigins 时)\n *\n * 无 Origin 头(curl / 官方 SDK 等非浏览器客户端)放行;\n * 有 Origin 且不匹配 → 403。\n */\nfunction checkOrigin(request: Request, options: McpHttpOptions | undefined): Response | null {\n const allowed = options?.allowedOrigins;\n if (!allowed || allowed.length === 0) return null;\n\n const origin = request.headers.get('origin');\n if (!origin) return null;\n if (allowed.includes(origin)) return null;\n\n return new Response(JSON.stringify({ error: 'Origin not allowed' }), {\n status: 403,\n headers: { 'Content-Type': 'application/json' },\n });\n}\n\n/**\n * 处理 MCP HTTP 请求\n *\n * 接收 Web API Request,返回 Web API Response。\n * 可直接在 faapi handler 中使用:\n * ```ts\n * export function POST(ctx) {\n * return mcp.handleWebRequest(ctx.request);\n * }\n * ```\n */\nexport async function handleMcpRequest(\n request: Request,\n server: McpServer,\n options?: McpHttpOptions,\n): Promise<Response> {\n const originRejected = checkOrigin(request, options);\n if (originRejected) return originRejected;\n\n switch (request.method) {\n case 'POST':\n return handlePost(request, server);\n case 'GET':\n return handleGet(server, request);\n case 'DELETE':\n return handleDelete(request, server);\n default:\n return new Response(null, { status: 405 });\n }\n}\n\n/**\n * 处理 GET 请求:打开 SSE 流,定期推送心跳维持连接\n *\n * 若携带 Mcp-Session-Id 头,会在 stream start 时注册 SSE 订阅者,\n * 服务端 sendLogging 等推送方法可通过 broadcastToSession 推送到客户端。\n * 心跳 tick 同时续期 session(touch),只收推送不发请求的客户端\n * 不会因空闲 TTL 被过期清理导致连接被强制断开。\n *\n * 携带的 Mcp-Session-Id 无效或已过期时返回 404(MCP 规范),\n * 客户端可据此重新 initialize 而非静默空转。\n *\n * 客户端断开时 stream cancel 触发,清理定时器 + 注销订阅者避免泄漏。\n */\nfunction handleGet(server: McpServer, request: Request): Response {\n const heartbeatMs = server.getSseHeartbeatMs() ?? DEFAULT_SSE_HEARTBEAT_MS;\n const encoder = new TextEncoder();\n let interval: ReturnType<typeof setInterval> | undefined;\n let subscriber:\n | { controller: ReadableStreamDefaultController<Uint8Array>; sessionId: string }\n | undefined;\n\n const sessionId = request.headers.get('mcp-session-id') ?? undefined;\n\n // 携带 session id 但无效/已过期 → 404(不打开空转的流)\n if (sessionId && !server.getSessionManager().has(sessionId)) {\n return jsonResponse(\n 404,\n createErrorResponse(null, ErrorCode.RequestTimeout, 'Session not found'),\n );\n }\n\n const stream = new ReadableStream<Uint8Array>({\n start(controller) {\n // 初始连接确认\n controller.enqueue(encoder.encode(': connected\\n\\n'));\n\n // 若有 session id,注册订阅者(用于服务端推送)\n if (sessionId) {\n subscriber = server.getSessionManager().addSubscriber(sessionId, controller);\n }\n\n // 心跳定时器(同时续期 session,防止活跃长连接被空闲 TTL 清理)\n interval = setInterval(() => {\n try {\n if (sessionId) {\n server.getSessionManager().touch(sessionId);\n }\n controller.enqueue(encoder.encode(`: keepalive ${Date.now()}\\n\\n`));\n } catch {\n // controller 已关闭,清理定时器 + 注销订阅者\n if (interval) clearInterval(interval);\n if (subscriber) {\n server.getSessionManager().removeSubscriber(subscriber);\n }\n }\n }, heartbeatMs);\n },\n cancel() {\n // 客户端断开,清理定时器 + 注销订阅者避免泄漏\n if (interval) clearInterval(interval);\n if (subscriber) {\n server.getSessionManager().removeSubscriber(subscriber);\n }\n },\n });\n\n return new Response(stream, {\n status: 200,\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n },\n });\n}\n\nasync function handlePost(request: Request, server: McpServer): Promise<Response> {\n // 解析请求体\n let body: unknown;\n try {\n body = await request.json();\n } catch {\n return jsonResponse(\n 400,\n createErrorResponse(null, ErrorCode.ParseError, 'Parse error: Invalid JSON'),\n );\n }\n\n // 解析 JSON-RPC 消息。批量走 parseJsonRpcBatch:批内单条无效仅对该条生成\n // ParseError 响应(JSON-RPC 2.0 规范),不整批 400\n let messages: JsonRpcMessage[];\n let invalidResponses: JsonRpcMessage[] = [];\n if (Array.isArray(body)) {\n // 空批:JSON-RPC 2.0 规范要求返回单个 Invalid Request 响应(延续 400 语义)\n if (body.length === 0) {\n return jsonResponse(\n 400,\n createErrorResponse(null, ErrorCode.ParseError, 'Invalid Request: empty batch'),\n );\n }\n const batch = parseJsonRpcBatch(body);\n messages = batch.messages;\n invalidResponses = batch.invalid;\n } else {\n try {\n messages = parseJsonRpcMessage(body);\n } catch (err) {\n const message = err instanceof JsonRpcParseError ? err.message : 'Invalid JSON-RPC message';\n return jsonResponse(400, createErrorResponse(null, ErrorCode.ParseError, message));\n }\n }\n\n // 分离请求和通知\n const requests = messages.filter(isRequest);\n const notifications = messages.filter(isNotification);\n\n // initialize 请求豁免 Accept 头校验(首次握手)\n const hasInitialize = requests.some((r) => r.method === 'initialize');\n if (!hasInitialize) {\n // MCP 2025-06-18 规范:Accept 头必须同时包含 application/json 和 text/event-stream\n const acceptHeader = request.headers.get('accept') ?? '';\n const accepts = acceptHeader\n .split(',')\n .map((s) => s.trim().toLowerCase())\n .filter((s) => s.length > 0);\n const hasJson = accepts.some(\n (s) => s === 'application/json' || s.startsWith('application/json;'),\n );\n const hasSse = accepts.some(\n (s) => s === 'text/event-stream' || s.startsWith('text/event-stream;'),\n );\n if (!hasJson || !hasSse) {\n return jsonResponse(\n 400,\n createErrorResponse(\n null,\n ErrorCode.InvalidRequest,\n 'Accept header must include both application/json and text/event-stream',\n ),\n );\n }\n }\n\n // 查找 session(非 initialize 请求需要 session)\n const sessionId = request.headers.get('mcp-session-id') ?? undefined;\n const sessionManager = server.getSessionManager();\n let session = sessionId ? sessionManager.get(sessionId) : undefined;\n\n if (hasInitialize) {\n if (session) {\n // 已有 session 却再次 initialize\n return jsonResponse(\n 400,\n createErrorResponse(null, ErrorCode.InvalidRequest, 'Server already initialized'),\n );\n }\n // 先创建 session,handleJsonRpc 中会填充 protocolVersion 等信息\n session = sessionManager.create();\n } else if (requests.length > 0) {\n // 非 initialize 请求必须有有效 session(MCP 规范:防止匿名客户端跳过握手直接调 tool)\n if (!sessionId) {\n return jsonResponse(\n 400,\n createErrorResponse(null, ErrorCode.InvalidRequest, 'Missing Mcp-Session-Id header'),\n );\n }\n if (!session) {\n // 有 session ID 但找不到\n return jsonResponse(\n 404,\n createErrorResponse(null, ErrorCode.RequestTimeout, 'Session not found'),\n );\n }\n }\n\n // 处理通知(无响应)\n for (const notification of notifications) {\n await server.handleJsonRpc(notification, session);\n }\n\n // 处理请求——批量场景并行执行(单个请求的错误由 handleJsonRpc 内部转为\n // error response,不互相影响);响应按请求声明顺序回传(与完成顺序无关)\n const handled = await Promise.all(requests.map((req) => server.handleJsonRpc(req, session)));\n const responses: JsonRpcMessage[] = [\n ...invalidResponses, // 解析期产生的 error response(解析阶段语义,置于响应数组最前)\n ...handled.filter((r): r is JsonRpcMessage => r !== null),\n ];\n\n // 构建响应\n if (requests.length === 0 && invalidResponses.length === 0) {\n // 全是通知且无解析失败 → 202 Accepted\n return new Response(null, { status: 202 });\n }\n\n const responseHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n // initialize 响应需带 Mcp-Session-Id header\n if (hasInitialize && session) {\n responseHeaders['Mcp-Session-Id'] = session.id;\n }\n\n const responseBody = responses.length === 1 ? responses[0] : responses;\n return new Response(JSON.stringify(responseBody), {\n status: 200,\n headers: responseHeaders,\n });\n}\n\nfunction handleDelete(request: Request, server: McpServer): Promise<Response> {\n const sessionId = request.headers.get('mcp-session-id');\n if (!sessionId) {\n return Promise.resolve(\n jsonResponse(\n 400,\n createErrorResponse(null, ErrorCode.InvalidRequest, 'Missing Mcp-Session-Id header'),\n ),\n );\n }\n\n const deleted = server.getSessionManager().delete(sessionId);\n if (!deleted) {\n return Promise.resolve(\n jsonResponse(404, createErrorResponse(null, ErrorCode.RequestTimeout, 'Session not found')),\n );\n }\n\n return Promise.resolve(new Response(null, { status: 200 }));\n}\n\nfunction jsonResponse(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'Content-Type': 'application/json' },\n });\n}\n","/**\n * faapi 适配器:把 MCP Server 挂载到 faapi 路由\n *\n * 两种集成方式:\n *\n * 1. handler 风格(推荐)——在 handler.ts 中使用:\n * ```ts\n * // api/mcp/handler.ts\n * import { createMcpServer, createMcpHandler } from '@faapi/mcp';\n * const mcp = createMcpServer({ name: 'my-app', version: '1.0.0' });\n * mcp.tool('hello', { ... });\n * export const { POST, GET, DELETE } = createMcpHandler(mcp);\n * ```\n *\n * 2. 插件风格——在 faapi.config.ts 中声明:\n * ```ts\n * plugins: [['@faapi/mcp', { path: '/mcp' }]]\n * ```\n * 需配合 mcp.tool() 注册(通常在 lifecycle.onReady 中)。\n *\n * 函数即接口:MCP endpoint 就是 faapi 的一个路由。\n */\n\nimport { Readable } from 'node:stream';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { McpServer } from './mcpServer';\nimport { handleMcpRequest, type McpHttpOptions } from './streamableHttp';\n\n/**\n * 创建 faapi handler 函数\n *\n * 返回 { POST, GET, DELETE },可直接在 handler.ts 中导出。\n * faapi 按参数名注入 ctx,函数内通过 ctx.request 获取 Web Request。\n *\n * @param options 传输选项(如 `allowedOrigins`——MCP 规范建议校验 Origin 防 DNS\n * rebinding;把 /mcp 暴露到生产网络时建议配置)\n */\nexport function createMcpHandler(\n mcp: McpServer,\n options?: McpHttpOptions,\n): {\n POST: (ctx: { request: Request }) => Promise<Response>;\n GET: (ctx: { request: Request }) => Promise<Response>;\n DELETE: (ctx: { request: Request }) => Promise<Response>;\n} {\n const handler = async (ctx: { request: Request }): Promise<Response> => {\n return handleMcpRequest(ctx.request, mcp, options);\n };\n return { POST: handler, GET: handler, DELETE: handler };\n}\n\n/**\n * 创建 Node.js 请求处理函数(供 wrapHandler 使用)\n *\n * 用于 faapi 插件场景:插件通过 wrapHandler 拦截指定路径,\n * 将 Node.js IncomingMessage/ServerResponse 转为 Web Request 处理。\n *\n * 使用 Node.js 原生 `Readable.toWeb(req)` 转换 body,正确处理 chunked transfer\n * (多 chunk 累积)、backpressure 和 stream error。\n *\n * @param options 传输选项(同 createMcpHandler)\n */\nexport function createMcpNodeHandler(\n mcp: McpServer,\n options?: McpHttpOptions,\n): (req: IncomingMessage, res: ServerResponse) => Promise<void> {\n return async (req, res) => {\n const url = new URL(req.url ?? '/', 'http://localhost');\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (Array.isArray(value)) {\n for (const v of value) headers.set(key, v);\n } else if (value !== undefined) {\n headers.set(key, value);\n }\n }\n\n const method = req.method ?? 'GET';\n // GET/HEAD/DELETE 无 body(DELETE 可能有 body 但 MCP 协议中 DELETE 用于销毁会话,无 body)\n const hasBody = method !== 'GET' && method !== 'HEAD' && method !== 'DELETE';\n\n // 使用 Node.js 原生 Readable.toWeb 转换,正确处理多 chunk、backpressure、error\n const body = hasBody ? (Readable.toWeb(req) as ReadableStream<Uint8Array>) : undefined;\n\n const request = new Request(url, {\n method,\n headers,\n ...(body && { body, duplex: 'half' as const }),\n } as RequestInit);\n\n const response = await handleMcpRequest(request, mcp, options);\n\n res.statusCode = response.status;\n response.headers.forEach((value, key) => {\n res.setHeader(key, value);\n });\n\n if (response.body) {\n // 用 pipe 处理流式响应(SSE 流),避免 text() 阻塞\n // 注意:SSE 流不主动结束,监听 'close'(客户端断开或 res 销毁)而非仅 'finish'\n const nodeStream = Readable.fromWeb(response.body as never);\n await new Promise<void>((resolve, reject) => {\n let settled = false;\n const settle = (err?: Error) => {\n if (settled) return;\n settled = true;\n if (err) reject(err);\n else resolve();\n };\n // 源流错误:真实错误 → reject(由 wrapHandler 错误处理链接管)\n nodeStream.on('error', (err) => settle(err));\n // 客户端断开(ECONNRESET 等):销毁源流并按正常完成收尾。\n // destroy 使底层 web ReadableStream 触发 cancel()——清理 SSE 心跳定时器\n // 与订阅者;不销毁的话定时器持续 enqueue 到无消费者的流,定时器 + 队列泄漏\n res.on('error', () => {\n nodeStream.destroy();\n settle();\n });\n res.on('finish', () => settle());\n res.on('close', () => {\n // 正常完成也会触发 'close'(此时 writableEnded=true),不视为断连\n if (!res.writableEnded) nodeStream.destroy();\n settle();\n });\n nodeStream.pipe(res);\n });\n return;\n }\n\n res.end();\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faapi/mcp",
3
- "version": "3.3.0",
3
+ "version": "4.1.0",
4
4
  "description": "MCP Server SDK for faapi — Streamable HTTP transport, zod-native tool definition",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -19,18 +19,17 @@
19
19
  },
20
20
  "dependencies": {},
21
21
  "devDependencies": {
22
- "@types/node": "^22.15.0",
23
- "@vitest/coverage-v8": "3",
22
+ "@types/node": "^24.3.0",
23
+ "@vitest/coverage-v8": "4",
24
24
  "tsup": "^8.4.0",
25
25
  "typescript": "^5.7.0",
26
- "vitest": "^3.1.0",
26
+ "vitest": "^4.1.11",
27
27
  "zod": "^4.4.3",
28
- "@faapi/faapi": "3.3.0"
28
+ "@faapi/faapi": "4.1.0"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "typescript": ">=5.7.0",
32
- "zod": "^4.4.3",
33
- "@faapi/faapi": "^3.3.0"
32
+ "zod": "^4.4.3"
34
33
  },
35
34
  "peerDependenciesMeta": {
36
35
  "typescript": {
@@ -50,6 +49,7 @@
50
49
  "scripts": {
51
50
  "build": "tsup",
52
51
  "test": "vitest run --passWithNoTests",
53
- "typecheck": "tsc --noEmit"
52
+ "typecheck": "tsc --noEmit",
53
+ "typecheck:test": "tsc --noEmit"
54
54
  }
55
55
  }