@faapi/mcp 0.0.0-canary.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.js ADDED
@@ -0,0 +1,1414 @@
1
+ // src/mcpServer.ts
2
+ import { z, toJSONSchema } from "zod";
3
+
4
+ // src/jsonRpc.ts
5
+ var ErrorCode = {
6
+ // JSON-RPC 标准错误码
7
+ ParseError: -32700,
8
+ InvalidRequest: -32600,
9
+ MethodNotFound: -32601,
10
+ InvalidParams: -32602,
11
+ InternalError: -32603,
12
+ // MCP 扩展错误码
13
+ ConnectionClosed: -32e3,
14
+ RequestTimeout: -32001
15
+ };
16
+ function isRequest(msg) {
17
+ return "method" in msg && "id" in msg && !("result" in msg) && !("error" in msg);
18
+ }
19
+ function isNotification(msg) {
20
+ return "method" in msg && !("id" in msg);
21
+ }
22
+ function isResultResponse(msg) {
23
+ return "result" in msg && "id" in msg;
24
+ }
25
+ function isErrorResponse(msg) {
26
+ return "error" in msg && "id" in msg;
27
+ }
28
+ function createResultResponse(id, result) {
29
+ return { jsonrpc: "2.0", id, result };
30
+ }
31
+ function createErrorResponse(id, code, message, data) {
32
+ const error = { code, message };
33
+ if (data !== void 0) error.data = data;
34
+ return { jsonrpc: "2.0", id, error };
35
+ }
36
+ function parseJsonRpcMessage(data) {
37
+ if (Array.isArray(data)) {
38
+ if (data.length === 0) {
39
+ throw new JsonRpcParseError("Invalid Request: empty batch");
40
+ }
41
+ return data.map((item) => parseSingleMessage(item));
42
+ }
43
+ return [parseSingleMessage(data)];
44
+ }
45
+ var JsonRpcParseError = class extends Error {
46
+ constructor(message) {
47
+ super(message);
48
+ this.name = "JsonRpcParseError";
49
+ }
50
+ };
51
+ function parseSingleMessage(data) {
52
+ if (typeof data !== "object" || data === null) {
53
+ throw new JsonRpcParseError("Invalid Request: not an object");
54
+ }
55
+ const obj = data;
56
+ if (obj.jsonrpc !== "2.0") {
57
+ throw new JsonRpcParseError('Invalid Request: jsonrpc must be "2.0"');
58
+ }
59
+ if (typeof obj.method === "string" && (typeof obj.id === "string" || typeof obj.id === "number")) {
60
+ return {
61
+ jsonrpc: "2.0",
62
+ id: obj.id,
63
+ method: obj.method,
64
+ ...obj.params !== void 0 && { params: obj.params }
65
+ };
66
+ }
67
+ if (typeof obj.method === "string" && obj.id === void 0) {
68
+ return {
69
+ jsonrpc: "2.0",
70
+ method: obj.method,
71
+ ...obj.params !== void 0 && { params: obj.params }
72
+ };
73
+ }
74
+ if ("result" in obj && (typeof obj.id === "string" || typeof obj.id === "number")) {
75
+ return {
76
+ jsonrpc: "2.0",
77
+ id: obj.id,
78
+ result: obj.result
79
+ };
80
+ }
81
+ if ("error" in obj && obj.error !== null && typeof obj.error === "object") {
82
+ return {
83
+ jsonrpc: "2.0",
84
+ id: obj.id ?? null,
85
+ error: obj.error
86
+ };
87
+ }
88
+ throw new JsonRpcParseError("Invalid Request: unknown message shape");
89
+ }
90
+
91
+ // src/session.ts
92
+ import { randomUUID } from "crypto";
93
+ var LOGGING_LEVEL_ORDER = {
94
+ debug: 10,
95
+ info: 20,
96
+ notice: 30,
97
+ warning: 40,
98
+ error: 50,
99
+ critical: 60,
100
+ alert: 70,
101
+ emergency: 80
102
+ };
103
+ var DEFAULT_TTL = 30 * 60 * 1e3;
104
+ var SessionManager = class {
105
+ sessions = /* @__PURE__ */ new Map();
106
+ ttl;
107
+ constructor(ttl = DEFAULT_TTL) {
108
+ this.ttl = ttl;
109
+ }
110
+ /** 是否启用 TTL 过期检查(ttl > 0) */
111
+ get ttlEnabled() {
112
+ return this.ttl > 0;
113
+ }
114
+ /** 创建新会话,返回 session 对象 */
115
+ create() {
116
+ if (this.ttlEnabled) this.cleanupExpired();
117
+ const now = Date.now();
118
+ const session = {
119
+ id: randomUUID(),
120
+ initialized: false,
121
+ protocolVersion: "",
122
+ createdAt: now,
123
+ lastActivity: now,
124
+ loggingLevel: "info",
125
+ subscribers: /* @__PURE__ */ new Set(),
126
+ subscribedResources: /* @__PURE__ */ new Set()
127
+ };
128
+ this.sessions.set(session.id, session);
129
+ return session;
130
+ }
131
+ /** 按 ID 获取会话(更新最后活动时间,过期返回 undefined) */
132
+ get(id) {
133
+ const session = this.sessions.get(id);
134
+ if (!session) return void 0;
135
+ if (this.ttlEnabled && Date.now() - session.lastActivity > this.ttl) {
136
+ this.closeSubscribers(session);
137
+ this.sessions.delete(id);
138
+ return void 0;
139
+ }
140
+ session.lastActivity = Date.now();
141
+ return session;
142
+ }
143
+ /** 是否存在(不更新活动时间,过期会话返回 false) */
144
+ has(id) {
145
+ const session = this.sessions.get(id);
146
+ if (!session) return false;
147
+ if (this.ttlEnabled && Date.now() - session.lastActivity > this.ttl) {
148
+ this.closeSubscribers(session);
149
+ this.sessions.delete(id);
150
+ return false;
151
+ }
152
+ return true;
153
+ }
154
+ /** 销毁会话(关闭所有 SSE 订阅者) */
155
+ delete(id) {
156
+ const session = this.sessions.get(id);
157
+ if (session) {
158
+ this.closeSubscribers(session);
159
+ }
160
+ return this.sessions.delete(id);
161
+ }
162
+ /** 当前会话数(含可能尚未清理的过期会话) */
163
+ get size() {
164
+ return this.sessions.size;
165
+ }
166
+ /** 获取所有 session ID 列表(用于全局广播通知) */
167
+ allSessionIds() {
168
+ return [...this.sessions.keys()];
169
+ }
170
+ /** 清空所有会话 */
171
+ clear() {
172
+ for (const session of this.sessions.values()) {
173
+ this.closeSubscribers(session);
174
+ }
175
+ this.sessions.clear();
176
+ }
177
+ /** 注册 SSE 订阅者到 session */
178
+ addSubscriber(sessionId, controller) {
179
+ const session = this.get(sessionId);
180
+ if (!session) return void 0;
181
+ const subscriber = { controller, sessionId };
182
+ session.subscribers.add(subscriber);
183
+ return subscriber;
184
+ }
185
+ /** 注销 SSE 订阅者 */
186
+ removeSubscriber(subscriber) {
187
+ const session = this.sessions.get(subscriber.sessionId);
188
+ if (session) {
189
+ session.subscribers.delete(subscriber);
190
+ }
191
+ }
192
+ /** 向 session 的所有订阅者推送 SSE 数据 */
193
+ broadcastToSession(sessionId, data) {
194
+ const session = this.sessions.get(sessionId);
195
+ if (!session) return;
196
+ const encoder = new TextEncoder();
197
+ for (const sub of session.subscribers) {
198
+ try {
199
+ sub.controller.enqueue(encoder.encode(data));
200
+ } catch {
201
+ session.subscribers.delete(sub);
202
+ }
203
+ }
204
+ }
205
+ /** 判断指定级别的日志是否应该推送(>= session.loggingLevel) */
206
+ shouldLog(sessionId, level) {
207
+ const session = this.sessions.get(sessionId);
208
+ if (!session) return false;
209
+ return LOGGING_LEVEL_ORDER[level] >= LOGGING_LEVEL_ORDER[session.loggingLevel];
210
+ }
211
+ /** 添加资源订阅(将 uri 加入 session.subscribedResources) */
212
+ subscribeResource(sessionId, uri) {
213
+ const session = this.sessions.get(sessionId);
214
+ if (!session) return false;
215
+ session.subscribedResources.add(uri);
216
+ return true;
217
+ }
218
+ /** 取消资源订阅(从 session.subscribedResources 移除 uri) */
219
+ unsubscribeResource(sessionId, uri) {
220
+ const session = this.sessions.get(sessionId);
221
+ if (!session) return false;
222
+ session.subscribedResources.delete(uri);
223
+ return true;
224
+ }
225
+ /** 找出所有订阅了指定 URI 的 session id 列表 */
226
+ findSubscribersOfUri(uri) {
227
+ const result = [];
228
+ for (const session of this.sessions.values()) {
229
+ if (session.subscribedResources.has(uri)) {
230
+ result.push(session.id);
231
+ }
232
+ }
233
+ return result;
234
+ }
235
+ /** 关闭 session 的所有订阅者(用于 session 销毁/过期) */
236
+ closeSubscribers(session) {
237
+ for (const sub of session.subscribers) {
238
+ try {
239
+ sub.controller.close();
240
+ } catch {
241
+ }
242
+ }
243
+ session.subscribers.clear();
244
+ }
245
+ /** 清理所有过期会话 */
246
+ cleanupExpired() {
247
+ if (!this.ttlEnabled) return;
248
+ const now = Date.now();
249
+ for (const [id, session] of this.sessions) {
250
+ if (now - session.lastActivity > this.ttl) {
251
+ this.closeSubscribers(session);
252
+ this.sessions.delete(id);
253
+ }
254
+ }
255
+ }
256
+ };
257
+
258
+ // src/mcpServer.ts
259
+ var PROTOCOL_VERSION = "2025-06-18";
260
+ var SUPPORTED_PROTOCOL_VERSIONS = ["2025-06-18", "2025-03-26"];
261
+ var BUILTIN_METHODS = /* @__PURE__ */ new Set([
262
+ "initialize",
263
+ "ping",
264
+ "notifications/initialized",
265
+ "notifications/cancelled",
266
+ "tools/list",
267
+ "tools/call",
268
+ "resources/list",
269
+ "resources/read",
270
+ "resources/templates/list",
271
+ "resources/subscribe",
272
+ "resources/unsubscribe",
273
+ "prompts/list",
274
+ "prompts/get",
275
+ "logging/setLevel",
276
+ "completion/complete"
277
+ ]);
278
+ var DEFAULT_PAGE_SIZE = 100;
279
+ function encodeCursor(offset) {
280
+ return Buffer.from(String(offset)).toString("base64");
281
+ }
282
+ function decodeCursor(cursor) {
283
+ const decoded = Buffer.from(cursor, "base64").toString("utf-8");
284
+ const offset = Number.parseInt(decoded, 10);
285
+ if (!Number.isFinite(offset) || offset < 0 || decoded !== String(offset)) {
286
+ throw new Error(`Invalid cursor: ${cursor}`);
287
+ }
288
+ return offset;
289
+ }
290
+ function paginate(items, cursor, pageSize) {
291
+ const offset = cursor ? decodeCursor(cursor) : 0;
292
+ if (offset > items.length) {
293
+ throw new Error(`cursor out of range: offset ${offset} > total ${items.length}`);
294
+ }
295
+ const slice = items.slice(offset, offset + pageSize);
296
+ const nextOffset = offset + pageSize;
297
+ const nextCursor = nextOffset < items.length ? encodeCursor(nextOffset) : void 0;
298
+ return { items: slice, nextCursor };
299
+ }
300
+ var VAR_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
301
+ var PLACEHOLDER_RE = /\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
302
+ function compileUriTemplate(uriTemplate) {
303
+ const paramNames = [];
304
+ let match;
305
+ PLACEHOLDER_RE.lastIndex = 0;
306
+ while ((match = PLACEHOLDER_RE.exec(uriTemplate)) !== null) {
307
+ const name = match[1];
308
+ if (!VAR_NAME_RE.test(name)) {
309
+ throw new Error(`Invalid URI template variable name: ${name}`);
310
+ }
311
+ paramNames.push(name);
312
+ }
313
+ if (paramNames.length === 0) {
314
+ throw new Error(`URI template must contain at least one {var} placeholder: ${uriTemplate}`);
315
+ }
316
+ const seen = /* @__PURE__ */ new Set();
317
+ for (const name of paramNames) {
318
+ if (seen.has(name)) {
319
+ throw new Error(`Duplicate URI template variable: ${name}`);
320
+ }
321
+ seen.add(name);
322
+ }
323
+ const isSingle = paramNames.length === 1;
324
+ const parts = [];
325
+ let lastIdx = 0;
326
+ PLACEHOLDER_RE.lastIndex = 0;
327
+ let m;
328
+ while ((m = PLACEHOLDER_RE.exec(uriTemplate)) !== null) {
329
+ const literal = uriTemplate.slice(lastIdx, m.index);
330
+ parts.push(escapeRegex(literal));
331
+ parts.push(isSingle ? "(.+)" : "([^/]+)");
332
+ lastIdx = m.index + m[0].length;
333
+ }
334
+ parts.push(escapeRegex(uriTemplate.slice(lastIdx)));
335
+ const regex = new RegExp(`^${parts.join("")}$`);
336
+ return { regex, paramNames };
337
+ }
338
+ function escapeRegex(s) {
339
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
340
+ }
341
+ function matchUriTemplate(regex, paramNames, uri) {
342
+ const m = regex.exec(uri);
343
+ if (!m) return null;
344
+ const params = {};
345
+ for (let i = 0; i < paramNames.length; i++) {
346
+ params[paramNames[i]] = m[i + 1];
347
+ }
348
+ return params;
349
+ }
350
+ var McpServer = class {
351
+ constructor(options) {
352
+ this.options = options;
353
+ this.sessions = new SessionManager(this.options.sessionTtl);
354
+ this.pageSize = this.options.defaultPageSize ?? DEFAULT_PAGE_SIZE;
355
+ this.toolsListChanged = this.options.toolsListChanged ?? false;
356
+ this.resourcesListChanged = this.options.resourcesListChanged ?? false;
357
+ this.promptsListChanged = this.options.promptsListChanged ?? false;
358
+ }
359
+ options;
360
+ tools = /* @__PURE__ */ new Map();
361
+ resources = /* @__PURE__ */ new Map();
362
+ resourceTemplates = /* @__PURE__ */ new Map();
363
+ prompts = /* @__PURE__ */ new Map();
364
+ completions = /* @__PURE__ */ new Map();
365
+ methods = /* @__PURE__ */ new Map();
366
+ sessions;
367
+ pageSize;
368
+ toolsListChanged;
369
+ resourcesListChanged;
370
+ promptsListChanged;
371
+ /** 注册 tool */
372
+ tool(name, definition) {
373
+ if (this.tools.has(name)) {
374
+ throw new Error(`Tool "${name}" is already registered`);
375
+ }
376
+ let inputSchema;
377
+ let jsonSchema;
378
+ if (definition.input) {
379
+ inputSchema = buildZodObject(definition.input);
380
+ jsonSchema = toJSONSchema(inputSchema, { target: "draft-7" });
381
+ delete jsonSchema.$schema;
382
+ delete jsonSchema.additionalProperties;
383
+ }
384
+ this.tools.set(name, { name, definition, inputSchema, jsonSchema });
385
+ }
386
+ /** 注册 resource */
387
+ resource(uri, definition) {
388
+ if (this.resources.has(uri)) {
389
+ throw new Error(`Resource "${uri}" is already registered`);
390
+ }
391
+ this.resources.set(uri, { uri, definition });
392
+ }
393
+ /** 注册 resource template(RFC 6570 URI 模板) */
394
+ resourceTemplate(uriTemplate, definition) {
395
+ if (this.resourceTemplates.has(uriTemplate)) {
396
+ throw new Error(`Resource template "${uriTemplate}" is already registered`);
397
+ }
398
+ const { regex, paramNames } = compileUriTemplate(uriTemplate);
399
+ this.resourceTemplates.set(uriTemplate, {
400
+ uriTemplate,
401
+ regex,
402
+ paramNames,
403
+ definition
404
+ });
405
+ }
406
+ /** 注册 prompt */
407
+ prompt(name, definition) {
408
+ if (this.prompts.has(name)) {
409
+ throw new Error(`Prompt "${name}" is already registered`);
410
+ }
411
+ this.prompts.set(name, { name, definition });
412
+ }
413
+ /**
414
+ * 注册参数补全 handler
415
+ *
416
+ * - `ref`:`{ type: 'ref/prompt', name }` 或 `{ type: 'ref/resource', uri }`(对资源模板,uri 是模板字符串)
417
+ * - `argumentName`:补全的参数名(对应 prompt arguments 中的 name,或 resource template URI 模板中的变量名)
418
+ * - 同一 (ref, argumentName) 重复注册抛错
419
+ */
420
+ completion(ref, argumentName, handler) {
421
+ const refType = ref.type;
422
+ const refId = refType === "ref/prompt" ? ref.name : ref.uri;
423
+ const key = completionKey(refType, refId, argumentName);
424
+ if (this.completions.has(key)) {
425
+ throw new Error(
426
+ `Completion for ${refType} "${refId}" argument "${argumentName}" is already registered`
427
+ );
428
+ }
429
+ this.completions.set(key, { refType, refId, argumentName, handler });
430
+ }
431
+ /**
432
+ * 注册自定义 JSON-RPC 方法 handler(业务拓展)
433
+ *
434
+ * - 方法名建议使用 `appName/action` 格式,避免与 MCP 标准方法冲突
435
+ * - 与 MCP 内置方法(initialize/ping/tools/* 等)冲突时抛错
436
+ * - 重复注册同名方法抛错
437
+ *
438
+ * handler 返回值规则:
439
+ * - 返回普通对象:作为 JSON-RPC result 字段
440
+ * - 返回 JsonRpcErrorResponse(含 error 字段):作为错误响应
441
+ */
442
+ method(name, handler) {
443
+ if (BUILTIN_METHODS.has(name)) {
444
+ throw new Error(`Cannot register built-in method "${name}": it is reserved by MCP protocol`);
445
+ }
446
+ if (this.methods.has(name)) {
447
+ throw new Error(`Method "${name}" is already registered`);
448
+ }
449
+ this.methods.set(name, { name, handler });
450
+ }
451
+ /** 获取已注册 tool 名称列表 */
452
+ listTools() {
453
+ return [...this.tools.keys()];
454
+ }
455
+ /** 获取已注册 resource URI 列表 */
456
+ listResources() {
457
+ return [...this.resources.keys()];
458
+ }
459
+ /** 获取已注册 prompt 名称列表 */
460
+ listPrompts() {
461
+ return [...this.prompts.keys()];
462
+ }
463
+ /** 获取已注册自定义方法名列表(业务拓展) */
464
+ listMethods() {
465
+ return [...this.methods.keys()];
466
+ }
467
+ // ─── remove 方法(运行时删除注册项) ───────────────────
468
+ /**
469
+ * 删除已注册 tool
470
+ *
471
+ * - `toolsListChanged: true` 时自动推送 `notifications/tools/list_changed`
472
+ * - `toolsListChanged: false` 时静默删除(用于 dev 热替换,客户端不感知)
473
+ */
474
+ removeTool(name) {
475
+ const deleted = this.tools.delete(name);
476
+ if (deleted && this.toolsListChanged) {
477
+ this.notifyToolsListChanged();
478
+ }
479
+ return deleted;
480
+ }
481
+ /**
482
+ * 删除已注册 resource
483
+ *
484
+ * - `resourcesListChanged: true` 时自动推送 `notifications/resources/list_changed`
485
+ */
486
+ removeResource(uri) {
487
+ const deleted = this.resources.delete(uri);
488
+ if (deleted && this.resourcesListChanged) {
489
+ this.notifyResourcesListChanged();
490
+ }
491
+ return deleted;
492
+ }
493
+ /**
494
+ * 删除已注册 resource template
495
+ *
496
+ * - `resourcesListChanged: true` 时自动推送 `notifications/resources/list_changed`
497
+ */
498
+ removeResourceTemplate(uriTemplate) {
499
+ const deleted = this.resourceTemplates.delete(uriTemplate);
500
+ if (deleted && this.resourcesListChanged) {
501
+ this.notifyResourcesListChanged();
502
+ }
503
+ return deleted;
504
+ }
505
+ /**
506
+ * 删除已注册 prompt
507
+ *
508
+ * - `promptsListChanged: true` 时自动推送 `notifications/prompts/list_changed`
509
+ */
510
+ removePrompt(name) {
511
+ const deleted = this.prompts.delete(name);
512
+ if (deleted && this.promptsListChanged) {
513
+ this.notifyPromptsListChanged();
514
+ }
515
+ return deleted;
516
+ }
517
+ /**
518
+ * 删除已注册 completion handler
519
+ *
520
+ * - completion 无 list_changed 通知机制(客户端按需请求,无需感知列表变化)
521
+ */
522
+ removeCompletion(ref, argumentName) {
523
+ const refType = ref.type;
524
+ const refId = refType === "ref/prompt" ? ref.name : ref.uri;
525
+ const key = completionKey(refType, refId, argumentName);
526
+ return this.completions.delete(key);
527
+ }
528
+ /** 删除已注册自定义方法 */
529
+ removeMethod(name) {
530
+ return this.methods.delete(name);
531
+ }
532
+ // ─── list_changed 通知 ───────────────────────────────
533
+ /**
534
+ * 推送 `notifications/tools/list_changed` 到所有 session 的 SSE 订阅者
535
+ *
536
+ * 客户端收到后应重新调用 `tools/list` 拉取最新列表。
537
+ * `removeTool` 在 `toolsListChanged: true` 时自动调用本方法,
538
+ * 业务方也可手动调用(如批量删除后只推送一次)。
539
+ */
540
+ notifyToolsListChanged() {
541
+ this.broadcastNotificationToAllSessions("notifications/tools/list_changed");
542
+ }
543
+ /** 推送 `notifications/resources/list_changed` 到所有 session 的 SSE 订阅者 */
544
+ notifyResourcesListChanged() {
545
+ this.broadcastNotificationToAllSessions("notifications/resources/list_changed");
546
+ }
547
+ /** 推送 `notifications/prompts/list_changed` 到所有 session 的 SSE 订阅者 */
548
+ notifyPromptsListChanged() {
549
+ this.broadcastNotificationToAllSessions("notifications/prompts/list_changed");
550
+ }
551
+ /**
552
+ * 向所有 session 的所有 SSE 订阅者广播通知(内部工具方法)
553
+ *
554
+ * 用于 list_changed 这种"全局广播"语义——所有 session 都应感知列表变化。
555
+ */
556
+ broadcastNotificationToAllSessions(method, params) {
557
+ const notification = {
558
+ jsonrpc: "2.0",
559
+ method
560
+ };
561
+ if (params !== void 0) {
562
+ notification.params = params;
563
+ }
564
+ const sseData = `data: ${JSON.stringify(notification)}
565
+
566
+ `;
567
+ for (const sessionId of this.sessions.allSessionIds()) {
568
+ this.sessions.broadcastToSession(sessionId, sseData);
569
+ }
570
+ }
571
+ /** 获取会话管理器 */
572
+ getSessionManager() {
573
+ return this.sessions;
574
+ }
575
+ /** 获取 GET SSE 流心跳间隔(毫秒) */
576
+ getSseHeartbeatMs() {
577
+ return this.options.sseHeartbeatMs ?? 3e4;
578
+ }
579
+ /**
580
+ * 处理 JSON-RPC 请求,返回响应消息
581
+ *
582
+ * 通知(无 id)不返回响应,返回 null。
583
+ */
584
+ async handleJsonRpc(message, session) {
585
+ if (isNotification(message)) {
586
+ return this.handleNotification(message, session);
587
+ }
588
+ if ("id" in message && "method" in message && !("result" in message) && !("error" in message)) {
589
+ return this.handleRequest(message, session);
590
+ }
591
+ return null;
592
+ }
593
+ handleNotification(message, session) {
594
+ switch (message.method) {
595
+ case "notifications/initialized":
596
+ if (session) session.initialized = true;
597
+ break;
598
+ case "notifications/cancelled":
599
+ break;
600
+ }
601
+ return null;
602
+ }
603
+ async handleRequest(request, session) {
604
+ try {
605
+ switch (request.method) {
606
+ case "initialize":
607
+ return this.handleInitialize(request, session);
608
+ case "ping":
609
+ return createResultResponse(request.id, {});
610
+ case "tools/list":
611
+ return this.handleToolsList(request);
612
+ case "tools/call":
613
+ return await this.handleToolsCall(request, session);
614
+ case "resources/list":
615
+ return this.handleResourcesList(request);
616
+ case "resources/read":
617
+ return await this.handleResourcesRead(request, session);
618
+ case "prompts/list":
619
+ return this.handlePromptsList(request);
620
+ case "prompts/get":
621
+ return await this.handlePromptsGet(request, session);
622
+ case "logging/setLevel":
623
+ return this.handleLoggingSetLevel(request, session);
624
+ case "resources/subscribe":
625
+ return this.handleResourcesSubscribe(request, session);
626
+ case "resources/unsubscribe":
627
+ return this.handleResourcesUnsubscribe(request, session);
628
+ case "resources/templates/list":
629
+ return this.handleResourcesTemplatesList(request);
630
+ case "completion/complete":
631
+ return await this.handleCompletionComplete(request);
632
+ default:
633
+ if (this.methods.has(request.method)) {
634
+ return await this.handleCustomMethod(request, session);
635
+ }
636
+ return createErrorResponse(
637
+ request.id,
638
+ ErrorCode.MethodNotFound,
639
+ `Method not found: ${request.method}`
640
+ );
641
+ }
642
+ } catch (err) {
643
+ return createErrorResponse(
644
+ request.id,
645
+ ErrorCode.InternalError,
646
+ err instanceof Error ? err.message : String(err)
647
+ );
648
+ }
649
+ }
650
+ // ─── initialize ──────────────────────────────────────
651
+ handleInitialize(request, session) {
652
+ const params = request.params ?? {};
653
+ const requestedVersion = params.protocolVersion ?? PROTOCOL_VERSION;
654
+ const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.includes(
655
+ requestedVersion
656
+ ) ? requestedVersion : PROTOCOL_VERSION;
657
+ if (!session) {
658
+ session = this.sessions.create();
659
+ }
660
+ session.protocolVersion = protocolVersion;
661
+ session.clientInfo = params.clientInfo;
662
+ const capabilities = {
663
+ tools: { listChanged: this.toolsListChanged },
664
+ // logging capability 始终声明——服务端内置日志推送能力
665
+ logging: {}
666
+ };
667
+ if (this.resources.size > 0 || this.resourceTemplates.size > 0) {
668
+ capabilities.resources = { listChanged: this.resourcesListChanged, subscribe: true };
669
+ }
670
+ if (this.prompts.size > 0) {
671
+ capabilities.prompts = { listChanged: this.promptsListChanged };
672
+ }
673
+ const result = {
674
+ protocolVersion,
675
+ capabilities,
676
+ serverInfo: {
677
+ name: this.options.name,
678
+ version: this.options.version,
679
+ ...this.options.title && { title: this.options.title }
680
+ },
681
+ ...this.options.instructions && { instructions: this.options.instructions }
682
+ };
683
+ return createResultResponse(request.id, result);
684
+ }
685
+ // ─── tools/list ──────────────────────────────────────
686
+ handleToolsList(request) {
687
+ const params = request.params ?? {};
688
+ const allTools = Array.from(this.tools.values()).map((tool) => ({
689
+ name: tool.name,
690
+ ...tool.definition.description && { description: tool.definition.description },
691
+ inputSchema: tool.jsonSchema ?? { type: "object", properties: {} },
692
+ ...tool.definition.annotations && { annotations: tool.definition.annotations }
693
+ }));
694
+ let paged;
695
+ try {
696
+ paged = paginate(allTools, params.cursor, this.pageSize);
697
+ } catch (err) {
698
+ return createErrorResponse(
699
+ request.id,
700
+ ErrorCode.InvalidParams,
701
+ err instanceof Error ? err.message : "Invalid cursor"
702
+ );
703
+ }
704
+ return createResultResponse(request.id, {
705
+ tools: paged.items,
706
+ ...paged.nextCursor && { nextCursor: paged.nextCursor }
707
+ });
708
+ }
709
+ // ─── tools/call ──────────────────────────────────────
710
+ async handleToolsCall(request, session) {
711
+ const params = request.params ?? {};
712
+ if (!params.name) {
713
+ return createErrorResponse(
714
+ request.id,
715
+ ErrorCode.InvalidParams,
716
+ "Missing required parameter: name"
717
+ );
718
+ }
719
+ const tool = this.tools.get(params.name);
720
+ if (!tool) {
721
+ return createErrorResponse(
722
+ request.id,
723
+ ErrorCode.InvalidParams,
724
+ `Unknown tool: ${params.name}`
725
+ );
726
+ }
727
+ const args = params.arguments ?? {};
728
+ if (tool.inputSchema) {
729
+ const parsed = tool.inputSchema.safeParse(args);
730
+ if (!parsed.success) {
731
+ return createErrorResponse(
732
+ request.id,
733
+ ErrorCode.InvalidParams,
734
+ "Invalid tool arguments",
735
+ parsed.error.issues
736
+ );
737
+ }
738
+ Object.assign(args, parsed.data);
739
+ }
740
+ const progressToken = params._meta?.progressToken;
741
+ const extra = {
742
+ sessionId: session?.id ?? "",
743
+ sendLogging: (level, data, logger) => {
744
+ if (session) this.sendLogging(session.id, level, data, logger);
745
+ },
746
+ sendProgress: (progress, total) => {
747
+ if (session) this.sendProgress(session.id, progressToken, progress, total);
748
+ }
749
+ };
750
+ let result;
751
+ try {
752
+ result = await tool.definition.handler(args, extra);
753
+ } catch (err) {
754
+ result = {
755
+ content: [
756
+ {
757
+ type: "text",
758
+ text: `Tool execution failed: ${err instanceof Error ? err.message : String(err)}`
759
+ }
760
+ ],
761
+ isError: true
762
+ };
763
+ }
764
+ return createResultResponse(request.id, result);
765
+ }
766
+ // ─── resources/list ─────────────────────────────────
767
+ handleResourcesList(request) {
768
+ const params = request.params ?? {};
769
+ const allResources = Array.from(this.resources.values()).map((r) => ({
770
+ uri: r.uri,
771
+ name: r.definition.name,
772
+ ...r.definition.description && { description: r.definition.description },
773
+ ...r.definition.mimeType && { mimeType: r.definition.mimeType }
774
+ }));
775
+ let paged;
776
+ try {
777
+ paged = paginate(allResources, params.cursor, this.pageSize);
778
+ } catch (err) {
779
+ return createErrorResponse(
780
+ request.id,
781
+ ErrorCode.InvalidParams,
782
+ err instanceof Error ? err.message : "Invalid cursor"
783
+ );
784
+ }
785
+ return createResultResponse(request.id, {
786
+ resources: paged.items,
787
+ ...paged.nextCursor && { nextCursor: paged.nextCursor }
788
+ });
789
+ }
790
+ // ─── resources/read ─────────────────────────────────
791
+ async handleResourcesRead(request, session) {
792
+ const params = request.params ?? {};
793
+ if (!params.uri) {
794
+ return createErrorResponse(
795
+ request.id,
796
+ ErrorCode.InvalidParams,
797
+ "Missing required parameter: uri"
798
+ );
799
+ }
800
+ const progressToken = params._meta?.progressToken;
801
+ const sendProgressFn = (progress, total) => {
802
+ if (session) this.sendProgress(session.id, progressToken, progress, total);
803
+ };
804
+ const extra = {
805
+ sessionId: session?.id ?? "",
806
+ sendLogging: (level, data, logger) => {
807
+ if (session) this.sendLogging(session.id, level, data, logger);
808
+ },
809
+ sendProgress: sendProgressFn
810
+ };
811
+ const resource = this.resources.get(params.uri);
812
+ if (resource) {
813
+ const result = await resource.definition.read(params.uri, extra);
814
+ return createResultResponse(request.id, result);
815
+ }
816
+ for (const tpl of this.resourceTemplates.values()) {
817
+ const params2 = matchUriTemplate(tpl.regex, tpl.paramNames, params.uri);
818
+ if (params2) {
819
+ const tplExtra = {
820
+ sessionId: session?.id ?? "",
821
+ sendLogging: (level, data, logger) => {
822
+ if (session) this.sendLogging(session.id, level, data, logger);
823
+ },
824
+ sendProgress: sendProgressFn
825
+ };
826
+ const result = await tpl.definition.read(params.uri, params2, tplExtra);
827
+ return createResultResponse(request.id, result);
828
+ }
829
+ }
830
+ return createErrorResponse(
831
+ request.id,
832
+ ErrorCode.InvalidParams,
833
+ `Unknown resource: ${params.uri}`
834
+ );
835
+ }
836
+ // ─── resources/templates/list ──────────────────────
837
+ handleResourcesTemplatesList(request) {
838
+ const params = request.params ?? {};
839
+ const allTemplates = Array.from(this.resourceTemplates.values()).map((t) => ({
840
+ uriTemplate: t.uriTemplate,
841
+ name: t.definition.name,
842
+ ...t.definition.description && { description: t.definition.description },
843
+ ...t.definition.mimeType && { mimeType: t.definition.mimeType }
844
+ }));
845
+ let paged;
846
+ try {
847
+ paged = paginate(allTemplates, params.cursor, this.pageSize);
848
+ } catch (err) {
849
+ return createErrorResponse(
850
+ request.id,
851
+ ErrorCode.InvalidParams,
852
+ err instanceof Error ? err.message : "Invalid cursor"
853
+ );
854
+ }
855
+ return createResultResponse(request.id, {
856
+ resourceTemplates: paged.items,
857
+ ...paged.nextCursor && { nextCursor: paged.nextCursor }
858
+ });
859
+ }
860
+ // ─── prompts/list ───────────────────────────────────
861
+ handlePromptsList(request) {
862
+ const params = request.params ?? {};
863
+ const allPrompts = Array.from(this.prompts.values()).map((p) => ({
864
+ name: p.name,
865
+ ...p.definition.description && { description: p.definition.description },
866
+ ...p.definition.arguments && { arguments: p.definition.arguments }
867
+ }));
868
+ let paged;
869
+ try {
870
+ paged = paginate(allPrompts, params.cursor, this.pageSize);
871
+ } catch (err) {
872
+ return createErrorResponse(
873
+ request.id,
874
+ ErrorCode.InvalidParams,
875
+ err instanceof Error ? err.message : "Invalid cursor"
876
+ );
877
+ }
878
+ return createResultResponse(request.id, {
879
+ prompts: paged.items,
880
+ ...paged.nextCursor && { nextCursor: paged.nextCursor }
881
+ });
882
+ }
883
+ // ─── prompts/get ────────────────────────────────────
884
+ async handlePromptsGet(request, session) {
885
+ const params = request.params ?? {};
886
+ if (!params.name) {
887
+ return createErrorResponse(
888
+ request.id,
889
+ ErrorCode.InvalidParams,
890
+ "Missing required parameter: name"
891
+ );
892
+ }
893
+ const prompt = this.prompts.get(params.name);
894
+ if (!prompt) {
895
+ return createErrorResponse(
896
+ request.id,
897
+ ErrorCode.InvalidParams,
898
+ `Unknown prompt: ${params.name}`
899
+ );
900
+ }
901
+ const progressToken = params._meta?.progressToken;
902
+ const extra = {
903
+ sessionId: session?.id ?? "",
904
+ sendLogging: (level, data, logger) => {
905
+ if (session) this.sendLogging(session.id, level, data, logger);
906
+ },
907
+ sendProgress: (progress, total) => {
908
+ if (session) this.sendProgress(session.id, progressToken, progress, total);
909
+ }
910
+ };
911
+ const args = params.arguments ?? {};
912
+ const result = await prompt.definition.get(args, extra);
913
+ return createResultResponse(request.id, result);
914
+ }
915
+ // ─── logging/setLevel ──────────────────────────────
916
+ handleLoggingSetLevel(request, session) {
917
+ const params = request.params ?? {};
918
+ if (!params.level) {
919
+ return createErrorResponse(
920
+ request.id,
921
+ ErrorCode.InvalidParams,
922
+ "Missing required parameter: level"
923
+ );
924
+ }
925
+ const validLevels = [
926
+ "debug",
927
+ "info",
928
+ "notice",
929
+ "warning",
930
+ "error",
931
+ "critical",
932
+ "alert",
933
+ "emergency"
934
+ ];
935
+ if (!validLevels.includes(params.level)) {
936
+ return createErrorResponse(
937
+ request.id,
938
+ ErrorCode.InvalidParams,
939
+ `Invalid logging level: ${params.level}`
940
+ );
941
+ }
942
+ if (session) {
943
+ session.loggingLevel = params.level;
944
+ }
945
+ return createResultResponse(request.id, {});
946
+ }
947
+ // ─── resources/subscribe ───────────────────────────
948
+ handleResourcesSubscribe(request, session) {
949
+ const params = request.params ?? {};
950
+ if (!params.uri) {
951
+ return createErrorResponse(
952
+ request.id,
953
+ ErrorCode.InvalidParams,
954
+ "Missing required parameter: uri"
955
+ );
956
+ }
957
+ if (!session) {
958
+ return createErrorResponse(request.id, ErrorCode.InvalidRequest, "No session for subscribe");
959
+ }
960
+ this.sessions.subscribeResource(session.id, params.uri);
961
+ return createResultResponse(request.id, {});
962
+ }
963
+ // ─── resources/unsubscribe ─────────────────────────
964
+ handleResourcesUnsubscribe(request, session) {
965
+ const params = request.params ?? {};
966
+ if (!params.uri) {
967
+ return createErrorResponse(
968
+ request.id,
969
+ ErrorCode.InvalidParams,
970
+ "Missing required parameter: uri"
971
+ );
972
+ }
973
+ if (!session) {
974
+ return createErrorResponse(
975
+ request.id,
976
+ ErrorCode.InvalidRequest,
977
+ "No session for unsubscribe"
978
+ );
979
+ }
980
+ this.sessions.unsubscribeResource(session.id, params.uri);
981
+ return createResultResponse(request.id, {});
982
+ }
983
+ // ─── completion/complete ───────────────────────────
984
+ async handleCompletionComplete(request) {
985
+ const params = request.params ?? {};
986
+ if (!params.ref || !params.argument) {
987
+ return createErrorResponse(
988
+ request.id,
989
+ ErrorCode.InvalidParams,
990
+ "Missing required parameter: ref and argument"
991
+ );
992
+ }
993
+ const ref = params.ref;
994
+ let refType;
995
+ let refId;
996
+ if (ref.type === "ref/prompt" && typeof ref.name === "string") {
997
+ refType = "ref/prompt";
998
+ refId = ref.name;
999
+ } else if (ref.type === "ref/resource" && typeof ref.uri === "string") {
1000
+ refType = "ref/resource";
1001
+ refId = ref.uri;
1002
+ } else {
1003
+ return createErrorResponse(
1004
+ request.id,
1005
+ ErrorCode.InvalidParams,
1006
+ `Invalid ref: ${JSON.stringify(ref)}`
1007
+ );
1008
+ }
1009
+ if (typeof params.argument.name !== "string") {
1010
+ return createErrorResponse(
1011
+ request.id,
1012
+ ErrorCode.InvalidParams,
1013
+ "Missing required parameter: argument.name"
1014
+ );
1015
+ }
1016
+ const key = completionKey(refType, refId, params.argument.name);
1017
+ const completion = this.completions.get(key);
1018
+ if (!completion) {
1019
+ return createErrorResponse(
1020
+ request.id,
1021
+ ErrorCode.MethodNotFound,
1022
+ `No completion handler for ${refType} "${refId}" argument "${params.argument.name}"`
1023
+ );
1024
+ }
1025
+ const value = params.argument.value ?? "";
1026
+ const context = {
1027
+ arguments: params.arguments ?? {}
1028
+ };
1029
+ let result;
1030
+ try {
1031
+ result = await completion.handler(value, context);
1032
+ } catch (err) {
1033
+ return createErrorResponse(
1034
+ request.id,
1035
+ ErrorCode.InternalError,
1036
+ err instanceof Error ? err.message : String(err)
1037
+ );
1038
+ }
1039
+ return createResultResponse(request.id, { completion: result });
1040
+ }
1041
+ // ─── 自定义方法分发(业务拓展) ───────────────────────
1042
+ async handleCustomMethod(request, session) {
1043
+ const registered = this.methods.get(request.method);
1044
+ if (!registered) {
1045
+ return createErrorResponse(
1046
+ request.id,
1047
+ ErrorCode.MethodNotFound,
1048
+ `Method not found: ${request.method}`
1049
+ );
1050
+ }
1051
+ const extra = {
1052
+ sessionId: session?.id ?? "",
1053
+ sendLogging: (level, data, logger) => {
1054
+ if (session) this.sendLogging(session.id, level, data, logger);
1055
+ },
1056
+ sendProgress: (progress, total) => {
1057
+ if (session) this.sendProgress(session.id, void 0, progress, total);
1058
+ }
1059
+ };
1060
+ let result;
1061
+ try {
1062
+ result = await registered.handler(request.params, session, extra);
1063
+ } catch (err) {
1064
+ return createErrorResponse(
1065
+ request.id,
1066
+ ErrorCode.InternalError,
1067
+ err instanceof Error ? err.message : String(err)
1068
+ );
1069
+ }
1070
+ if (result !== null && typeof result === "object" && "error" in result && result.error !== null && typeof result.error === "object") {
1071
+ const errResp = result;
1072
+ return { ...errResp, jsonrpc: "2.0", id: request.id };
1073
+ }
1074
+ return createResultResponse(request.id, result);
1075
+ }
1076
+ // ─── sendLogging(应用级 + handler extra 共用) ─────
1077
+ /**
1078
+ * 推送 notifications/message 到 session 的所有 SSE 订阅者
1079
+ *
1080
+ * - 无 session 或无订阅者:静默丢弃
1081
+ * - level 低于 session.loggingLevel:静默丢弃
1082
+ * - SSE 行格式:`data: ${JSON.stringify(notification)}\n\n`
1083
+ */
1084
+ sendLogging(sessionId, level, data, logger) {
1085
+ if (!this.sessions.shouldLog(sessionId, level)) return;
1086
+ const notification = {
1087
+ jsonrpc: "2.0",
1088
+ method: "notifications/message",
1089
+ params: { level, data }
1090
+ };
1091
+ if (logger !== void 0) {
1092
+ notification.params.logger = logger;
1093
+ }
1094
+ const sseData = `data: ${JSON.stringify(notification)}
1095
+
1096
+ `;
1097
+ this.sessions.broadcastToSession(sessionId, sseData);
1098
+ }
1099
+ /**
1100
+ * 推送 notifications/resources/updated 到所有订阅了该 URI 的 session
1101
+ *
1102
+ * - 找出所有 subscribedResources 包含该 URI 的 session
1103
+ * - 对每个 session 的所有 SSE 订阅者推送通知
1104
+ * - 无订阅者的 session 静默跳过
1105
+ */
1106
+ sendResourceUpdated(uri) {
1107
+ const sessionIds = this.sessions.findSubscribersOfUri(uri);
1108
+ if (sessionIds.length === 0) return;
1109
+ const notification = {
1110
+ jsonrpc: "2.0",
1111
+ method: "notifications/resources/updated",
1112
+ params: { uri }
1113
+ };
1114
+ const sseData = `data: ${JSON.stringify(notification)}
1115
+
1116
+ `;
1117
+ for (const sessionId of sessionIds) {
1118
+ this.sessions.broadcastToSession(sessionId, sseData);
1119
+ }
1120
+ }
1121
+ /**
1122
+ * 推送 notifications/progress 到 session 的所有 SSE 订阅者
1123
+ *
1124
+ * - 无 session 或无订阅者:静默丢弃
1125
+ * - progressToken 为 undefined/null:静默丢弃(无法关联进度与请求)
1126
+ *
1127
+ * @param sessionId 会话 ID
1128
+ * @param progressToken 客户端在请求 _meta.progressToken 中传入的 token(任意 JSON 值)
1129
+ * @param progress 当前进度(数值)
1130
+ * @param total 总数(可选)
1131
+ */
1132
+ sendProgress(sessionId, progressToken, progress, total) {
1133
+ if (progressToken === void 0 || progressToken === null) return;
1134
+ const params = { progressToken, progress };
1135
+ if (total !== void 0) {
1136
+ params.total = total;
1137
+ }
1138
+ const notification = {
1139
+ jsonrpc: "2.0",
1140
+ method: "notifications/progress",
1141
+ params
1142
+ };
1143
+ const sseData = `data: ${JSON.stringify(notification)}
1144
+
1145
+ `;
1146
+ this.sessions.broadcastToSession(sessionId, sseData);
1147
+ }
1148
+ /**
1149
+ * 通用通知推送(业务拓展)——向指定 session 的所有 SSE 订阅者推送任意通知
1150
+ *
1151
+ * - 无 session 或无订阅者:静默丢弃
1152
+ * - 不校验 method 是否符合 MCP 规范——业务方自行负责
1153
+ * - SSE 行格式:`data: ${JSON.stringify(notification)}\n\n`
1154
+ *
1155
+ * @param sessionId 目标 session ID
1156
+ * @param method 通知方法名(如 `notifications/myapp/sync`)
1157
+ * @param params 通知参数(可选)
1158
+ */
1159
+ sendNotification(sessionId, method, params) {
1160
+ const notification = {
1161
+ jsonrpc: "2.0",
1162
+ method
1163
+ };
1164
+ if (params !== void 0) {
1165
+ notification.params = params;
1166
+ }
1167
+ const sseData = `data: ${JSON.stringify(notification)}
1168
+
1169
+ `;
1170
+ this.sessions.broadcastToSession(sessionId, sseData);
1171
+ }
1172
+ };
1173
+ function buildZodObject(shape) {
1174
+ return z.object(shape);
1175
+ }
1176
+ function completionKey(refType, refId, argumentName) {
1177
+ return `${refType}:${refId}:${argumentName}`;
1178
+ }
1179
+ function createMcpServer(options) {
1180
+ return new McpServer(options);
1181
+ }
1182
+
1183
+ // src/streamableHttp.ts
1184
+ var DEFAULT_SSE_HEARTBEAT_MS = 3e4;
1185
+ async function handleMcpRequest(request, server) {
1186
+ switch (request.method) {
1187
+ case "POST":
1188
+ return handlePost(request, server);
1189
+ case "GET":
1190
+ return handleGet(server, request);
1191
+ case "DELETE":
1192
+ return handleDelete(request, server);
1193
+ default:
1194
+ return new Response(null, { status: 405 });
1195
+ }
1196
+ }
1197
+ function handleGet(server, request) {
1198
+ const heartbeatMs = server.getSseHeartbeatMs() ?? DEFAULT_SSE_HEARTBEAT_MS;
1199
+ const encoder = new TextEncoder();
1200
+ let interval;
1201
+ let subscriber;
1202
+ const sessionId = request.headers.get("mcp-session-id") ?? void 0;
1203
+ const stream = new ReadableStream({
1204
+ start(controller) {
1205
+ controller.enqueue(encoder.encode(": connected\n\n"));
1206
+ if (sessionId) {
1207
+ subscriber = server.getSessionManager().addSubscriber(sessionId, controller);
1208
+ }
1209
+ interval = setInterval(() => {
1210
+ try {
1211
+ controller.enqueue(encoder.encode(`: keepalive ${Date.now()}
1212
+
1213
+ `));
1214
+ } catch {
1215
+ if (interval) clearInterval(interval);
1216
+ }
1217
+ }, heartbeatMs);
1218
+ },
1219
+ cancel() {
1220
+ if (interval) clearInterval(interval);
1221
+ if (subscriber) {
1222
+ server.getSessionManager().removeSubscriber(subscriber);
1223
+ }
1224
+ }
1225
+ });
1226
+ return new Response(stream, {
1227
+ status: 200,
1228
+ headers: {
1229
+ "Content-Type": "text/event-stream",
1230
+ "Cache-Control": "no-cache",
1231
+ Connection: "keep-alive"
1232
+ }
1233
+ });
1234
+ }
1235
+ async function handlePost(request, server) {
1236
+ let body;
1237
+ try {
1238
+ body = await request.json();
1239
+ } catch {
1240
+ return jsonResponse(
1241
+ 400,
1242
+ createErrorResponse(null, ErrorCode.ParseError, "Parse error: Invalid JSON")
1243
+ );
1244
+ }
1245
+ 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));
1251
+ }
1252
+ const requests = messages.filter(isRequest);
1253
+ const notifications = messages.filter(isNotification);
1254
+ const hasInitialize = requests.some((r) => r.method === "initialize");
1255
+ if (!hasInitialize) {
1256
+ const acceptHeader = request.headers.get("accept") ?? "";
1257
+ const accepts = acceptHeader.split(",").map((s) => s.trim().toLowerCase()).filter((s) => s.length > 0);
1258
+ const hasJson = accepts.some(
1259
+ (s) => s === "application/json" || s.startsWith("application/json;")
1260
+ );
1261
+ const hasSse = accepts.some(
1262
+ (s) => s === "text/event-stream" || s.startsWith("text/event-stream;")
1263
+ );
1264
+ if (!hasJson || !hasSse) {
1265
+ return jsonResponse(
1266
+ 400,
1267
+ createErrorResponse(
1268
+ null,
1269
+ ErrorCode.InvalidRequest,
1270
+ "Accept header must include both application/json and text/event-stream"
1271
+ )
1272
+ );
1273
+ }
1274
+ }
1275
+ const sessionId = request.headers.get("mcp-session-id") ?? void 0;
1276
+ const sessionManager = server.getSessionManager();
1277
+ let session = sessionId ? sessionManager.get(sessionId) : void 0;
1278
+ if (hasInitialize) {
1279
+ if (session) {
1280
+ return jsonResponse(
1281
+ 400,
1282
+ createErrorResponse(null, ErrorCode.InvalidRequest, "Server already initialized")
1283
+ );
1284
+ }
1285
+ 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
+ );
1291
+ }
1292
+ for (const notification of notifications) {
1293
+ await server.handleJsonRpc(notification, session);
1294
+ }
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) {
1303
+ return new Response(null, { status: 202 });
1304
+ }
1305
+ const responseHeaders = {
1306
+ "Content-Type": "application/json"
1307
+ };
1308
+ if (hasInitialize && session) {
1309
+ responseHeaders["Mcp-Session-Id"] = session.id;
1310
+ }
1311
+ const responseBody = responses.length === 1 ? responses[0] : responses;
1312
+ return new Response(JSON.stringify(responseBody), {
1313
+ status: 200,
1314
+ headers: responseHeaders
1315
+ });
1316
+ }
1317
+ function handleDelete(request, server) {
1318
+ const sessionId = request.headers.get("mcp-session-id");
1319
+ if (!sessionId) {
1320
+ return Promise.resolve(
1321
+ jsonResponse(
1322
+ 400,
1323
+ createErrorResponse(null, ErrorCode.InvalidRequest, "Missing Mcp-Session-Id header")
1324
+ )
1325
+ );
1326
+ }
1327
+ const deleted = server.getSessionManager().delete(sessionId);
1328
+ if (!deleted) {
1329
+ return Promise.resolve(
1330
+ jsonResponse(404, createErrorResponse(null, ErrorCode.RequestTimeout, "Session not found"))
1331
+ );
1332
+ }
1333
+ return Promise.resolve(new Response(null, { status: 200 }));
1334
+ }
1335
+ function jsonResponse(status, body) {
1336
+ return new Response(JSON.stringify(body), {
1337
+ status,
1338
+ headers: { "Content-Type": "application/json" }
1339
+ });
1340
+ }
1341
+
1342
+ // src/faapiAdapter.ts
1343
+ import { Readable } from "stream";
1344
+ function createMcpHandler(mcp) {
1345
+ const handler = async (ctx) => {
1346
+ return handleMcpRequest(ctx.request, mcp);
1347
+ };
1348
+ return { POST: handler, GET: handler, DELETE: handler };
1349
+ }
1350
+ function createMcpNodeHandler(mcp) {
1351
+ return async (req, res) => {
1352
+ const url = new URL(req.url ?? "/", "http://localhost");
1353
+ const headers = new Headers();
1354
+ for (const [key, value] of Object.entries(req.headers)) {
1355
+ if (Array.isArray(value)) {
1356
+ for (const v of value) headers.set(key, v);
1357
+ } else if (value !== void 0) {
1358
+ headers.set(key, value);
1359
+ }
1360
+ }
1361
+ const method = req.method ?? "GET";
1362
+ const hasBody = method !== "GET" && method !== "HEAD" && method !== "DELETE";
1363
+ const body = hasBody ? Readable.toWeb(req) : void 0;
1364
+ const request = new Request(url, {
1365
+ method,
1366
+ headers,
1367
+ ...body && { body, duplex: "half" }
1368
+ });
1369
+ const response = await handleMcpRequest(request, mcp);
1370
+ res.statusCode = response.status;
1371
+ response.headers.forEach((value, key) => {
1372
+ res.setHeader(key, value);
1373
+ });
1374
+ if (response.body) {
1375
+ const nodeStream = Readable.fromWeb(response.body);
1376
+ await new Promise((resolve, reject) => {
1377
+ let settled = false;
1378
+ const settle = (err) => {
1379
+ if (settled) return;
1380
+ settled = true;
1381
+ if (err) reject(err);
1382
+ else resolve();
1383
+ };
1384
+ nodeStream.on("error", (err) => settle(err));
1385
+ res.on("error", (err) => settle(err));
1386
+ res.on("finish", () => settle());
1387
+ res.on("close", () => settle());
1388
+ nodeStream.pipe(res);
1389
+ });
1390
+ return;
1391
+ }
1392
+ res.end();
1393
+ };
1394
+ }
1395
+ export {
1396
+ ErrorCode,
1397
+ JsonRpcParseError,
1398
+ McpServer,
1399
+ PROTOCOL_VERSION,
1400
+ SUPPORTED_PROTOCOL_VERSIONS,
1401
+ SessionManager,
1402
+ createErrorResponse,
1403
+ createMcpHandler,
1404
+ createMcpNodeHandler,
1405
+ createMcpServer,
1406
+ createResultResponse,
1407
+ handleMcpRequest,
1408
+ isErrorResponse,
1409
+ isNotification,
1410
+ isRequest,
1411
+ isResultResponse,
1412
+ parseJsonRpcMessage
1413
+ };
1414
+ //# sourceMappingURL=index.js.map