@modelcontextprotocol/server 2.0.0-alpha.1

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.mjs ADDED
@@ -0,0 +1,2296 @@
1
+ import { $ as PARSE_ERROR, A as parseJSONRPCMessage, B as ListRootsResultSchema, C as isInitializeRequest, D as isJSONRPCRequest, E as isJSONRPCNotification, F as CreateTaskResultSchema, G as ProtocolErrorCode, H as getResultSchema, I as ElicitResultSchema, J as INVALID_PARAMS, K as DEFAULT_NEGOTIATED_PROTOCOL_VERSION, L as EmptyResultSchema, M as CallToolResultSchema, N as CreateMessageResultSchema, O as isJSONRPCResultResponse, P as CreateMessageResultWithToolsSchema, Q as METHOD_NOT_FOUND, R as GetTaskPayloadResultSchema, S as assertCompleteRequestResourceTemplate, T as isJSONRPCErrorResponse, U as ProtocolError, V as LoggingLevelSchema, W as UrlElicitationRequiredError, X as JSONRPC_VERSION, Y as INVALID_REQUEST, Z as LATEST_PROTOCOL_VERSION, _ as mergeCapabilities, a as standardSchemaToJsonSchema, at as SdkError, b as parseSchema, c as createFetchWithInit, ct as OAuthErrorCode, d as deserializeMessage, et as RELATED_TASK_META_KEY, f as serializeMessage, g as Protocol, h as DEFAULT_REQUEST_TIMEOUT_MSEC, i as promptArgumentsFromStandardSchema, it as resourceUrlFromServerUrl, j as CallToolRequestSchema, k as isTaskAugmentedRequestParams, l as validateAndWarnToolName, m as toArrayAsync, n as CfWorkerJsonSchemaValidator, nt as getDisplayName, o as validateStandardSchema, ot as SdkErrorCode, p as takeResult, q as INTERNAL_ERROR, r as AjvJsonSchemaValidator, rt as checkResourceAllowed, s as UriTemplate, st as OAuthError, t as fromJsonSchema$1, tt as SUPPORTED_PROTOCOL_VERSIONS, u as ReadBuffer, v as extractTaskManagerOptions, w as isInitializedNotification, x as assertCompleteRequestPrompt, y as isTerminal, z as JSONRPCMessageSchema } from "./src-IKPjmxu7.mjs";
2
+ import { DefaultJsonSchemaValidator, process } from "@modelcontextprotocol/server/_shims";
3
+
4
+ //#region src/server/completable.ts
5
+ const COMPLETABLE_SYMBOL = Symbol.for("mcp.completable");
6
+ /**
7
+ * Wraps a schema to provide autocompletion capabilities. Useful for, e.g., prompt arguments in MCP.
8
+ *
9
+ * @example
10
+ * ```ts source="./completable.examples.ts#completable_basicUsage"
11
+ * server.registerPrompt(
12
+ * 'review-code',
13
+ * {
14
+ * title: 'Code Review',
15
+ * argsSchema: z.object({
16
+ * language: completable(z.string().describe('Programming language'), value =>
17
+ * ['typescript', 'javascript', 'python', 'rust', 'go'].filter(lang => lang.startsWith(value))
18
+ * )
19
+ * })
20
+ * },
21
+ * ({ language }) => ({
22
+ * messages: [
23
+ * {
24
+ * role: 'user' as const,
25
+ * content: {
26
+ * type: 'text' as const,
27
+ * text: `Review this ${language} code.`
28
+ * }
29
+ * }
30
+ * ]
31
+ * })
32
+ * );
33
+ * ```
34
+ *
35
+ * @see {@linkcode server/mcp.McpServer.registerPrompt | McpServer.registerPrompt} for using completable schemas in prompt argument definitions
36
+ */
37
+ function completable(schema, complete) {
38
+ Object.defineProperty(schema, COMPLETABLE_SYMBOL, {
39
+ value: { complete },
40
+ enumerable: false,
41
+ writable: false,
42
+ configurable: false
43
+ });
44
+ return schema;
45
+ }
46
+ /**
47
+ * Checks if a schema is completable (has completion metadata).
48
+ */
49
+ function isCompletable(schema) {
50
+ return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
51
+ }
52
+ /**
53
+ * Gets the completer callback from a completable schema, if it exists.
54
+ */
55
+ function getCompleter(schema) {
56
+ return schema[COMPLETABLE_SYMBOL]?.complete;
57
+ }
58
+
59
+ //#endregion
60
+ //#region ../core/src/experimental/tasks/helpers.ts
61
+ /**
62
+ * Experimental task capability assertion helpers.
63
+ * WARNING: These APIs are experimental and may change without notice.
64
+ *
65
+ * @experimental
66
+ */
67
+ /**
68
+ * Asserts that task creation is supported for `tools/call`.
69
+ * Used to implement the `assertTaskCapability` or `assertTaskHandlerCapability` abstract methods on Protocol.
70
+ *
71
+ * @param requests - The task requests capability object
72
+ * @param method - The method being checked
73
+ * @param entityName - `'Server'` or `'Client'` for error messages
74
+ * @throws {@linkcode SdkError} with {@linkcode SdkErrorCode.CapabilityNotSupported} if the capability is not supported
75
+ *
76
+ * @experimental
77
+ */
78
+ function assertToolsCallTaskCapability(requests, method, entityName) {
79
+ if (!requests) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `${entityName} does not support task creation (required for ${method})`);
80
+ switch (method) {
81
+ case "tools/call":
82
+ if (!requests.tools?.call) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `${entityName} does not support task creation for tools/call (required for ${method})`);
83
+ break;
84
+ default: break;
85
+ }
86
+ }
87
+ /**
88
+ * Asserts that task creation is supported for `sampling/createMessage` or `elicitation/create`.
89
+ * Used to implement the `assertTaskCapability` or `assertTaskHandlerCapability` abstract methods on Protocol.
90
+ *
91
+ * @param requests - The task requests capability object
92
+ * @param method - The method being checked
93
+ * @param entityName - `'Server'` or `'Client'` for error messages
94
+ * @throws {@linkcode SdkError} with {@linkcode SdkErrorCode.CapabilityNotSupported} if the capability is not supported
95
+ *
96
+ * @experimental
97
+ */
98
+ function assertClientRequestTaskCapability(requests, method, entityName) {
99
+ if (!requests) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `${entityName} does not support task creation (required for ${method})`);
100
+ switch (method) {
101
+ case "sampling/createMessage":
102
+ if (!requests.sampling?.createMessage) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `${entityName} does not support task creation for sampling/createMessage (required for ${method})`);
103
+ break;
104
+ case "elicitation/create":
105
+ if (!requests.elicitation?.create) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `${entityName} does not support task creation for elicitation/create (required for ${method})`);
106
+ break;
107
+ default: break;
108
+ }
109
+ }
110
+
111
+ //#endregion
112
+ //#region ../core/src/experimental/tasks/stores/inMemory.ts
113
+ /**
114
+ * In-memory {@linkcode TaskStore} implementation for development and testing.
115
+ * For production, use a database or distributed cache.
116
+ * @experimental
117
+ */
118
+ var InMemoryTaskStore = class {
119
+ tasks = /* @__PURE__ */ new Map();
120
+ cleanupTimers = /* @__PURE__ */ new Map();
121
+ /**
122
+ * Generates a unique task ID using Web Crypto API.
123
+ */
124
+ generateTaskId() {
125
+ return crypto.randomUUID().replaceAll("-", "");
126
+ }
127
+ /** {@inheritDoc TaskStore.createTask} */
128
+ async createTask(taskParams, requestId, request, sessionId) {
129
+ const taskId = this.generateTaskId();
130
+ if (this.tasks.has(taskId)) throw new Error(`Task with ID ${taskId} already exists`);
131
+ const actualTtl = taskParams.ttl ?? null;
132
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
133
+ const task = {
134
+ taskId,
135
+ status: "working",
136
+ ttl: actualTtl,
137
+ createdAt,
138
+ lastUpdatedAt: createdAt,
139
+ pollInterval: taskParams.pollInterval ?? 1e3
140
+ };
141
+ this.tasks.set(taskId, {
142
+ task,
143
+ request,
144
+ requestId,
145
+ sessionId
146
+ });
147
+ if (actualTtl) {
148
+ const timer = setTimeout(() => {
149
+ this.tasks.delete(taskId);
150
+ this.cleanupTimers.delete(taskId);
151
+ }, actualTtl);
152
+ this.cleanupTimers.set(taskId, timer);
153
+ }
154
+ return task;
155
+ }
156
+ /**
157
+ * Retrieves a stored task, enforcing session ownership when a sessionId is provided.
158
+ * Returns undefined if the task does not exist or belongs to a different session.
159
+ */
160
+ getStoredTask(taskId, sessionId) {
161
+ const stored = this.tasks.get(taskId);
162
+ if (!stored) return;
163
+ if (sessionId !== void 0 && stored.sessionId !== void 0 && stored.sessionId !== sessionId) return;
164
+ return stored;
165
+ }
166
+ async getTask(taskId, sessionId) {
167
+ const stored = this.getStoredTask(taskId, sessionId);
168
+ return stored ? { ...stored.task } : null;
169
+ }
170
+ /** {@inheritDoc TaskStore.storeTaskResult} */
171
+ async storeTaskResult(taskId, status, result, sessionId) {
172
+ const stored = this.getStoredTask(taskId, sessionId);
173
+ if (!stored) throw new Error(`Task with ID ${taskId} not found`);
174
+ if (isTerminal(stored.task.status)) throw new Error(`Cannot store result for task ${taskId} in terminal status '${stored.task.status}'. Task results can only be stored once.`);
175
+ stored.result = result;
176
+ stored.task.status = status;
177
+ stored.task.lastUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
178
+ if (stored.task.ttl) {
179
+ const existingTimer = this.cleanupTimers.get(taskId);
180
+ if (existingTimer) clearTimeout(existingTimer);
181
+ const timer = setTimeout(() => {
182
+ this.tasks.delete(taskId);
183
+ this.cleanupTimers.delete(taskId);
184
+ }, stored.task.ttl);
185
+ this.cleanupTimers.set(taskId, timer);
186
+ }
187
+ }
188
+ /** {@inheritDoc TaskStore.getTaskResult} */
189
+ async getTaskResult(taskId, sessionId) {
190
+ const stored = this.getStoredTask(taskId, sessionId);
191
+ if (!stored) throw new Error(`Task with ID ${taskId} not found`);
192
+ if (!stored.result) throw new Error(`Task ${taskId} has no result stored`);
193
+ return stored.result;
194
+ }
195
+ /** {@inheritDoc TaskStore.updateTaskStatus} */
196
+ async updateTaskStatus(taskId, status, statusMessage, sessionId) {
197
+ const stored = this.getStoredTask(taskId, sessionId);
198
+ if (!stored) throw new Error(`Task with ID ${taskId} not found`);
199
+ if (isTerminal(stored.task.status)) throw new Error(`Cannot update task ${taskId} from terminal status '${stored.task.status}' to '${status}'. Terminal states (completed, failed, cancelled) cannot transition to other states.`);
200
+ stored.task.status = status;
201
+ if (statusMessage) stored.task.statusMessage = statusMessage;
202
+ stored.task.lastUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
203
+ if (isTerminal(status) && stored.task.ttl) {
204
+ const existingTimer = this.cleanupTimers.get(taskId);
205
+ if (existingTimer) clearTimeout(existingTimer);
206
+ const timer = setTimeout(() => {
207
+ this.tasks.delete(taskId);
208
+ this.cleanupTimers.delete(taskId);
209
+ }, stored.task.ttl);
210
+ this.cleanupTimers.set(taskId, timer);
211
+ }
212
+ }
213
+ /** {@inheritDoc TaskStore.listTasks} */
214
+ async listTasks(cursor, sessionId) {
215
+ const PAGE_SIZE = 10;
216
+ const filteredTaskIds = [...this.tasks.entries()].filter(([, stored]) => {
217
+ if (sessionId === void 0 || stored.sessionId === void 0) return true;
218
+ return stored.sessionId === sessionId;
219
+ }).map(([taskId]) => taskId);
220
+ let startIndex = 0;
221
+ if (cursor) {
222
+ const cursorIndex = filteredTaskIds.indexOf(cursor);
223
+ if (cursorIndex === -1) throw new Error(`Invalid cursor: ${cursor}`);
224
+ else startIndex = cursorIndex + 1;
225
+ }
226
+ const pageTaskIds = filteredTaskIds.slice(startIndex, startIndex + PAGE_SIZE);
227
+ return {
228
+ tasks: pageTaskIds.map((taskId) => {
229
+ return { ...this.tasks.get(taskId).task };
230
+ }),
231
+ nextCursor: startIndex + PAGE_SIZE < filteredTaskIds.length ? pageTaskIds.at(-1) : void 0
232
+ };
233
+ }
234
+ /**
235
+ * Cleanup all timers (useful for testing or graceful shutdown)
236
+ */
237
+ cleanup() {
238
+ for (const timer of this.cleanupTimers.values()) clearTimeout(timer);
239
+ this.cleanupTimers.clear();
240
+ this.tasks.clear();
241
+ }
242
+ /**
243
+ * Get all tasks (useful for debugging)
244
+ */
245
+ getAllTasks() {
246
+ return [...this.tasks.values()].map((stored) => ({ ...stored.task }));
247
+ }
248
+ };
249
+ /**
250
+ * In-memory {@linkcode TaskMessageQueue} implementation for development and testing.
251
+ * For production, use Redis or another distributed queue.
252
+ * @experimental
253
+ */
254
+ var InMemoryTaskMessageQueue = class {
255
+ queues = /* @__PURE__ */ new Map();
256
+ /**
257
+ * Generates a queue key from taskId.
258
+ * SessionId is intentionally ignored because taskIds are globally unique
259
+ * and tasks need to be accessible across HTTP requests/sessions.
260
+ */
261
+ getQueueKey(taskId, _sessionId) {
262
+ return taskId;
263
+ }
264
+ /**
265
+ * Gets or creates a queue for the given task and session.
266
+ */
267
+ getQueue(taskId, sessionId) {
268
+ const key = this.getQueueKey(taskId, sessionId);
269
+ let queue = this.queues.get(key);
270
+ if (!queue) {
271
+ queue = [];
272
+ this.queues.set(key, queue);
273
+ }
274
+ return queue;
275
+ }
276
+ /**
277
+ * Adds a message to the end of the queue for a specific task.
278
+ * Atomically checks queue size and throws if maxSize would be exceeded.
279
+ * @param taskId The task identifier
280
+ * @param message The message to enqueue
281
+ * @param sessionId Optional session ID for binding the operation to a specific session
282
+ * @param maxSize Optional maximum queue size - if specified and queue is full, throws an error
283
+ * @throws Error if maxSize is specified and would be exceeded
284
+ */
285
+ async enqueue(taskId, message, sessionId, maxSize) {
286
+ const queue = this.getQueue(taskId, sessionId);
287
+ if (maxSize !== void 0 && queue.length >= maxSize) throw new Error(`Task message queue overflow: queue size (${queue.length}) exceeds maximum (${maxSize})`);
288
+ queue.push(message);
289
+ }
290
+ /**
291
+ * Removes and returns the first message from the queue for a specific task.
292
+ * @param taskId The task identifier
293
+ * @param sessionId Optional session ID for binding the query to a specific session
294
+ * @returns The first message, or `undefined` if the queue is empty
295
+ */
296
+ async dequeue(taskId, sessionId) {
297
+ return this.getQueue(taskId, sessionId).shift();
298
+ }
299
+ /**
300
+ * Removes and returns all messages from the queue for a specific task.
301
+ * @param taskId The task identifier
302
+ * @param sessionId Optional session ID for binding the query to a specific session
303
+ * @returns Array of all messages that were in the queue
304
+ */
305
+ async dequeueAll(taskId, sessionId) {
306
+ const key = this.getQueueKey(taskId, sessionId);
307
+ const queue = this.queues.get(key) ?? [];
308
+ this.queues.delete(key);
309
+ return queue;
310
+ }
311
+ };
312
+
313
+ //#endregion
314
+ //#region src/experimental/tasks/mcpServer.ts
315
+ /**
316
+ * Experimental task features for {@linkcode McpServer}.
317
+ *
318
+ * Access via `server.experimental.tasks`:
319
+ * ```typescript
320
+ * server.experimental.tasks.registerToolTask('long-running', config, handler);
321
+ * ```
322
+ *
323
+ * @experimental
324
+ */
325
+ var ExperimentalMcpServerTasks = class {
326
+ constructor(_mcpServer) {
327
+ this._mcpServer = _mcpServer;
328
+ }
329
+ registerToolTask(name, config, handler) {
330
+ const execution = {
331
+ taskSupport: "required",
332
+ ...config.execution
333
+ };
334
+ if (execution.taskSupport === "forbidden") throw new Error(`Cannot register task-based tool '${name}' with taskSupport 'forbidden'. Use registerTool() instead.`);
335
+ return this._mcpServer._createRegisteredTool(name, config.title, config.description, config.inputSchema, config.outputSchema, config.annotations, execution, config._meta, handler);
336
+ }
337
+ };
338
+
339
+ //#endregion
340
+ //#region src/experimental/tasks/server.ts
341
+ /**
342
+ * Experimental task features for low-level MCP servers.
343
+ *
344
+ * Access via `server.experimental.tasks`:
345
+ * ```typescript
346
+ * const stream = server.experimental.tasks.requestStream(request, options);
347
+ * ```
348
+ *
349
+ * For high-level server usage with task-based tools, use {@linkcode index.McpServer | McpServer}.experimental.tasks instead.
350
+ *
351
+ * @experimental
352
+ */
353
+ var ExperimentalServerTasks = class {
354
+ constructor(_server) {
355
+ this._server = _server;
356
+ }
357
+ get _module() {
358
+ return this._server.taskManager;
359
+ }
360
+ /**
361
+ * Sends a request and returns an AsyncGenerator that yields response messages.
362
+ * The generator is guaranteed to end with either a `'result'` or `'error'` message.
363
+ *
364
+ * This method provides streaming access to request processing, allowing you to
365
+ * observe intermediate task status updates for task-augmented requests.
366
+ *
367
+ * @param request - The request to send (method name determines the result schema)
368
+ * @param options - Optional request options (timeout, signal, task creation params, etc.)
369
+ * @returns AsyncGenerator that yields {@linkcode ResponseMessage} objects
370
+ *
371
+ * @experimental
372
+ */
373
+ requestStream(request, options) {
374
+ const resultSchema = getResultSchema(request.method);
375
+ return this._module.requestStream(request, resultSchema, options);
376
+ }
377
+ /**
378
+ * Sends a sampling request and returns an AsyncGenerator that yields response messages.
379
+ * The generator is guaranteed to end with either a 'result' or 'error' message.
380
+ *
381
+ * For task-augmented requests, yields 'taskCreated' and 'taskStatus' messages
382
+ * before the final result.
383
+ *
384
+ * @example
385
+ * ```typescript
386
+ * const stream = server.experimental.tasks.createMessageStream({
387
+ * messages: [{ role: 'user', content: { type: 'text', text: 'Hello' } }],
388
+ * maxTokens: 100
389
+ * }, {
390
+ * onprogress: (progress) => {
391
+ * // Handle streaming tokens via progress notifications
392
+ * console.log('Progress:', progress.message);
393
+ * }
394
+ * });
395
+ *
396
+ * for await (const message of stream) {
397
+ * switch (message.type) {
398
+ * case 'taskCreated':
399
+ * console.log('Task created:', message.task.taskId);
400
+ * break;
401
+ * case 'taskStatus':
402
+ * console.log('Task status:', message.task.status);
403
+ * break;
404
+ * case 'result':
405
+ * console.log('Final result:', message.result);
406
+ * break;
407
+ * case 'error':
408
+ * console.error('Error:', message.error);
409
+ * break;
410
+ * }
411
+ * }
412
+ * ```
413
+ *
414
+ * @param params - The sampling request parameters
415
+ * @param options - Optional request options (timeout, signal, task creation params, onprogress, etc.)
416
+ * @returns AsyncGenerator that yields ResponseMessage objects
417
+ *
418
+ * @experimental
419
+ */
420
+ createMessageStream(params, options) {
421
+ const clientCapabilities = this._server.getClientCapabilities();
422
+ if ((params.tools || params.toolChoice) && !clientCapabilities?.sampling?.tools) throw new SdkError(SdkErrorCode.CapabilityNotSupported, "Client does not support sampling tools capability.");
423
+ if (params.messages.length > 0) {
424
+ const lastMessage = params.messages.at(-1);
425
+ const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [lastMessage.content];
426
+ const hasToolResults = lastContent.some((c) => c.type === "tool_result");
427
+ const previousMessage = params.messages.length > 1 ? params.messages.at(-2) : void 0;
428
+ const previousContent = previousMessage ? Array.isArray(previousMessage.content) ? previousMessage.content : [previousMessage.content] : [];
429
+ const hasPreviousToolUse = previousContent.some((c) => c.type === "tool_use");
430
+ if (hasToolResults) {
431
+ if (lastContent.some((c) => c.type !== "tool_result")) throw new Error("The last message must contain only tool_result content if any is present");
432
+ if (!hasPreviousToolUse) throw new Error("tool_result blocks are not matching any tool_use from the previous message");
433
+ }
434
+ if (hasPreviousToolUse) {
435
+ const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
436
+ const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
437
+ if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
438
+ }
439
+ }
440
+ return this.requestStream({
441
+ method: "sampling/createMessage",
442
+ params
443
+ }, options);
444
+ }
445
+ /**
446
+ * Sends an elicitation request and returns an AsyncGenerator that yields response messages.
447
+ * The generator is guaranteed to end with either a 'result' or 'error' message.
448
+ *
449
+ * For task-augmented requests (especially URL-based elicitation), yields 'taskCreated'
450
+ * and 'taskStatus' messages before the final result.
451
+ *
452
+ * @example
453
+ * ```typescript
454
+ * const stream = server.experimental.tasks.elicitInputStream({
455
+ * mode: 'url',
456
+ * message: 'Please authenticate',
457
+ * elicitationId: 'auth-123',
458
+ * url: 'https://example.com/auth'
459
+ * }, {
460
+ * task: { ttl: 300000 } // Task-augmented for long-running auth flow
461
+ * });
462
+ *
463
+ * for await (const message of stream) {
464
+ * switch (message.type) {
465
+ * case 'taskCreated':
466
+ * console.log('Task created:', message.task.taskId);
467
+ * break;
468
+ * case 'taskStatus':
469
+ * console.log('Task status:', message.task.status);
470
+ * break;
471
+ * case 'result':
472
+ * console.log('User action:', message.result.action);
473
+ * break;
474
+ * case 'error':
475
+ * console.error('Error:', message.error);
476
+ * break;
477
+ * }
478
+ * }
479
+ * ```
480
+ *
481
+ * @param params - The elicitation request parameters
482
+ * @param options - Optional request options (timeout, signal, task creation params, etc.)
483
+ * @returns AsyncGenerator that yields ResponseMessage objects
484
+ *
485
+ * @experimental
486
+ */
487
+ elicitInputStream(params, options) {
488
+ const clientCapabilities = this._server.getClientCapabilities();
489
+ const mode = params.mode ?? "form";
490
+ switch (mode) {
491
+ case "url":
492
+ if (!clientCapabilities?.elicitation?.url) throw new SdkError(SdkErrorCode.CapabilityNotSupported, "Client does not support url elicitation.");
493
+ break;
494
+ case "form":
495
+ if (!clientCapabilities?.elicitation?.form) throw new SdkError(SdkErrorCode.CapabilityNotSupported, "Client does not support form elicitation.");
496
+ break;
497
+ }
498
+ const normalizedParams = mode === "form" && params.mode !== "form" ? {
499
+ ...params,
500
+ mode: "form"
501
+ } : params;
502
+ return this.requestStream({
503
+ method: "elicitation/create",
504
+ params: normalizedParams
505
+ }, options);
506
+ }
507
+ /**
508
+ * Gets the current status of a task.
509
+ *
510
+ * @param taskId - The task identifier
511
+ * @param options - Optional request options
512
+ * @returns The task status
513
+ *
514
+ * @experimental
515
+ */
516
+ async getTask(taskId, options) {
517
+ return this._module.getTask({ taskId }, options);
518
+ }
519
+ /**
520
+ * Retrieves the result of a completed task.
521
+ *
522
+ * @param taskId - The task identifier
523
+ * @param options - Optional request options
524
+ * @returns The task result. The payload structure matches the result type of the
525
+ * original request (e.g., a `tools/call` task returns a `CallToolResult`).
526
+ *
527
+ * @experimental
528
+ */
529
+ async getTaskResult(taskId, options) {
530
+ return this._module.getTaskResult({ taskId }, GetTaskPayloadResultSchema, options);
531
+ }
532
+ /**
533
+ * Lists tasks with optional pagination.
534
+ *
535
+ * @param cursor - Optional pagination cursor
536
+ * @param options - Optional request options
537
+ * @returns List of tasks with optional next cursor
538
+ *
539
+ * @experimental
540
+ */
541
+ async listTasks(cursor, options) {
542
+ return this._module.listTasks(cursor ? { cursor } : void 0, options);
543
+ }
544
+ /**
545
+ * Cancels a running task.
546
+ *
547
+ * @param taskId - The task identifier
548
+ * @param options - Optional request options
549
+ *
550
+ * @experimental
551
+ */
552
+ async cancelTask(taskId, options) {
553
+ return this._module.cancelTask({ taskId }, options);
554
+ }
555
+ };
556
+
557
+ //#endregion
558
+ //#region src/server/server.ts
559
+ /**
560
+ * An MCP server on top of a pluggable transport.
561
+ *
562
+ * This server will automatically respond to the initialization flow as initiated from the client.
563
+ *
564
+ * @deprecated Use {@linkcode server/mcp.McpServer | McpServer} instead for the high-level API. Only use `Server` for advanced use cases.
565
+ */
566
+ var Server = class extends Protocol {
567
+ _clientCapabilities;
568
+ _clientVersion;
569
+ _capabilities;
570
+ _instructions;
571
+ _jsonSchemaValidator;
572
+ _experimental;
573
+ /**
574
+ * Callback for when initialization has fully completed (i.e., the client has sent an `notifications/initialized` notification).
575
+ */
576
+ oninitialized;
577
+ /**
578
+ * Initializes this server with the given name and version information.
579
+ */
580
+ constructor(_serverInfo, options) {
581
+ super({
582
+ ...options,
583
+ tasks: extractTaskManagerOptions(options?.capabilities?.tasks)
584
+ });
585
+ this._serverInfo = _serverInfo;
586
+ this._capabilities = options?.capabilities ? { ...options.capabilities } : {};
587
+ this._instructions = options?.instructions;
588
+ this._jsonSchemaValidator = options?.jsonSchemaValidator ?? new DefaultJsonSchemaValidator();
589
+ if (options?.capabilities?.tasks) {
590
+ const { taskStore, taskMessageQueue, defaultTaskPollInterval, maxTaskQueueSize, ...wireCapabilities } = options.capabilities.tasks;
591
+ this._capabilities.tasks = wireCapabilities;
592
+ }
593
+ this.setRequestHandler("initialize", (request) => this._oninitialize(request));
594
+ this.setNotificationHandler("notifications/initialized", () => this.oninitialized?.());
595
+ if (this._capabilities.logging) this._registerLoggingHandler();
596
+ }
597
+ _registerLoggingHandler() {
598
+ this.setRequestHandler("logging/setLevel", async (request, ctx) => {
599
+ const transportSessionId = ctx.sessionId || ctx.http?.req?.headers.get("mcp-session-id") || void 0;
600
+ const { level } = request.params;
601
+ const parseResult = parseSchema(LoggingLevelSchema, level);
602
+ if (parseResult.success) this._loggingLevels.set(transportSessionId, parseResult.data);
603
+ return {};
604
+ });
605
+ }
606
+ buildContext(ctx, transportInfo) {
607
+ const hasHttpInfo = ctx.http || transportInfo?.request || transportInfo?.closeSSEStream || transportInfo?.closeStandaloneSSEStream;
608
+ return {
609
+ ...ctx,
610
+ mcpReq: {
611
+ ...ctx.mcpReq,
612
+ log: (level, data, logger) => this.sendLoggingMessage({
613
+ level,
614
+ data,
615
+ logger
616
+ }),
617
+ elicitInput: (params, options) => this.elicitInput(params, options),
618
+ requestSampling: (params, options) => this.createMessage(params, options)
619
+ },
620
+ http: hasHttpInfo ? {
621
+ ...ctx.http,
622
+ req: transportInfo?.request,
623
+ closeSSE: transportInfo?.closeSSEStream,
624
+ closeStandaloneSSE: transportInfo?.closeStandaloneSSEStream
625
+ } : void 0
626
+ };
627
+ }
628
+ /**
629
+ * Access experimental features.
630
+ *
631
+ * WARNING: These APIs are experimental and may change without notice.
632
+ *
633
+ * @experimental
634
+ */
635
+ get experimental() {
636
+ if (!this._experimental) this._experimental = { tasks: new ExperimentalServerTasks(this) };
637
+ return this._experimental;
638
+ }
639
+ _loggingLevels = /* @__PURE__ */ new Map();
640
+ LOG_LEVEL_SEVERITY = new Map(LoggingLevelSchema.options.map((level, index) => [level, index]));
641
+ isMessageIgnored = (level, sessionId) => {
642
+ const currentLevel = this._loggingLevels.get(sessionId);
643
+ return currentLevel ? this.LOG_LEVEL_SEVERITY.get(level) < this.LOG_LEVEL_SEVERITY.get(currentLevel) : false;
644
+ };
645
+ /**
646
+ * Registers new capabilities. This can only be called before connecting to a transport.
647
+ *
648
+ * The new capabilities will be merged with any existing capabilities previously given (e.g., at initialization).
649
+ */
650
+ registerCapabilities(capabilities) {
651
+ if (this.transport) throw new SdkError(SdkErrorCode.AlreadyConnected, "Cannot register capabilities after connecting to transport");
652
+ const hadLogging = !!this._capabilities.logging;
653
+ this._capabilities = mergeCapabilities(this._capabilities, capabilities);
654
+ if (!hadLogging && this._capabilities.logging) this._registerLoggingHandler();
655
+ }
656
+ /**
657
+ * Override request handler registration to enforce server-side validation for `tools/call`.
658
+ */
659
+ setRequestHandler(method, handler) {
660
+ if (method === "tools/call") {
661
+ const wrappedHandler = async (request, ctx) => {
662
+ const validatedRequest = parseSchema(CallToolRequestSchema, request);
663
+ if (!validatedRequest.success) {
664
+ const errorMessage = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error);
665
+ throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage}`);
666
+ }
667
+ const { params } = validatedRequest.data;
668
+ const result = await Promise.resolve(handler(request, ctx));
669
+ if (params.task) {
670
+ const taskValidationResult = parseSchema(CreateTaskResultSchema, result);
671
+ if (!taskValidationResult.success) {
672
+ const errorMessage = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error);
673
+ throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage}`);
674
+ }
675
+ return taskValidationResult.data;
676
+ }
677
+ const validationResult = parseSchema(CallToolResultSchema, result);
678
+ if (!validationResult.success) {
679
+ const errorMessage = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error);
680
+ throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage}`);
681
+ }
682
+ return validationResult.data;
683
+ };
684
+ return super.setRequestHandler(method, wrappedHandler);
685
+ }
686
+ return super.setRequestHandler(method, handler);
687
+ }
688
+ assertCapabilityForMethod(method) {
689
+ switch (method) {
690
+ case "sampling/createMessage":
691
+ if (!this._clientCapabilities?.sampling) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `Client does not support sampling (required for ${method})`);
692
+ break;
693
+ case "elicitation/create":
694
+ if (!this._clientCapabilities?.elicitation) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `Client does not support elicitation (required for ${method})`);
695
+ break;
696
+ case "roots/list":
697
+ if (!this._clientCapabilities?.roots) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `Client does not support listing roots (required for ${method})`);
698
+ break;
699
+ case "ping": break;
700
+ }
701
+ }
702
+ assertNotificationCapability(method) {
703
+ switch (method) {
704
+ case "notifications/message":
705
+ if (!this._capabilities.logging) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `Server does not support logging (required for ${method})`);
706
+ break;
707
+ case "notifications/resources/updated":
708
+ case "notifications/resources/list_changed":
709
+ if (!this._capabilities.resources) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `Server does not support notifying about resources (required for ${method})`);
710
+ break;
711
+ case "notifications/tools/list_changed":
712
+ if (!this._capabilities.tools) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `Server does not support notifying of tool list changes (required for ${method})`);
713
+ break;
714
+ case "notifications/prompts/list_changed":
715
+ if (!this._capabilities.prompts) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `Server does not support notifying of prompt list changes (required for ${method})`);
716
+ break;
717
+ case "notifications/elicitation/complete":
718
+ if (!this._clientCapabilities?.elicitation?.url) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `Client does not support URL elicitation (required for ${method})`);
719
+ break;
720
+ case "notifications/cancelled": break;
721
+ case "notifications/progress": break;
722
+ }
723
+ }
724
+ assertRequestHandlerCapability(method) {
725
+ switch (method) {
726
+ case "completion/complete":
727
+ if (!this._capabilities.completions) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `Server does not support completions (required for ${method})`);
728
+ break;
729
+ case "logging/setLevel":
730
+ if (!this._capabilities.logging) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `Server does not support logging (required for ${method})`);
731
+ break;
732
+ case "prompts/get":
733
+ case "prompts/list":
734
+ if (!this._capabilities.prompts) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `Server does not support prompts (required for ${method})`);
735
+ break;
736
+ case "resources/list":
737
+ case "resources/templates/list":
738
+ case "resources/read":
739
+ if (!this._capabilities.resources) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `Server does not support resources (required for ${method})`);
740
+ break;
741
+ case "tools/call":
742
+ case "tools/list":
743
+ if (!this._capabilities.tools) throw new SdkError(SdkErrorCode.CapabilityNotSupported, `Server does not support tools (required for ${method})`);
744
+ break;
745
+ case "ping":
746
+ case "initialize": break;
747
+ }
748
+ }
749
+ assertTaskCapability(method) {
750
+ assertClientRequestTaskCapability(this._clientCapabilities?.tasks?.requests, method, "Client");
751
+ }
752
+ assertTaskHandlerCapability(method) {
753
+ assertToolsCallTaskCapability(this._capabilities?.tasks?.requests, method, "Server");
754
+ }
755
+ async _oninitialize(request) {
756
+ const requestedVersion = request.params.protocolVersion;
757
+ this._clientCapabilities = request.params.capabilities;
758
+ this._clientVersion = request.params.clientInfo;
759
+ const protocolVersion = this._supportedProtocolVersions.includes(requestedVersion) ? requestedVersion : this._supportedProtocolVersions[0] ?? LATEST_PROTOCOL_VERSION;
760
+ this.transport?.setProtocolVersion?.(protocolVersion);
761
+ return {
762
+ protocolVersion,
763
+ capabilities: this.getCapabilities(),
764
+ serverInfo: this._serverInfo,
765
+ ...this._instructions && { instructions: this._instructions }
766
+ };
767
+ }
768
+ /**
769
+ * After initialization has completed, this will be populated with the client's reported capabilities.
770
+ */
771
+ getClientCapabilities() {
772
+ return this._clientCapabilities;
773
+ }
774
+ /**
775
+ * After initialization has completed, this will be populated with information about the client's name and version.
776
+ */
777
+ getClientVersion() {
778
+ return this._clientVersion;
779
+ }
780
+ /**
781
+ * Returns the current server capabilities.
782
+ */
783
+ getCapabilities() {
784
+ return this._capabilities;
785
+ }
786
+ async ping() {
787
+ return this._requestWithSchema({ method: "ping" }, EmptyResultSchema);
788
+ }
789
+ async createMessage(params, options) {
790
+ if ((params.tools || params.toolChoice) && !this._clientCapabilities?.sampling?.tools) throw new SdkError(SdkErrorCode.CapabilityNotSupported, "Client does not support sampling tools capability.");
791
+ if (params.messages.length > 0) {
792
+ const lastMessage = params.messages.at(-1);
793
+ const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [lastMessage.content];
794
+ const hasToolResults = lastContent.some((c) => c.type === "tool_result");
795
+ const previousMessage = params.messages.length > 1 ? params.messages.at(-2) : void 0;
796
+ const previousContent = previousMessage ? Array.isArray(previousMessage.content) ? previousMessage.content : [previousMessage.content] : [];
797
+ const hasPreviousToolUse = previousContent.some((c) => c.type === "tool_use");
798
+ if (hasToolResults) {
799
+ if (lastContent.some((c) => c.type !== "tool_result")) throw new ProtocolError(ProtocolErrorCode.InvalidParams, "The last message must contain only tool_result content if any is present");
800
+ if (!hasPreviousToolUse) throw new ProtocolError(ProtocolErrorCode.InvalidParams, "tool_result blocks are not matching any tool_use from the previous message");
801
+ }
802
+ if (hasPreviousToolUse) {
803
+ const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
804
+ const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
805
+ if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) throw new ProtocolError(ProtocolErrorCode.InvalidParams, "ids of tool_result blocks and tool_use blocks from previous message do not match");
806
+ }
807
+ }
808
+ if (params.tools) return this._requestWithSchema({
809
+ method: "sampling/createMessage",
810
+ params
811
+ }, CreateMessageResultWithToolsSchema, options);
812
+ return this._requestWithSchema({
813
+ method: "sampling/createMessage",
814
+ params
815
+ }, CreateMessageResultSchema, options);
816
+ }
817
+ /**
818
+ * Creates an elicitation request for the given parameters.
819
+ * For backwards compatibility, `mode` may be omitted for form requests and will default to `"form"`.
820
+ * @param params The parameters for the elicitation request.
821
+ * @param options Optional request options.
822
+ * @returns The result of the elicitation request.
823
+ */
824
+ async elicitInput(params, options) {
825
+ switch (params.mode ?? "form") {
826
+ case "url": {
827
+ if (!this._clientCapabilities?.elicitation?.url) throw new SdkError(SdkErrorCode.CapabilityNotSupported, "Client does not support url elicitation.");
828
+ const urlParams = params;
829
+ return this._requestWithSchema({
830
+ method: "elicitation/create",
831
+ params: urlParams
832
+ }, ElicitResultSchema, options);
833
+ }
834
+ case "form": {
835
+ if (!this._clientCapabilities?.elicitation?.form) throw new SdkError(SdkErrorCode.CapabilityNotSupported, "Client does not support form elicitation.");
836
+ const formParams = params.mode === "form" ? params : {
837
+ ...params,
838
+ mode: "form"
839
+ };
840
+ const result = await this._requestWithSchema({
841
+ method: "elicitation/create",
842
+ params: formParams
843
+ }, ElicitResultSchema, options);
844
+ if (result.action === "accept" && result.content && formParams.requestedSchema) try {
845
+ const validationResult = this._jsonSchemaValidator.getValidator(formParams.requestedSchema)(result.content);
846
+ if (!validationResult.valid) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Elicitation response content does not match requested schema: ${validationResult.errorMessage}`);
847
+ } catch (error) {
848
+ if (error instanceof ProtocolError) throw error;
849
+ throw new ProtocolError(ProtocolErrorCode.InternalError, `Error validating elicitation response: ${error instanceof Error ? error.message : String(error)}`);
850
+ }
851
+ return result;
852
+ }
853
+ }
854
+ }
855
+ /**
856
+ * Creates a reusable callback that, when invoked, will send a `notifications/elicitation/complete`
857
+ * notification for the specified elicitation ID.
858
+ *
859
+ * @param elicitationId The ID of the elicitation to mark as complete.
860
+ * @param options Optional notification options. Useful when the completion notification should be related to a prior request.
861
+ * @returns A function that emits the completion notification when awaited.
862
+ */
863
+ createElicitationCompletionNotifier(elicitationId, options) {
864
+ if (!this._clientCapabilities?.elicitation?.url) throw new SdkError(SdkErrorCode.CapabilityNotSupported, "Client does not support URL elicitation (required for notifications/elicitation/complete)");
865
+ return () => this.notification({
866
+ method: "notifications/elicitation/complete",
867
+ params: { elicitationId }
868
+ }, options);
869
+ }
870
+ async listRoots(params, options) {
871
+ return this._requestWithSchema({
872
+ method: "roots/list",
873
+ params
874
+ }, ListRootsResultSchema, options);
875
+ }
876
+ /**
877
+ * Sends a logging message to the client, if connected.
878
+ * Note: You only need to send the parameters object, not the entire JSON-RPC message.
879
+ * @see {@linkcode LoggingMessageNotification}
880
+ * @param params
881
+ * @param sessionId Optional for stateless transports and backward compatibility.
882
+ */
883
+ async sendLoggingMessage(params, sessionId) {
884
+ if (this._capabilities.logging && !this.isMessageIgnored(params.level, sessionId)) return this.notification({
885
+ method: "notifications/message",
886
+ params
887
+ });
888
+ }
889
+ async sendResourceUpdated(params) {
890
+ return this.notification({
891
+ method: "notifications/resources/updated",
892
+ params
893
+ });
894
+ }
895
+ async sendResourceListChanged() {
896
+ return this.notification({ method: "notifications/resources/list_changed" });
897
+ }
898
+ async sendToolListChanged() {
899
+ return this.notification({ method: "notifications/tools/list_changed" });
900
+ }
901
+ async sendPromptListChanged() {
902
+ return this.notification({ method: "notifications/prompts/list_changed" });
903
+ }
904
+ };
905
+
906
+ //#endregion
907
+ //#region src/server/mcp.ts
908
+ /**
909
+ * High-level MCP server that provides a simpler API for working with resources, tools, and prompts.
910
+ * For advanced usage (like sending notifications or setting custom request handlers), use the underlying
911
+ * {@linkcode Server} instance available via the {@linkcode McpServer.server | server} property.
912
+ *
913
+ * @example
914
+ * ```ts source="./mcp.examples.ts#McpServer_basicUsage"
915
+ * const server = new McpServer({
916
+ * name: 'my-server',
917
+ * version: '1.0.0'
918
+ * });
919
+ * ```
920
+ */
921
+ var McpServer = class {
922
+ /**
923
+ * The underlying {@linkcode Server} instance, useful for advanced operations like sending notifications.
924
+ */
925
+ server;
926
+ _registeredResources = {};
927
+ _registeredResourceTemplates = {};
928
+ _registeredTools = {};
929
+ _registeredPrompts = {};
930
+ _experimental;
931
+ constructor(serverInfo, options) {
932
+ this.server = new Server(serverInfo, options);
933
+ }
934
+ /**
935
+ * Access experimental features.
936
+ *
937
+ * WARNING: These APIs are experimental and may change without notice.
938
+ *
939
+ * @experimental
940
+ */
941
+ get experimental() {
942
+ if (!this._experimental) this._experimental = { tasks: new ExperimentalMcpServerTasks(this) };
943
+ return this._experimental;
944
+ }
945
+ /**
946
+ * Attaches to the given transport, starts it, and starts listening for messages.
947
+ *
948
+ * The `server` object assumes ownership of the {@linkcode Transport}, replacing any callbacks that have already been set, and expects that it is the only user of the {@linkcode Transport} instance going forward.
949
+ *
950
+ * @example
951
+ * ```ts source="./mcp.examples.ts#McpServer_connect_stdio"
952
+ * const server = new McpServer({ name: 'my-server', version: '1.0.0' });
953
+ * const transport = new StdioServerTransport();
954
+ * await server.connect(transport);
955
+ * ```
956
+ */
957
+ async connect(transport) {
958
+ return await this.server.connect(transport);
959
+ }
960
+ /**
961
+ * Closes the connection.
962
+ */
963
+ async close() {
964
+ await this.server.close();
965
+ }
966
+ _toolHandlersInitialized = false;
967
+ setToolRequestHandlers() {
968
+ if (this._toolHandlersInitialized) return;
969
+ this.server.assertCanSetRequestHandler("tools/list");
970
+ this.server.assertCanSetRequestHandler("tools/call");
971
+ this.server.registerCapabilities({ tools: { listChanged: this.server.getCapabilities().tools?.listChanged ?? true } });
972
+ this.server.setRequestHandler("tools/list", () => ({ tools: Object.entries(this._registeredTools).filter(([, tool]) => tool.enabled).map(([name, tool]) => {
973
+ const toolDefinition = {
974
+ name,
975
+ title: tool.title,
976
+ description: tool.description,
977
+ inputSchema: tool.inputSchema ? standardSchemaToJsonSchema(tool.inputSchema, "input") : EMPTY_OBJECT_JSON_SCHEMA,
978
+ annotations: tool.annotations,
979
+ execution: tool.execution,
980
+ _meta: tool._meta
981
+ };
982
+ if (tool.outputSchema) toolDefinition.outputSchema = standardSchemaToJsonSchema(tool.outputSchema, "output");
983
+ return toolDefinition;
984
+ }) }));
985
+ this.server.setRequestHandler("tools/call", async (request, ctx) => {
986
+ const tool = this._registeredTools[request.params.name];
987
+ if (!tool) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Tool ${request.params.name} not found`);
988
+ if (!tool.enabled) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Tool ${request.params.name} disabled`);
989
+ try {
990
+ const isTaskRequest = !!request.params.task;
991
+ const taskSupport = tool.execution?.taskSupport;
992
+ const isTaskHandler = "createTask" in tool.handler;
993
+ if ((taskSupport === "required" || taskSupport === "optional") && !isTaskHandler) throw new ProtocolError(ProtocolErrorCode.InternalError, `Tool ${request.params.name} has taskSupport '${taskSupport}' but was not registered with registerToolTask`);
994
+ if (taskSupport === "required" && !isTaskRequest) throw new ProtocolError(ProtocolErrorCode.MethodNotFound, `Tool ${request.params.name} requires task augmentation (taskSupport: 'required')`);
995
+ if (taskSupport === "optional" && !isTaskRequest && isTaskHandler) return await this.handleAutomaticTaskPolling(tool, request, ctx);
996
+ const args = await this.validateToolInput(tool, request.params.arguments, request.params.name);
997
+ const result = await this.executeToolHandler(tool, args, ctx);
998
+ if (isTaskRequest) return result;
999
+ await this.validateToolOutput(tool, result, request.params.name);
1000
+ return result;
1001
+ } catch (error) {
1002
+ if (error instanceof ProtocolError && error.code === ProtocolErrorCode.UrlElicitationRequired) throw error;
1003
+ return this.createToolError(error instanceof Error ? error.message : String(error));
1004
+ }
1005
+ });
1006
+ this._toolHandlersInitialized = true;
1007
+ }
1008
+ /**
1009
+ * Creates a tool error result.
1010
+ *
1011
+ * @param errorMessage - The error message.
1012
+ * @returns The tool error result.
1013
+ */
1014
+ createToolError(errorMessage) {
1015
+ return {
1016
+ content: [{
1017
+ type: "text",
1018
+ text: errorMessage
1019
+ }],
1020
+ isError: true
1021
+ };
1022
+ }
1023
+ /**
1024
+ * Validates tool input arguments against the tool's input schema.
1025
+ */
1026
+ async validateToolInput(tool, args, toolName) {
1027
+ if (!tool.inputSchema) return;
1028
+ const parseResult = await validateStandardSchema(tool.inputSchema, args ?? {});
1029
+ if (!parseResult.success) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${parseResult.error}`);
1030
+ return parseResult.data;
1031
+ }
1032
+ /**
1033
+ * Validates tool output against the tool's output schema.
1034
+ */
1035
+ async validateToolOutput(tool, result, toolName) {
1036
+ if (!tool.outputSchema) return;
1037
+ if (!("content" in result)) return;
1038
+ if (result.isError) return;
1039
+ if (!result.structuredContent) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Output validation error: Tool ${toolName} has an output schema but no structured content was provided`);
1040
+ const parseResult = await validateStandardSchema(tool.outputSchema, result.structuredContent);
1041
+ if (!parseResult.success) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${parseResult.error}`);
1042
+ }
1043
+ /**
1044
+ * Executes a tool handler (either regular or task-based).
1045
+ */
1046
+ async executeToolHandler(tool, args, ctx) {
1047
+ return tool.executor(args, ctx);
1048
+ }
1049
+ /**
1050
+ * Handles automatic task polling for tools with `taskSupport` `'optional'`.
1051
+ */
1052
+ async handleAutomaticTaskPolling(tool, request, ctx) {
1053
+ if (!ctx.task?.store) throw new Error("No task store provided for task-capable tool.");
1054
+ const args = await this.validateToolInput(tool, request.params.arguments, request.params.name);
1055
+ const createTaskResult = await tool.executor(args, ctx);
1056
+ const taskId = createTaskResult.task.taskId;
1057
+ let task = createTaskResult.task;
1058
+ const pollInterval = task.pollInterval ?? 5e3;
1059
+ while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
1060
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
1061
+ const updatedTask = await ctx.task.store.getTask(taskId);
1062
+ if (!updatedTask) throw new ProtocolError(ProtocolErrorCode.InternalError, `Task ${taskId} not found during polling`);
1063
+ task = updatedTask;
1064
+ }
1065
+ return await ctx.task.store.getTaskResult(taskId);
1066
+ }
1067
+ _completionHandlerInitialized = false;
1068
+ setCompletionRequestHandler() {
1069
+ if (this._completionHandlerInitialized) return;
1070
+ this.server.assertCanSetRequestHandler("completion/complete");
1071
+ this.server.registerCapabilities({ completions: {} });
1072
+ this.server.setRequestHandler("completion/complete", async (request) => {
1073
+ switch (request.params.ref.type) {
1074
+ case "ref/prompt":
1075
+ assertCompleteRequestPrompt(request);
1076
+ return this.handlePromptCompletion(request, request.params.ref);
1077
+ case "ref/resource":
1078
+ assertCompleteRequestResourceTemplate(request);
1079
+ return this.handleResourceCompletion(request, request.params.ref);
1080
+ default: throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid completion reference: ${request.params.ref}`);
1081
+ }
1082
+ });
1083
+ this._completionHandlerInitialized = true;
1084
+ }
1085
+ async handlePromptCompletion(request, ref) {
1086
+ const prompt = this._registeredPrompts[ref.name];
1087
+ if (!prompt) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Prompt ${ref.name} not found`);
1088
+ if (!prompt.enabled) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Prompt ${ref.name} disabled`);
1089
+ if (!prompt.argsSchema) return EMPTY_COMPLETION_RESULT;
1090
+ const field = unwrapOptionalSchema(getSchemaShape(prompt.argsSchema)?.[request.params.argument.name]);
1091
+ if (!isCompletable(field)) return EMPTY_COMPLETION_RESULT;
1092
+ const completer = getCompleter(field);
1093
+ if (!completer) return EMPTY_COMPLETION_RESULT;
1094
+ return createCompletionResult(await completer(request.params.argument.value, request.params.context));
1095
+ }
1096
+ async handleResourceCompletion(request, ref) {
1097
+ const template = Object.values(this._registeredResourceTemplates).find((t) => t.resourceTemplate.uriTemplate.toString() === ref.uri);
1098
+ if (!template) {
1099
+ if (this._registeredResources[ref.uri]) return EMPTY_COMPLETION_RESULT;
1100
+ throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Resource template ${request.params.ref.uri} not found`);
1101
+ }
1102
+ const completer = template.resourceTemplate.completeCallback(request.params.argument.name);
1103
+ if (!completer) return EMPTY_COMPLETION_RESULT;
1104
+ return createCompletionResult(await completer(request.params.argument.value, request.params.context));
1105
+ }
1106
+ _resourceHandlersInitialized = false;
1107
+ setResourceRequestHandlers() {
1108
+ if (this._resourceHandlersInitialized) return;
1109
+ this.server.assertCanSetRequestHandler("resources/list");
1110
+ this.server.assertCanSetRequestHandler("resources/templates/list");
1111
+ this.server.assertCanSetRequestHandler("resources/read");
1112
+ this.server.registerCapabilities({ resources: { listChanged: this.server.getCapabilities().resources?.listChanged ?? true } });
1113
+ this.server.setRequestHandler("resources/list", async (_request, ctx) => {
1114
+ const resources = Object.entries(this._registeredResources).filter(([_, resource]) => resource.enabled).map(([uri, resource]) => ({
1115
+ uri,
1116
+ name: resource.name,
1117
+ ...resource.metadata
1118
+ }));
1119
+ const templateResources = [];
1120
+ for (const template of Object.values(this._registeredResourceTemplates)) {
1121
+ if (!template.resourceTemplate.listCallback) continue;
1122
+ const result = await template.resourceTemplate.listCallback(ctx);
1123
+ for (const resource of result.resources) templateResources.push({
1124
+ ...template.metadata,
1125
+ ...resource
1126
+ });
1127
+ }
1128
+ return { resources: [...resources, ...templateResources] };
1129
+ });
1130
+ this.server.setRequestHandler("resources/templates/list", async () => {
1131
+ return { resourceTemplates: Object.entries(this._registeredResourceTemplates).map(([name, template]) => ({
1132
+ name,
1133
+ uriTemplate: template.resourceTemplate.uriTemplate.toString(),
1134
+ ...template.metadata
1135
+ })) };
1136
+ });
1137
+ this.server.setRequestHandler("resources/read", async (request, ctx) => {
1138
+ const uri = new URL(request.params.uri);
1139
+ const resource = this._registeredResources[uri.toString()];
1140
+ if (resource) {
1141
+ if (!resource.enabled) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Resource ${uri} disabled`);
1142
+ return resource.readCallback(uri, ctx);
1143
+ }
1144
+ for (const template of Object.values(this._registeredResourceTemplates)) {
1145
+ const variables = template.resourceTemplate.uriTemplate.match(uri.toString());
1146
+ if (variables) return template.readCallback(uri, variables, ctx);
1147
+ }
1148
+ throw new ProtocolError(ProtocolErrorCode.ResourceNotFound, `Resource ${uri} not found`);
1149
+ });
1150
+ this._resourceHandlersInitialized = true;
1151
+ }
1152
+ _promptHandlersInitialized = false;
1153
+ setPromptRequestHandlers() {
1154
+ if (this._promptHandlersInitialized) return;
1155
+ this.server.assertCanSetRequestHandler("prompts/list");
1156
+ this.server.assertCanSetRequestHandler("prompts/get");
1157
+ this.server.registerCapabilities({ prompts: { listChanged: this.server.getCapabilities().prompts?.listChanged ?? true } });
1158
+ this.server.setRequestHandler("prompts/list", () => ({ prompts: Object.entries(this._registeredPrompts).filter(([, prompt]) => prompt.enabled).map(([name, prompt]) => {
1159
+ return {
1160
+ name,
1161
+ title: prompt.title,
1162
+ description: prompt.description,
1163
+ arguments: prompt.argsSchema ? promptArgumentsFromStandardSchema(prompt.argsSchema) : void 0,
1164
+ _meta: prompt._meta
1165
+ };
1166
+ }) }));
1167
+ this.server.setRequestHandler("prompts/get", async (request, ctx) => {
1168
+ const prompt = this._registeredPrompts[request.params.name];
1169
+ if (!prompt) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Prompt ${request.params.name} not found`);
1170
+ if (!prompt.enabled) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Prompt ${request.params.name} disabled`);
1171
+ return prompt.handler(request.params.arguments, ctx);
1172
+ });
1173
+ this._promptHandlersInitialized = true;
1174
+ }
1175
+ registerResource(name, uriOrTemplate, config, readCallback) {
1176
+ if (typeof uriOrTemplate === "string") {
1177
+ if (this._registeredResources[uriOrTemplate]) throw new Error(`Resource ${uriOrTemplate} is already registered`);
1178
+ const registeredResource = this._createRegisteredResource(name, config.title, uriOrTemplate, config, readCallback);
1179
+ this.setResourceRequestHandlers();
1180
+ this.sendResourceListChanged();
1181
+ return registeredResource;
1182
+ } else {
1183
+ if (this._registeredResourceTemplates[name]) throw new Error(`Resource template ${name} is already registered`);
1184
+ const registeredResourceTemplate = this._createRegisteredResourceTemplate(name, config.title, uriOrTemplate, config, readCallback);
1185
+ this.setResourceRequestHandlers();
1186
+ this.sendResourceListChanged();
1187
+ return registeredResourceTemplate;
1188
+ }
1189
+ }
1190
+ _createRegisteredResource(name, title, uri, metadata, readCallback) {
1191
+ const registeredResource = {
1192
+ name,
1193
+ title,
1194
+ metadata,
1195
+ readCallback,
1196
+ enabled: true,
1197
+ disable: () => registeredResource.update({ enabled: false }),
1198
+ enable: () => registeredResource.update({ enabled: true }),
1199
+ remove: () => registeredResource.update({ uri: null }),
1200
+ update: (updates) => {
1201
+ if (updates.uri !== void 0 && updates.uri !== uri) {
1202
+ delete this._registeredResources[uri];
1203
+ if (updates.uri) this._registeredResources[updates.uri] = registeredResource;
1204
+ }
1205
+ if (updates.name !== void 0) registeredResource.name = updates.name;
1206
+ if (updates.title !== void 0) registeredResource.title = updates.title;
1207
+ if (updates.metadata !== void 0) registeredResource.metadata = updates.metadata;
1208
+ if (updates.callback !== void 0) registeredResource.readCallback = updates.callback;
1209
+ if (updates.enabled !== void 0) registeredResource.enabled = updates.enabled;
1210
+ this.sendResourceListChanged();
1211
+ }
1212
+ };
1213
+ this._registeredResources[uri] = registeredResource;
1214
+ return registeredResource;
1215
+ }
1216
+ _createRegisteredResourceTemplate(name, title, template, metadata, readCallback) {
1217
+ const registeredResourceTemplate = {
1218
+ resourceTemplate: template,
1219
+ title,
1220
+ metadata,
1221
+ readCallback,
1222
+ enabled: true,
1223
+ disable: () => registeredResourceTemplate.update({ enabled: false }),
1224
+ enable: () => registeredResourceTemplate.update({ enabled: true }),
1225
+ remove: () => registeredResourceTemplate.update({ name: null }),
1226
+ update: (updates) => {
1227
+ if (updates.name !== void 0 && updates.name !== name) {
1228
+ delete this._registeredResourceTemplates[name];
1229
+ if (updates.name) this._registeredResourceTemplates[updates.name] = registeredResourceTemplate;
1230
+ }
1231
+ if (updates.title !== void 0) registeredResourceTemplate.title = updates.title;
1232
+ if (updates.template !== void 0) registeredResourceTemplate.resourceTemplate = updates.template;
1233
+ if (updates.metadata !== void 0) registeredResourceTemplate.metadata = updates.metadata;
1234
+ if (updates.callback !== void 0) registeredResourceTemplate.readCallback = updates.callback;
1235
+ if (updates.enabled !== void 0) registeredResourceTemplate.enabled = updates.enabled;
1236
+ this.sendResourceListChanged();
1237
+ }
1238
+ };
1239
+ this._registeredResourceTemplates[name] = registeredResourceTemplate;
1240
+ const variableNames = template.uriTemplate.variableNames;
1241
+ if (Array.isArray(variableNames) && variableNames.some((v) => !!template.completeCallback(v))) this.setCompletionRequestHandler();
1242
+ return registeredResourceTemplate;
1243
+ }
1244
+ _createRegisteredPrompt(name, title, description, argsSchema, callback, _meta) {
1245
+ let currentArgsSchema = argsSchema;
1246
+ let currentCallback = callback;
1247
+ const registeredPrompt = {
1248
+ title,
1249
+ description,
1250
+ argsSchema,
1251
+ _meta,
1252
+ handler: createPromptHandler(name, argsSchema, callback),
1253
+ enabled: true,
1254
+ disable: () => registeredPrompt.update({ enabled: false }),
1255
+ enable: () => registeredPrompt.update({ enabled: true }),
1256
+ remove: () => registeredPrompt.update({ name: null }),
1257
+ update: (updates) => {
1258
+ if (updates.name !== void 0 && updates.name !== name) {
1259
+ delete this._registeredPrompts[name];
1260
+ if (updates.name) this._registeredPrompts[updates.name] = registeredPrompt;
1261
+ }
1262
+ if (updates.title !== void 0) registeredPrompt.title = updates.title;
1263
+ if (updates.description !== void 0) registeredPrompt.description = updates.description;
1264
+ if (updates._meta !== void 0) registeredPrompt._meta = updates._meta;
1265
+ let needsHandlerRegen = false;
1266
+ if (updates.argsSchema !== void 0) {
1267
+ registeredPrompt.argsSchema = updates.argsSchema;
1268
+ currentArgsSchema = updates.argsSchema;
1269
+ needsHandlerRegen = true;
1270
+ }
1271
+ if (updates.callback !== void 0) {
1272
+ currentCallback = updates.callback;
1273
+ needsHandlerRegen = true;
1274
+ }
1275
+ if (needsHandlerRegen) registeredPrompt.handler = createPromptHandler(name, currentArgsSchema, currentCallback);
1276
+ if (updates.enabled !== void 0) registeredPrompt.enabled = updates.enabled;
1277
+ this.sendPromptListChanged();
1278
+ }
1279
+ };
1280
+ this._registeredPrompts[name] = registeredPrompt;
1281
+ if (argsSchema) {
1282
+ const shape = getSchemaShape(argsSchema);
1283
+ if (shape) {
1284
+ if (Object.values(shape).some((field) => {
1285
+ return isCompletable(unwrapOptionalSchema(field));
1286
+ })) this.setCompletionRequestHandler();
1287
+ }
1288
+ }
1289
+ return registeredPrompt;
1290
+ }
1291
+ _createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, execution, _meta, handler) {
1292
+ validateAndWarnToolName(name);
1293
+ let currentHandler = handler;
1294
+ const registeredTool = {
1295
+ title,
1296
+ description,
1297
+ inputSchema,
1298
+ outputSchema,
1299
+ annotations,
1300
+ execution,
1301
+ _meta,
1302
+ handler,
1303
+ executor: createToolExecutor(inputSchema, handler),
1304
+ enabled: true,
1305
+ disable: () => registeredTool.update({ enabled: false }),
1306
+ enable: () => registeredTool.update({ enabled: true }),
1307
+ remove: () => registeredTool.update({ name: null }),
1308
+ update: (updates) => {
1309
+ if (updates.name !== void 0 && updates.name !== name) {
1310
+ if (typeof updates.name === "string") validateAndWarnToolName(updates.name);
1311
+ delete this._registeredTools[name];
1312
+ if (updates.name) this._registeredTools[updates.name] = registeredTool;
1313
+ }
1314
+ if (updates.title !== void 0) registeredTool.title = updates.title;
1315
+ if (updates.description !== void 0) registeredTool.description = updates.description;
1316
+ let needsExecutorRegen = false;
1317
+ if (updates.paramsSchema !== void 0) {
1318
+ registeredTool.inputSchema = updates.paramsSchema;
1319
+ needsExecutorRegen = true;
1320
+ }
1321
+ if (updates.callback !== void 0) {
1322
+ registeredTool.handler = updates.callback;
1323
+ currentHandler = updates.callback;
1324
+ needsExecutorRegen = true;
1325
+ }
1326
+ if (needsExecutorRegen) registeredTool.executor = createToolExecutor(registeredTool.inputSchema, currentHandler);
1327
+ if (updates.outputSchema !== void 0) registeredTool.outputSchema = updates.outputSchema;
1328
+ if (updates.annotations !== void 0) registeredTool.annotations = updates.annotations;
1329
+ if (updates._meta !== void 0) registeredTool._meta = updates._meta;
1330
+ if (updates.enabled !== void 0) registeredTool.enabled = updates.enabled;
1331
+ this.sendToolListChanged();
1332
+ }
1333
+ };
1334
+ this._registeredTools[name] = registeredTool;
1335
+ this.setToolRequestHandlers();
1336
+ this.sendToolListChanged();
1337
+ return registeredTool;
1338
+ }
1339
+ /**
1340
+ * Registers a tool with a config object and callback.
1341
+ *
1342
+ * @example
1343
+ * ```ts source="./mcp.examples.ts#McpServer_registerTool_basic"
1344
+ * server.registerTool(
1345
+ * 'calculate-bmi',
1346
+ * {
1347
+ * title: 'BMI Calculator',
1348
+ * description: 'Calculate Body Mass Index',
1349
+ * inputSchema: z.object({
1350
+ * weightKg: z.number(),
1351
+ * heightM: z.number()
1352
+ * }),
1353
+ * outputSchema: z.object({ bmi: z.number() })
1354
+ * },
1355
+ * async ({ weightKg, heightM }) => {
1356
+ * const output = { bmi: weightKg / (heightM * heightM) };
1357
+ * return {
1358
+ * content: [{ type: 'text', text: JSON.stringify(output) }],
1359
+ * structuredContent: output
1360
+ * };
1361
+ * }
1362
+ * );
1363
+ * ```
1364
+ */
1365
+ registerTool(name, config, cb) {
1366
+ if (this._registeredTools[name]) throw new Error(`Tool ${name} is already registered`);
1367
+ const { title, description, inputSchema, outputSchema, annotations, _meta } = config;
1368
+ return this._createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, { taskSupport: "forbidden" }, _meta, cb);
1369
+ }
1370
+ /**
1371
+ * Registers a prompt with a config object and callback.
1372
+ *
1373
+ * @example
1374
+ * ```ts source="./mcp.examples.ts#McpServer_registerPrompt_basic"
1375
+ * server.registerPrompt(
1376
+ * 'review-code',
1377
+ * {
1378
+ * title: 'Code Review',
1379
+ * description: 'Review code for best practices',
1380
+ * argsSchema: z.object({ code: z.string() })
1381
+ * },
1382
+ * ({ code }) => ({
1383
+ * messages: [
1384
+ * {
1385
+ * role: 'user' as const,
1386
+ * content: {
1387
+ * type: 'text' as const,
1388
+ * text: `Please review this code:\n\n${code}`
1389
+ * }
1390
+ * }
1391
+ * ]
1392
+ * })
1393
+ * );
1394
+ * ```
1395
+ */
1396
+ registerPrompt(name, config, cb) {
1397
+ if (this._registeredPrompts[name]) throw new Error(`Prompt ${name} is already registered`);
1398
+ const { title, description, argsSchema, _meta } = config;
1399
+ const registeredPrompt = this._createRegisteredPrompt(name, title, description, argsSchema, cb, _meta);
1400
+ this.setPromptRequestHandlers();
1401
+ this.sendPromptListChanged();
1402
+ return registeredPrompt;
1403
+ }
1404
+ /**
1405
+ * Checks if the server is connected to a transport.
1406
+ * @returns `true` if the server is connected
1407
+ */
1408
+ isConnected() {
1409
+ return this.server.transport !== void 0;
1410
+ }
1411
+ /**
1412
+ * Sends a logging message to the client, if connected.
1413
+ * Note: You only need to send the parameters object, not the entire JSON-RPC message.
1414
+ * @see {@linkcode LoggingMessageNotification}
1415
+ * @param params
1416
+ * @param sessionId Optional for stateless transports and backward compatibility.
1417
+ *
1418
+ * @example
1419
+ * ```ts source="./mcp.examples.ts#McpServer_sendLoggingMessage_basic"
1420
+ * await server.sendLoggingMessage({
1421
+ * level: 'info',
1422
+ * data: 'Processing complete'
1423
+ * });
1424
+ * ```
1425
+ */
1426
+ async sendLoggingMessage(params, sessionId) {
1427
+ return this.server.sendLoggingMessage(params, sessionId);
1428
+ }
1429
+ /**
1430
+ * Sends a resource list changed event to the client, if connected.
1431
+ */
1432
+ sendResourceListChanged() {
1433
+ if (this.isConnected()) this.server.sendResourceListChanged();
1434
+ }
1435
+ /**
1436
+ * Sends a tool list changed event to the client, if connected.
1437
+ */
1438
+ sendToolListChanged() {
1439
+ if (this.isConnected()) this.server.sendToolListChanged();
1440
+ }
1441
+ /**
1442
+ * Sends a prompt list changed event to the client, if connected.
1443
+ */
1444
+ sendPromptListChanged() {
1445
+ if (this.isConnected()) this.server.sendPromptListChanged();
1446
+ }
1447
+ };
1448
+ /**
1449
+ * A resource template combines a URI pattern with optional functionality to enumerate
1450
+ * all resources matching that pattern.
1451
+ */
1452
+ var ResourceTemplate = class {
1453
+ _uriTemplate;
1454
+ constructor(uriTemplate, _callbacks) {
1455
+ this._callbacks = _callbacks;
1456
+ this._uriTemplate = typeof uriTemplate === "string" ? new UriTemplate(uriTemplate) : uriTemplate;
1457
+ }
1458
+ /**
1459
+ * Gets the URI template pattern.
1460
+ */
1461
+ get uriTemplate() {
1462
+ return this._uriTemplate;
1463
+ }
1464
+ /**
1465
+ * Gets the list callback, if one was provided.
1466
+ */
1467
+ get listCallback() {
1468
+ return this._callbacks.list;
1469
+ }
1470
+ /**
1471
+ * Gets the callback for completing a specific URI template variable, if one was provided.
1472
+ */
1473
+ completeCallback(variable) {
1474
+ return this._callbacks.complete?.[variable];
1475
+ }
1476
+ };
1477
+ /**
1478
+ * Creates an executor that invokes the handler with the appropriate arguments.
1479
+ * When `inputSchema` is defined, the handler is called with `(args, ctx)`.
1480
+ * When `inputSchema` is undefined, the handler is called with just `(ctx)`.
1481
+ */
1482
+ function createToolExecutor(inputSchema, handler) {
1483
+ if ("createTask" in handler) {
1484
+ const taskHandler = handler;
1485
+ return async (args, ctx) => {
1486
+ if (!ctx.task?.store) throw new Error("No task store provided.");
1487
+ const taskCtx = {
1488
+ ...ctx,
1489
+ task: {
1490
+ store: ctx.task.store,
1491
+ requestedTtl: ctx.task?.requestedTtl
1492
+ }
1493
+ };
1494
+ if (inputSchema) return taskHandler.createTask(args, taskCtx);
1495
+ return taskHandler.createTask(taskCtx);
1496
+ };
1497
+ }
1498
+ if (inputSchema) {
1499
+ const callback$1 = handler;
1500
+ return async (args, ctx) => callback$1(args, ctx);
1501
+ }
1502
+ const callback = handler;
1503
+ return async (_args, ctx) => callback(ctx);
1504
+ }
1505
+ const EMPTY_OBJECT_JSON_SCHEMA = {
1506
+ type: "object",
1507
+ properties: {}
1508
+ };
1509
+ /**
1510
+ * Creates a type-safe prompt handler that captures the schema and callback in a closure.
1511
+ * This eliminates the need for type assertions at the call site.
1512
+ */
1513
+ function createPromptHandler(name, argsSchema, callback) {
1514
+ if (argsSchema) {
1515
+ const typedCallback = callback;
1516
+ return async (args, ctx) => {
1517
+ const parseResult = await validateStandardSchema(argsSchema, args);
1518
+ if (!parseResult.success) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid arguments for prompt ${name}: ${parseResult.error}`);
1519
+ return typedCallback(parseResult.data, ctx);
1520
+ };
1521
+ } else {
1522
+ const typedCallback = callback;
1523
+ return async (_args, ctx) => {
1524
+ return typedCallback(ctx);
1525
+ };
1526
+ }
1527
+ }
1528
+ function createCompletionResult(suggestions) {
1529
+ return { completion: {
1530
+ values: suggestions.map(String).slice(0, 100),
1531
+ total: suggestions.length,
1532
+ hasMore: suggestions.length > 100
1533
+ } };
1534
+ }
1535
+ const EMPTY_COMPLETION_RESULT = { completion: {
1536
+ values: [],
1537
+ hasMore: false
1538
+ } };
1539
+ /** @internal Gets the shape of a Zod object schema */
1540
+ function getSchemaShape(schema) {
1541
+ const candidate = schema;
1542
+ if (candidate.shape && typeof candidate.shape === "object") return candidate.shape;
1543
+ }
1544
+ /** @internal Checks if a Zod schema is optional */
1545
+ function isOptionalSchema(schema) {
1546
+ return schema?.type === "optional";
1547
+ }
1548
+ /** @internal Unwraps an optional Zod schema */
1549
+ function unwrapOptionalSchema(schema) {
1550
+ if (!isOptionalSchema(schema)) return schema;
1551
+ return schema.def?.innerType ?? schema;
1552
+ }
1553
+
1554
+ //#endregion
1555
+ //#region src/server/middleware/hostHeaderValidation.ts
1556
+ /**
1557
+ * Parse and validate a `Host` header against an allowlist of hostnames (port-agnostic).
1558
+ *
1559
+ * - Input host header may include a port (e.g. `localhost:3000`) or IPv6 brackets (e.g. `[::1]:3000`).
1560
+ * - Allowlist items should be hostnames only (no ports). For IPv6, include brackets (e.g. `[::1]`).
1561
+ */
1562
+ function validateHostHeader(hostHeader, allowedHostnames) {
1563
+ if (!hostHeader) return {
1564
+ ok: false,
1565
+ errorCode: "missing_host",
1566
+ message: "Missing Host header"
1567
+ };
1568
+ let hostname;
1569
+ try {
1570
+ hostname = new URL(`http://${hostHeader}`).hostname;
1571
+ } catch {
1572
+ return {
1573
+ ok: false,
1574
+ errorCode: "invalid_host_header",
1575
+ message: `Invalid Host header: ${hostHeader}`,
1576
+ hostHeader
1577
+ };
1578
+ }
1579
+ if (!allowedHostnames.includes(hostname)) return {
1580
+ ok: false,
1581
+ errorCode: "invalid_host",
1582
+ message: `Invalid Host: ${hostname}`,
1583
+ hostHeader,
1584
+ hostname
1585
+ };
1586
+ return {
1587
+ ok: true,
1588
+ hostname
1589
+ };
1590
+ }
1591
+ /**
1592
+ * Convenience allowlist for `localhost` DNS rebinding protection.
1593
+ */
1594
+ function localhostAllowedHostnames() {
1595
+ return [
1596
+ "localhost",
1597
+ "127.0.0.1",
1598
+ "[::1]"
1599
+ ];
1600
+ }
1601
+ /**
1602
+ * Web-standard `Request` helper for DNS rebinding protection.
1603
+ * @example
1604
+ * ```ts source="./hostHeaderValidation.examples.ts#hostHeaderValidationResponse_basicUsage"
1605
+ * const result = validateHostHeader(req.headers.get('host'), ['localhost']);
1606
+ * ```
1607
+ */
1608
+ function hostHeaderValidationResponse(req, allowedHostnames) {
1609
+ const result = validateHostHeader(req.headers.get("host"), allowedHostnames);
1610
+ if (result.ok) return void 0;
1611
+ return Response.json({
1612
+ jsonrpc: "2.0",
1613
+ error: {
1614
+ code: -32e3,
1615
+ message: result.message
1616
+ },
1617
+ id: null
1618
+ }, {
1619
+ status: 403,
1620
+ headers: { "Content-Type": "application/json" }
1621
+ });
1622
+ }
1623
+
1624
+ //#endregion
1625
+ //#region src/server/stdio.ts
1626
+ /**
1627
+ * Server transport for stdio: this communicates with an MCP client by reading from the current process' `stdin` and writing to `stdout`.
1628
+ *
1629
+ * This transport is only available in Node.js environments.
1630
+ *
1631
+ * @example
1632
+ * ```ts source="./stdio.examples.ts#StdioServerTransport_basicUsage"
1633
+ * const server = new McpServer({ name: 'my-server', version: '1.0.0' });
1634
+ * const transport = new StdioServerTransport();
1635
+ * await server.connect(transport);
1636
+ * ```
1637
+ */
1638
+ var StdioServerTransport = class {
1639
+ _readBuffer = new ReadBuffer();
1640
+ _started = false;
1641
+ _closed = false;
1642
+ constructor(_stdin = process.stdin, _stdout = process.stdout) {
1643
+ this._stdin = _stdin;
1644
+ this._stdout = _stdout;
1645
+ }
1646
+ onclose;
1647
+ onerror;
1648
+ onmessage;
1649
+ _ondata = (chunk) => {
1650
+ this._readBuffer.append(chunk);
1651
+ this.processReadBuffer();
1652
+ };
1653
+ _onerror = (error) => {
1654
+ this.onerror?.(error);
1655
+ };
1656
+ _onstdouterror = (error) => {
1657
+ this.onerror?.(error);
1658
+ this.close().catch(() => {});
1659
+ };
1660
+ /**
1661
+ * Starts listening for messages on `stdin`.
1662
+ */
1663
+ async start() {
1664
+ if (this._started) throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
1665
+ this._started = true;
1666
+ this._stdin.on("data", this._ondata);
1667
+ this._stdin.on("error", this._onerror);
1668
+ this._stdout.on("error", this._onstdouterror);
1669
+ }
1670
+ processReadBuffer() {
1671
+ while (true) try {
1672
+ const message = this._readBuffer.readMessage();
1673
+ if (message === null) break;
1674
+ this.onmessage?.(message);
1675
+ } catch (error) {
1676
+ this.onerror?.(error);
1677
+ }
1678
+ }
1679
+ async close() {
1680
+ if (this._closed) return;
1681
+ this._closed = true;
1682
+ this._stdin.off("data", this._ondata);
1683
+ this._stdin.off("error", this._onerror);
1684
+ this._stdout.off("error", this._onstdouterror);
1685
+ if (this._stdin.listenerCount("data") === 0) this._stdin.pause();
1686
+ this._readBuffer.clear();
1687
+ this.onclose?.();
1688
+ }
1689
+ send(message) {
1690
+ if (this._closed) return Promise.reject(/* @__PURE__ */ new Error("StdioServerTransport is closed"));
1691
+ return new Promise((resolve, reject) => {
1692
+ const json = serializeMessage(message);
1693
+ let settled = false;
1694
+ const onError = (error) => {
1695
+ if (settled) return;
1696
+ settled = true;
1697
+ this._stdout.off("error", onError);
1698
+ this._stdout.off("drain", onDrain);
1699
+ reject(error);
1700
+ };
1701
+ const onDrain = () => {
1702
+ if (settled) return;
1703
+ settled = true;
1704
+ this._stdout.off("error", onError);
1705
+ this._stdout.off("drain", onDrain);
1706
+ resolve();
1707
+ };
1708
+ this._stdout.once("error", onError);
1709
+ if (this._stdout.write(json)) {
1710
+ if (settled) return;
1711
+ settled = true;
1712
+ this._stdout.off("error", onError);
1713
+ resolve();
1714
+ } else if (!settled) this._stdout.once("drain", onDrain);
1715
+ });
1716
+ }
1717
+ };
1718
+
1719
+ //#endregion
1720
+ //#region src/server/streamableHttp.ts
1721
+ /**
1722
+ * Server transport for Web Standards Streamable HTTP: this implements the MCP Streamable HTTP transport specification
1723
+ * using Web Standard APIs (`Request`, `Response`, `ReadableStream`).
1724
+ *
1725
+ * This transport works on any runtime that supports Web Standards: Node.js 18+, Cloudflare Workers, Deno, Bun, etc.
1726
+ *
1727
+ * In stateful mode:
1728
+ * - Session ID is generated and included in response headers
1729
+ * - Session ID is always included in initialization responses
1730
+ * - Requests with invalid session IDs are rejected with `404 Not Found`
1731
+ * - Non-initialization requests without a session ID are rejected with `400 Bad Request`
1732
+ * - State is maintained in-memory (connections, message history)
1733
+ *
1734
+ * In stateless mode:
1735
+ * - No Session ID is included in any responses
1736
+ * - No session validation is performed
1737
+ *
1738
+ * @example Stateful setup
1739
+ * ```ts source="./streamableHttp.examples.ts#WebStandardStreamableHTTPServerTransport_stateful"
1740
+ * const server = new McpServer({ name: 'my-server', version: '1.0.0' });
1741
+ *
1742
+ * const transport = new WebStandardStreamableHTTPServerTransport({
1743
+ * sessionIdGenerator: () => crypto.randomUUID()
1744
+ * });
1745
+ *
1746
+ * await server.connect(transport);
1747
+ * ```
1748
+ *
1749
+ * @example Stateless setup
1750
+ * ```ts source="./streamableHttp.examples.ts#WebStandardStreamableHTTPServerTransport_stateless"
1751
+ * const transport = new WebStandardStreamableHTTPServerTransport({
1752
+ * sessionIdGenerator: undefined
1753
+ * });
1754
+ * ```
1755
+ *
1756
+ * @example Hono.js
1757
+ * ```ts source="./streamableHttp.examples.ts#WebStandardStreamableHTTPServerTransport_hono"
1758
+ * app.all('/mcp', async c => {
1759
+ * return transport.handleRequest(c.req.raw);
1760
+ * });
1761
+ * ```
1762
+ *
1763
+ * @example Cloudflare Workers
1764
+ * ```ts source="./streamableHttp.examples.ts#WebStandardStreamableHTTPServerTransport_workers"
1765
+ * const worker = {
1766
+ * async fetch(request: Request): Promise<Response> {
1767
+ * return transport.handleRequest(request);
1768
+ * }
1769
+ * };
1770
+ * ```
1771
+ */
1772
+ var WebStandardStreamableHTTPServerTransport = class {
1773
+ sessionIdGenerator;
1774
+ _started = false;
1775
+ _streamMapping = /* @__PURE__ */ new Map();
1776
+ _requestToStreamMapping = /* @__PURE__ */ new Map();
1777
+ _requestResponseMap = /* @__PURE__ */ new Map();
1778
+ _initialized = false;
1779
+ _enableJsonResponse = false;
1780
+ _standaloneSseStreamId = "_GET_stream";
1781
+ _eventStore;
1782
+ _onsessioninitialized;
1783
+ _onsessionclosed;
1784
+ _allowedHosts;
1785
+ _allowedOrigins;
1786
+ _enableDnsRebindingProtection;
1787
+ _retryInterval;
1788
+ _supportedProtocolVersions;
1789
+ sessionId;
1790
+ onclose;
1791
+ onerror;
1792
+ onmessage;
1793
+ constructor(options = {}) {
1794
+ this.sessionIdGenerator = options.sessionIdGenerator;
1795
+ this._enableJsonResponse = options.enableJsonResponse ?? false;
1796
+ this._eventStore = options.eventStore;
1797
+ this._onsessioninitialized = options.onsessioninitialized;
1798
+ this._onsessionclosed = options.onsessionclosed;
1799
+ this._allowedHosts = options.allowedHosts;
1800
+ this._allowedOrigins = options.allowedOrigins;
1801
+ this._enableDnsRebindingProtection = options.enableDnsRebindingProtection ?? false;
1802
+ this._retryInterval = options.retryInterval;
1803
+ this._supportedProtocolVersions = options.supportedProtocolVersions ?? SUPPORTED_PROTOCOL_VERSIONS;
1804
+ }
1805
+ /**
1806
+ * Starts the transport. This is required by the {@linkcode Transport} interface but is a no-op
1807
+ * for the Streamable HTTP transport as connections are managed per-request.
1808
+ */
1809
+ async start() {
1810
+ if (this._started) throw new Error("Transport already started");
1811
+ this._started = true;
1812
+ }
1813
+ /**
1814
+ * Sets the supported protocol versions for header validation.
1815
+ * Called by the server during {@linkcode server/server.Server.connect | connect()} to pass its supported versions.
1816
+ */
1817
+ setSupportedProtocolVersions(versions) {
1818
+ this._supportedProtocolVersions = versions;
1819
+ }
1820
+ /**
1821
+ * Helper to create a JSON error response
1822
+ */
1823
+ createJsonErrorResponse(status, code, message, options) {
1824
+ const error = {
1825
+ code,
1826
+ message
1827
+ };
1828
+ if (options?.data !== void 0) error.data = options.data;
1829
+ return Response.json({
1830
+ jsonrpc: "2.0",
1831
+ error,
1832
+ id: null
1833
+ }, {
1834
+ status,
1835
+ headers: {
1836
+ "Content-Type": "application/json",
1837
+ ...options?.headers
1838
+ }
1839
+ });
1840
+ }
1841
+ /**
1842
+ * Validates request headers for DNS rebinding protection.
1843
+ * @returns Error response if validation fails, `undefined` if validation passes.
1844
+ */
1845
+ validateRequestHeaders(req) {
1846
+ if (!this._enableDnsRebindingProtection) return;
1847
+ if (this._allowedHosts && this._allowedHosts.length > 0) {
1848
+ const hostHeader = req.headers.get("host");
1849
+ if (!hostHeader || !this._allowedHosts.includes(hostHeader)) {
1850
+ const error = `Invalid Host header: ${hostHeader}`;
1851
+ this.onerror?.(new Error(error));
1852
+ return this.createJsonErrorResponse(403, -32e3, error);
1853
+ }
1854
+ }
1855
+ if (this._allowedOrigins && this._allowedOrigins.length > 0) {
1856
+ const originHeader = req.headers.get("origin");
1857
+ if (originHeader && !this._allowedOrigins.includes(originHeader)) {
1858
+ const error = `Invalid Origin header: ${originHeader}`;
1859
+ this.onerror?.(new Error(error));
1860
+ return this.createJsonErrorResponse(403, -32e3, error);
1861
+ }
1862
+ }
1863
+ }
1864
+ /**
1865
+ * Handles an incoming HTTP request, whether `GET`, `POST`, or `DELETE`
1866
+ * Returns a `Response` object (Web Standard)
1867
+ */
1868
+ async handleRequest(req, options) {
1869
+ const validationError = this.validateRequestHeaders(req);
1870
+ if (validationError) return validationError;
1871
+ switch (req.method) {
1872
+ case "POST": return this.handlePostRequest(req, options);
1873
+ case "GET": return this.handleGetRequest(req);
1874
+ case "DELETE": return this.handleDeleteRequest(req);
1875
+ default: return this.handleUnsupportedRequest();
1876
+ }
1877
+ }
1878
+ /**
1879
+ * Writes a priming event to establish resumption capability.
1880
+ * Only sends if `eventStore` is configured (opt-in for resumability) and
1881
+ * the client's protocol version supports empty SSE data (>= `2025-11-25`).
1882
+ */
1883
+ async writePrimingEvent(controller, encoder, streamId, protocolVersion) {
1884
+ if (!this._eventStore) return;
1885
+ if (protocolVersion < "2025-11-25") return;
1886
+ const primingEventId = await this._eventStore.storeEvent(streamId, {});
1887
+ let primingEvent = `id: ${primingEventId}\ndata: \n\n`;
1888
+ if (this._retryInterval !== void 0) primingEvent = `id: ${primingEventId}\nretry: ${this._retryInterval}\ndata: \n\n`;
1889
+ controller.enqueue(encoder.encode(primingEvent));
1890
+ }
1891
+ /**
1892
+ * Handles `GET` requests for SSE stream
1893
+ */
1894
+ async handleGetRequest(req) {
1895
+ if (!req.headers.get("accept")?.includes("text/event-stream")) {
1896
+ this.onerror?.(/* @__PURE__ */ new Error("Not Acceptable: Client must accept text/event-stream"));
1897
+ return this.createJsonErrorResponse(406, -32e3, "Not Acceptable: Client must accept text/event-stream");
1898
+ }
1899
+ const sessionError = this.validateSession(req);
1900
+ if (sessionError) return sessionError;
1901
+ const protocolError = this.validateProtocolVersion(req);
1902
+ if (protocolError) return protocolError;
1903
+ if (this._eventStore) {
1904
+ const lastEventId = req.headers.get("last-event-id");
1905
+ if (lastEventId) return this.replayEvents(lastEventId);
1906
+ }
1907
+ if (this._streamMapping.get(this._standaloneSseStreamId) !== void 0) {
1908
+ this.onerror?.(/* @__PURE__ */ new Error("Conflict: Only one SSE stream is allowed per session"));
1909
+ return this.createJsonErrorResponse(409, -32e3, "Conflict: Only one SSE stream is allowed per session");
1910
+ }
1911
+ const encoder = new TextEncoder();
1912
+ let streamController;
1913
+ const readable = new ReadableStream({
1914
+ start: (controller) => {
1915
+ streamController = controller;
1916
+ },
1917
+ cancel: () => {
1918
+ this._streamMapping.delete(this._standaloneSseStreamId);
1919
+ }
1920
+ });
1921
+ const headers = {
1922
+ "Content-Type": "text/event-stream",
1923
+ "Cache-Control": "no-cache, no-transform",
1924
+ Connection: "keep-alive"
1925
+ };
1926
+ if (this.sessionId !== void 0) headers["mcp-session-id"] = this.sessionId;
1927
+ this._streamMapping.set(this._standaloneSseStreamId, {
1928
+ controller: streamController,
1929
+ encoder,
1930
+ cleanup: () => {
1931
+ this._streamMapping.delete(this._standaloneSseStreamId);
1932
+ try {
1933
+ streamController.close();
1934
+ } catch {}
1935
+ }
1936
+ });
1937
+ return new Response(readable, { headers });
1938
+ }
1939
+ /**
1940
+ * Replays events that would have been sent after the specified event ID
1941
+ * Only used when resumability is enabled
1942
+ */
1943
+ async replayEvents(lastEventId) {
1944
+ if (!this._eventStore) {
1945
+ this.onerror?.(/* @__PURE__ */ new Error("Event store not configured"));
1946
+ return this.createJsonErrorResponse(400, -32e3, "Event store not configured");
1947
+ }
1948
+ try {
1949
+ let streamId;
1950
+ if (this._eventStore.getStreamIdForEventId) {
1951
+ streamId = await this._eventStore.getStreamIdForEventId(lastEventId);
1952
+ if (!streamId) {
1953
+ this.onerror?.(/* @__PURE__ */ new Error("Invalid event ID format"));
1954
+ return this.createJsonErrorResponse(400, -32e3, "Invalid event ID format");
1955
+ }
1956
+ if (this._streamMapping.get(streamId) !== void 0) {
1957
+ this.onerror?.(/* @__PURE__ */ new Error("Conflict: Stream already has an active connection"));
1958
+ return this.createJsonErrorResponse(409, -32e3, "Conflict: Stream already has an active connection");
1959
+ }
1960
+ }
1961
+ const headers = {
1962
+ "Content-Type": "text/event-stream",
1963
+ "Cache-Control": "no-cache, no-transform",
1964
+ Connection: "keep-alive"
1965
+ };
1966
+ if (this.sessionId !== void 0) headers["mcp-session-id"] = this.sessionId;
1967
+ const encoder = new TextEncoder();
1968
+ let streamController;
1969
+ const readable = new ReadableStream({
1970
+ start: (controller) => {
1971
+ streamController = controller;
1972
+ },
1973
+ cancel: () => {}
1974
+ });
1975
+ const replayedStreamId = await this._eventStore.replayEventsAfter(lastEventId, { send: async (eventId, message) => {
1976
+ if (!this.writeSSEEvent(streamController, encoder, message, eventId)) try {
1977
+ streamController.close();
1978
+ } catch {}
1979
+ } });
1980
+ this._streamMapping.set(replayedStreamId, {
1981
+ controller: streamController,
1982
+ encoder,
1983
+ cleanup: () => {
1984
+ this._streamMapping.delete(replayedStreamId);
1985
+ try {
1986
+ streamController.close();
1987
+ } catch {}
1988
+ }
1989
+ });
1990
+ return new Response(readable, { headers });
1991
+ } catch (error) {
1992
+ this.onerror?.(error);
1993
+ return this.createJsonErrorResponse(500, -32e3, "Error replaying events");
1994
+ }
1995
+ }
1996
+ /**
1997
+ * Writes an event to an SSE stream via controller with proper formatting
1998
+ */
1999
+ writeSSEEvent(controller, encoder, message, eventId) {
2000
+ try {
2001
+ let eventData = `event: message\n`;
2002
+ if (eventId) eventData += `id: ${eventId}\n`;
2003
+ eventData += `data: ${JSON.stringify(message)}\n\n`;
2004
+ controller.enqueue(encoder.encode(eventData));
2005
+ return true;
2006
+ } catch (error) {
2007
+ this.onerror?.(error);
2008
+ return false;
2009
+ }
2010
+ }
2011
+ /**
2012
+ * Handles unsupported requests (`PUT`, `PATCH`, etc.)
2013
+ */
2014
+ handleUnsupportedRequest() {
2015
+ this.onerror?.(/* @__PURE__ */ new Error("Method not allowed."));
2016
+ return Response.json({
2017
+ jsonrpc: "2.0",
2018
+ error: {
2019
+ code: -32e3,
2020
+ message: "Method not allowed."
2021
+ },
2022
+ id: null
2023
+ }, {
2024
+ status: 405,
2025
+ headers: {
2026
+ Allow: "GET, POST, DELETE",
2027
+ "Content-Type": "application/json"
2028
+ }
2029
+ });
2030
+ }
2031
+ /**
2032
+ * Handles `POST` requests containing JSON-RPC messages
2033
+ */
2034
+ async handlePostRequest(req, options) {
2035
+ try {
2036
+ const acceptHeader = req.headers.get("accept");
2037
+ if (!acceptHeader?.includes("application/json") || !acceptHeader.includes("text/event-stream")) {
2038
+ this.onerror?.(/* @__PURE__ */ new Error("Not Acceptable: Client must accept both application/json and text/event-stream"));
2039
+ return this.createJsonErrorResponse(406, -32e3, "Not Acceptable: Client must accept both application/json and text/event-stream");
2040
+ }
2041
+ const ct = req.headers.get("content-type");
2042
+ if (!ct || !ct.includes("application/json")) {
2043
+ this.onerror?.(/* @__PURE__ */ new Error("Unsupported Media Type: Content-Type must be application/json"));
2044
+ return this.createJsonErrorResponse(415, -32e3, "Unsupported Media Type: Content-Type must be application/json");
2045
+ }
2046
+ const request = req;
2047
+ let rawMessage;
2048
+ if (options?.parsedBody === void 0) try {
2049
+ rawMessage = await req.json();
2050
+ } catch (error) {
2051
+ this.onerror?.(error);
2052
+ return this.createJsonErrorResponse(400, -32700, "Parse error: Invalid JSON");
2053
+ }
2054
+ else rawMessage = options.parsedBody;
2055
+ let messages;
2056
+ try {
2057
+ messages = Array.isArray(rawMessage) ? rawMessage.map((msg) => JSONRPCMessageSchema.parse(msg)) : [JSONRPCMessageSchema.parse(rawMessage)];
2058
+ } catch (error) {
2059
+ this.onerror?.(error);
2060
+ return this.createJsonErrorResponse(400, -32700, "Parse error: Invalid JSON-RPC message");
2061
+ }
2062
+ const isInitializationRequest = messages.some((element) => isInitializeRequest(element));
2063
+ if (isInitializationRequest) {
2064
+ if (this._initialized && this.sessionId !== void 0) {
2065
+ this.onerror?.(/* @__PURE__ */ new Error("Invalid Request: Server already initialized"));
2066
+ return this.createJsonErrorResponse(400, -32600, "Invalid Request: Server already initialized");
2067
+ }
2068
+ if (messages.length > 1) {
2069
+ this.onerror?.(/* @__PURE__ */ new Error("Invalid Request: Only one initialization request is allowed"));
2070
+ return this.createJsonErrorResponse(400, -32600, "Invalid Request: Only one initialization request is allowed");
2071
+ }
2072
+ this.sessionId = this.sessionIdGenerator?.();
2073
+ this._initialized = true;
2074
+ if (this.sessionId && this._onsessioninitialized) await Promise.resolve(this._onsessioninitialized(this.sessionId));
2075
+ }
2076
+ if (!isInitializationRequest) {
2077
+ const sessionError = this.validateSession(req);
2078
+ if (sessionError) return sessionError;
2079
+ const protocolError = this.validateProtocolVersion(req);
2080
+ if (protocolError) return protocolError;
2081
+ }
2082
+ if (!messages.some((element) => isJSONRPCRequest(element))) {
2083
+ for (const message of messages) this.onmessage?.(message, {
2084
+ authInfo: options?.authInfo,
2085
+ request
2086
+ });
2087
+ return new Response(null, { status: 202 });
2088
+ }
2089
+ const streamId = crypto.randomUUID();
2090
+ const initRequest = messages.find((m) => isInitializeRequest(m));
2091
+ const clientProtocolVersion = initRequest ? initRequest.params.protocolVersion : req.headers.get("mcp-protocol-version") ?? DEFAULT_NEGOTIATED_PROTOCOL_VERSION;
2092
+ if (this._enableJsonResponse) return new Promise((resolve) => {
2093
+ this._streamMapping.set(streamId, {
2094
+ resolveJson: resolve,
2095
+ cleanup: () => {
2096
+ this._streamMapping.delete(streamId);
2097
+ }
2098
+ });
2099
+ for (const message of messages) if (isJSONRPCRequest(message)) this._requestToStreamMapping.set(message.id, streamId);
2100
+ for (const message of messages) this.onmessage?.(message, {
2101
+ authInfo: options?.authInfo,
2102
+ request
2103
+ });
2104
+ });
2105
+ const encoder = new TextEncoder();
2106
+ let streamController;
2107
+ const readable = new ReadableStream({
2108
+ start: (controller) => {
2109
+ streamController = controller;
2110
+ },
2111
+ cancel: () => {
2112
+ this._streamMapping.delete(streamId);
2113
+ }
2114
+ });
2115
+ const headers = {
2116
+ "Content-Type": "text/event-stream",
2117
+ "Cache-Control": "no-cache",
2118
+ Connection: "keep-alive"
2119
+ };
2120
+ if (this.sessionId !== void 0) headers["mcp-session-id"] = this.sessionId;
2121
+ for (const message of messages) if (isJSONRPCRequest(message)) {
2122
+ this._streamMapping.set(streamId, {
2123
+ controller: streamController,
2124
+ encoder,
2125
+ cleanup: () => {
2126
+ this._streamMapping.delete(streamId);
2127
+ try {
2128
+ streamController.close();
2129
+ } catch {}
2130
+ }
2131
+ });
2132
+ this._requestToStreamMapping.set(message.id, streamId);
2133
+ }
2134
+ await this.writePrimingEvent(streamController, encoder, streamId, clientProtocolVersion);
2135
+ for (const message of messages) {
2136
+ let closeSSEStream;
2137
+ let closeStandaloneSSEStream;
2138
+ if (isJSONRPCRequest(message) && this._eventStore && clientProtocolVersion >= "2025-11-25") {
2139
+ closeSSEStream = () => {
2140
+ this.closeSSEStream(message.id);
2141
+ };
2142
+ closeStandaloneSSEStream = () => {
2143
+ this.closeStandaloneSSEStream();
2144
+ };
2145
+ }
2146
+ this.onmessage?.(message, {
2147
+ authInfo: options?.authInfo,
2148
+ request,
2149
+ closeSSEStream,
2150
+ closeStandaloneSSEStream
2151
+ });
2152
+ }
2153
+ return new Response(readable, {
2154
+ status: 200,
2155
+ headers
2156
+ });
2157
+ } catch (error) {
2158
+ this.onerror?.(error);
2159
+ return this.createJsonErrorResponse(400, -32700, "Parse error", { data: String(error) });
2160
+ }
2161
+ }
2162
+ /**
2163
+ * Handles `DELETE` requests to terminate sessions
2164
+ */
2165
+ async handleDeleteRequest(req) {
2166
+ const sessionError = this.validateSession(req);
2167
+ if (sessionError) return sessionError;
2168
+ const protocolError = this.validateProtocolVersion(req);
2169
+ if (protocolError) return protocolError;
2170
+ await Promise.resolve(this._onsessionclosed?.(this.sessionId));
2171
+ await this.close();
2172
+ return new Response(null, { status: 200 });
2173
+ }
2174
+ /**
2175
+ * Validates session ID for non-initialization requests.
2176
+ * Returns `Response` error if invalid, `undefined` otherwise
2177
+ */
2178
+ validateSession(req) {
2179
+ if (this.sessionIdGenerator === void 0) return;
2180
+ if (!this._initialized) {
2181
+ this.onerror?.(/* @__PURE__ */ new Error("Bad Request: Server not initialized"));
2182
+ return this.createJsonErrorResponse(400, -32e3, "Bad Request: Server not initialized");
2183
+ }
2184
+ const sessionId = req.headers.get("mcp-session-id");
2185
+ if (!sessionId) {
2186
+ this.onerror?.(/* @__PURE__ */ new Error("Bad Request: Mcp-Session-Id header is required"));
2187
+ return this.createJsonErrorResponse(400, -32e3, "Bad Request: Mcp-Session-Id header is required");
2188
+ }
2189
+ if (sessionId !== this.sessionId) {
2190
+ this.onerror?.(/* @__PURE__ */ new Error("Session not found"));
2191
+ return this.createJsonErrorResponse(404, -32001, "Session not found");
2192
+ }
2193
+ }
2194
+ /**
2195
+ * Validates the `MCP-Protocol-Version` header on incoming requests.
2196
+ *
2197
+ * For initialization: Version negotiation handles unknown versions gracefully
2198
+ * (server responds with its supported version).
2199
+ *
2200
+ * For subsequent requests with `MCP-Protocol-Version` header:
2201
+ * - Accept if in supported list
2202
+ * - 400 if unsupported
2203
+ *
2204
+ * For HTTP requests without the `MCP-Protocol-Version` header:
2205
+ * - Accept and default to the version negotiated at initialization
2206
+ */
2207
+ validateProtocolVersion(req) {
2208
+ const protocolVersion = req.headers.get("mcp-protocol-version");
2209
+ if (protocolVersion !== null && !this._supportedProtocolVersions.includes(protocolVersion)) {
2210
+ const error = `Bad Request: Unsupported protocol version: ${protocolVersion} (supported versions: ${this._supportedProtocolVersions.join(", ")})`;
2211
+ this.onerror?.(new Error(error));
2212
+ return this.createJsonErrorResponse(400, -32e3, error);
2213
+ }
2214
+ }
2215
+ async close() {
2216
+ for (const { cleanup } of this._streamMapping.values()) cleanup();
2217
+ this._streamMapping.clear();
2218
+ this._requestResponseMap.clear();
2219
+ this.onclose?.();
2220
+ }
2221
+ /**
2222
+ * Close an SSE stream for a specific request, triggering client reconnection.
2223
+ * Use this to implement polling behavior during long-running operations -
2224
+ * client will reconnect after the retry interval specified in the priming event.
2225
+ */
2226
+ closeSSEStream(requestId) {
2227
+ const streamId = this._requestToStreamMapping.get(requestId);
2228
+ if (!streamId) return;
2229
+ const stream = this._streamMapping.get(streamId);
2230
+ if (stream) stream.cleanup();
2231
+ }
2232
+ /**
2233
+ * Close the standalone `GET` SSE stream, triggering client reconnection.
2234
+ * Use this to implement polling behavior for server-initiated notifications.
2235
+ */
2236
+ closeStandaloneSSEStream() {
2237
+ const stream = this._streamMapping.get(this._standaloneSseStreamId);
2238
+ if (stream) stream.cleanup();
2239
+ }
2240
+ async send(message, options) {
2241
+ let requestId = options?.relatedRequestId;
2242
+ if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) requestId = message.id;
2243
+ if (requestId === void 0) {
2244
+ if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) throw new Error("Cannot send a response on a standalone SSE stream unless resuming a previous client request");
2245
+ let eventId;
2246
+ if (this._eventStore) eventId = await this._eventStore.storeEvent(this._standaloneSseStreamId, message);
2247
+ const standaloneSse = this._streamMapping.get(this._standaloneSseStreamId);
2248
+ if (standaloneSse === void 0) return;
2249
+ if (standaloneSse.controller && standaloneSse.encoder) this.writeSSEEvent(standaloneSse.controller, standaloneSse.encoder, message, eventId);
2250
+ return;
2251
+ }
2252
+ const streamId = this._requestToStreamMapping.get(requestId);
2253
+ if (!streamId) throw new Error(`No connection established for request ID: ${String(requestId)}`);
2254
+ const stream = this._streamMapping.get(streamId);
2255
+ if (!this._enableJsonResponse && stream?.controller && stream?.encoder) {
2256
+ let eventId;
2257
+ if (this._eventStore) eventId = await this._eventStore.storeEvent(streamId, message);
2258
+ this.writeSSEEvent(stream.controller, stream.encoder, message, eventId);
2259
+ }
2260
+ if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) {
2261
+ this._requestResponseMap.set(requestId, message);
2262
+ const relatedIds = [...this._requestToStreamMapping.entries()].filter(([_, sid]) => sid === streamId).map(([id]) => id);
2263
+ if (relatedIds.every((id) => this._requestResponseMap.has(id))) {
2264
+ if (!stream) throw new Error(`No connection established for request ID: ${String(requestId)}`);
2265
+ if (this._enableJsonResponse && stream.resolveJson) {
2266
+ const headers = { "Content-Type": "application/json" };
2267
+ if (this.sessionId !== void 0) headers["mcp-session-id"] = this.sessionId;
2268
+ const responses = relatedIds.map((id) => this._requestResponseMap.get(id));
2269
+ if (responses.length === 1) stream.resolveJson(Response.json(responses[0], {
2270
+ status: 200,
2271
+ headers
2272
+ }));
2273
+ else stream.resolveJson(Response.json(responses, {
2274
+ status: 200,
2275
+ headers
2276
+ }));
2277
+ } else stream.cleanup();
2278
+ for (const id of relatedIds) {
2279
+ this._requestResponseMap.delete(id);
2280
+ this._requestToStreamMapping.delete(id);
2281
+ }
2282
+ }
2283
+ }
2284
+ }
2285
+ };
2286
+
2287
+ //#endregion
2288
+ //#region src/fromJsonSchema.ts
2289
+ let _defaultValidator;
2290
+ function fromJsonSchema(schema, validator) {
2291
+ return fromJsonSchema$1(schema, validator ?? (_defaultValidator ??= new DefaultJsonSchemaValidator()));
2292
+ }
2293
+
2294
+ //#endregion
2295
+ export { AjvJsonSchemaValidator, CfWorkerJsonSchemaValidator, DEFAULT_NEGOTIATED_PROTOCOL_VERSION, DEFAULT_REQUEST_TIMEOUT_MSEC, ExperimentalMcpServerTasks, ExperimentalServerTasks, INTERNAL_ERROR, INVALID_PARAMS, INVALID_REQUEST, InMemoryTaskMessageQueue, InMemoryTaskStore, JSONRPC_VERSION, LATEST_PROTOCOL_VERSION, METHOD_NOT_FOUND, McpServer, OAuthError, OAuthErrorCode, PARSE_ERROR, ProtocolError, ProtocolErrorCode, RELATED_TASK_META_KEY, ReadBuffer, ResourceTemplate, SUPPORTED_PROTOCOL_VERSIONS, SdkError, SdkErrorCode, Server, StdioServerTransport, UriTemplate, UrlElicitationRequiredError, WebStandardStreamableHTTPServerTransport, assertClientRequestTaskCapability, assertCompleteRequestPrompt, assertCompleteRequestResourceTemplate, assertToolsCallTaskCapability, checkResourceAllowed, completable, createFetchWithInit, deserializeMessage, fromJsonSchema, getDisplayName, hostHeaderValidationResponse, isCompletable, isInitializeRequest, isInitializedNotification, isJSONRPCErrorResponse, isJSONRPCNotification, isJSONRPCRequest, isJSONRPCResultResponse, isTaskAugmentedRequestParams, isTerminal, localhostAllowedHostnames, parseJSONRPCMessage, resourceUrlFromServerUrl, serializeMessage, takeResult, toArrayAsync, validateHostHeader };
2296
+ //# sourceMappingURL=index.mjs.map