tmcp 1.5.0 → 1.6.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/LICENSE +21 -0
- package/README.md +409 -23
- package/package.json +2 -1
- package/src/index.d.ts +31 -14
- package/src/index.js +316 -65
- package/src/internal/internal.d.ts +21 -8
- package/src/validation/index.d.ts +3864 -757
- package/src/validation/index.js +1287 -203
- package/src/validation/version.d.ts +85 -0
- package/src/validation/version.js +197 -0
package/src/index.js
CHANGED
|
@@ -1,25 +1,62 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
3
|
+
* @import SqidsType from "sqids";
|
|
3
4
|
* @import { JSONRPCRequest, JSONRPCParams } from "json-rpc-2.0";
|
|
4
5
|
* @import { ExtractURITemplateVariables } from "./internal/uri-template.js";
|
|
5
|
-
* @import { CallToolResult, ReadResourceResult, GetPromptResult, ClientCapabilities as ClientCapabilitiesType, JSONRPCRequest as JSONRPCRequestType, JSONRPCResponse,
|
|
6
|
-
* @import { Tool, Completion, Prompt,
|
|
6
|
+
* @import { CallToolResult, ReadResourceResult, GetPromptResult, ClientCapabilities as ClientCapabilitiesType, JSONRPCRequest as JSONRPCRequestType, JSONRPCResponse, CreateMessageRequestParams, CreateMessageResult, Resource, LoggingLevel } from "./validation/index.js";
|
|
7
|
+
* @import { Tool, Completion, Prompt, StoredResource, ServerOptions, ServerInfo, SubscriptionsKeys, McpEvents } from "./internal/internal.js";
|
|
7
8
|
*/
|
|
8
|
-
import {
|
|
9
|
+
import { JSONRPCClient, JSONRPCServer } from 'json-rpc-2.0';
|
|
10
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
9
11
|
import { UriTemplateMatcher } from 'uri-template-matcher';
|
|
12
|
+
import * as v from 'valibot';
|
|
10
13
|
import {
|
|
11
14
|
CallToolResultSchema,
|
|
12
|
-
ReadResourceResultSchema,
|
|
13
|
-
GetPromptResultSchema,
|
|
14
15
|
CompleteResultSchema,
|
|
15
|
-
|
|
16
|
+
CreateMessageRequestParamsSchema,
|
|
17
|
+
CreateMessageResultSchema,
|
|
18
|
+
GetPromptResultSchema,
|
|
19
|
+
InitializeRequestParamsSchema,
|
|
20
|
+
JSONRPCNotificationSchema,
|
|
16
21
|
JSONRPCRequestSchema,
|
|
17
22
|
JSONRPCResponseSchema,
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
McpError,
|
|
24
|
+
ReadResourceResultSchema,
|
|
20
25
|
} from './validation/index.js';
|
|
21
|
-
import {
|
|
22
|
-
|
|
26
|
+
import {
|
|
27
|
+
get_supported_versions,
|
|
28
|
+
negotiate_protocol_version,
|
|
29
|
+
} from './validation/version.js';
|
|
30
|
+
import { should_version_negotiation_fail } from './validation/version.js';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @type {SqidsType | undefined}
|
|
34
|
+
*/
|
|
35
|
+
let Sqids;
|
|
36
|
+
|
|
37
|
+
async function get_sqids() {
|
|
38
|
+
if (!Sqids) {
|
|
39
|
+
Sqids = new (await import('sqids')).default();
|
|
40
|
+
}
|
|
41
|
+
return Sqids;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Encode a cursor for pagination
|
|
46
|
+
* @param {number} offset
|
|
47
|
+
*/
|
|
48
|
+
async function encode_cursor(offset) {
|
|
49
|
+
return (await get_sqids()).encode([offset]);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Decode a cursor from pagination
|
|
54
|
+
* @param {string} cursor
|
|
55
|
+
*/
|
|
56
|
+
async function decode_cursor(cursor) {
|
|
57
|
+
const [decoded] = (await get_sqids()).decode(cursor);
|
|
58
|
+
return decoded;
|
|
59
|
+
}
|
|
23
60
|
|
|
24
61
|
/**
|
|
25
62
|
* @typedef {ClientCapabilitiesType} ClientCapabilities
|
|
@@ -44,7 +81,7 @@ export class McpServer {
|
|
|
44
81
|
*/
|
|
45
82
|
#prompts = new Map();
|
|
46
83
|
/**
|
|
47
|
-
* @type {Map<string,
|
|
84
|
+
* @type {Map<string, StoredResource>}
|
|
48
85
|
*/
|
|
49
86
|
#resources = new Map();
|
|
50
87
|
#templates = new UriTemplateMatcher();
|
|
@@ -82,6 +119,16 @@ export class McpServer {
|
|
|
82
119
|
*/
|
|
83
120
|
#client_capabilities_map = new Map();
|
|
84
121
|
|
|
122
|
+
/**
|
|
123
|
+
* @type {Map<string|undefined, string>}
|
|
124
|
+
*/
|
|
125
|
+
#negotiated_protocol_versions = new Map();
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* @type {Map<string|undefined, LoggingLevel>}
|
|
129
|
+
*/
|
|
130
|
+
#session_log_levels = new Map();
|
|
131
|
+
|
|
85
132
|
/**
|
|
86
133
|
* @param {ServerInfo} server_info
|
|
87
134
|
* @param {ServerOptions<StandardSchema>} options
|
|
@@ -89,28 +136,90 @@ export class McpServer {
|
|
|
89
136
|
constructor(server_info, options) {
|
|
90
137
|
this.#options = options;
|
|
91
138
|
this.#server.addMethod('initialize', (initialize_request) => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
139
|
+
try {
|
|
140
|
+
// Validate basic request format
|
|
141
|
+
const validated_initialize = v.parse(
|
|
142
|
+
InitializeRequestParamsSchema,
|
|
143
|
+
initialize_request,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const session_id = this.#session_id;
|
|
147
|
+
|
|
148
|
+
// Validate protocol version format
|
|
149
|
+
if (
|
|
150
|
+
should_version_negotiation_fail(
|
|
151
|
+
validated_initialize.protocolVersion,
|
|
152
|
+
)
|
|
153
|
+
) {
|
|
154
|
+
// Return JSON-RPC error for invalid protocol version format
|
|
155
|
+
const error = new McpError(
|
|
156
|
+
-32602,
|
|
157
|
+
'Invalid protocol version format',
|
|
158
|
+
);
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Negotiate protocol version
|
|
163
|
+
const negotiated_version = negotiate_protocol_version(
|
|
164
|
+
validated_initialize.protocolVersion,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
// Store negotiated protocol version
|
|
168
|
+
this.#negotiated_protocol_versions.set(
|
|
169
|
+
session_id,
|
|
170
|
+
negotiated_version,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
// Store client capabilities
|
|
174
|
+
this.#client_capabilities_map.set(
|
|
175
|
+
session_id,
|
|
176
|
+
validated_initialize.capabilities,
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// Dispatch initialization event
|
|
180
|
+
this.#event_target.dispatchEvent(
|
|
181
|
+
new CustomEvent('initialize', {
|
|
182
|
+
detail: validated_initialize,
|
|
183
|
+
}),
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// Trigger initial roots request if client supports it
|
|
187
|
+
if (validated_initialize.capabilities?.roots) {
|
|
188
|
+
this.#refresh_roots();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Return server response with negotiated version and capabilities
|
|
192
|
+
return {
|
|
193
|
+
protocolVersion: negotiated_version,
|
|
194
|
+
...options,
|
|
195
|
+
serverInfo: server_info,
|
|
196
|
+
};
|
|
197
|
+
} catch (error) {
|
|
198
|
+
// Enhanced error handling for initialization failures
|
|
199
|
+
if (error instanceof McpError) {
|
|
200
|
+
// Already has JSON-RPC error code, re-throw
|
|
201
|
+
throw error;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (
|
|
205
|
+
/** @type {Error} */ (error).message?.includes(
|
|
206
|
+
'Protocol version',
|
|
207
|
+
)
|
|
208
|
+
) {
|
|
209
|
+
const rpc_error = new McpError(
|
|
210
|
+
-32602,
|
|
211
|
+
`Protocol version validation failed: ${/** @type {Error} */ (error).message}. Server supports: ${get_supported_versions().join(', ')}`,
|
|
212
|
+
);
|
|
213
|
+
throw rpc_error;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// General initialization error
|
|
217
|
+
const rpc_error = new McpError(
|
|
218
|
+
-32603,
|
|
219
|
+
`Initialization failed: ${/** @type {Error} */ (error).message}`,
|
|
220
|
+
);
|
|
221
|
+
throw rpc_error;
|
|
108
222
|
}
|
|
109
|
-
return {
|
|
110
|
-
protocolVersion: validated_initialize.protocolVersion,
|
|
111
|
-
...options,
|
|
112
|
-
serverInfo: server_info,
|
|
113
|
-
};
|
|
114
223
|
});
|
|
115
224
|
this.#server.addMethod('ping', () => {
|
|
116
225
|
return {};
|
|
@@ -123,6 +232,7 @@ export class McpServer {
|
|
|
123
232
|
this.#init_resources();
|
|
124
233
|
this.#init_roots();
|
|
125
234
|
this.#init_completion();
|
|
235
|
+
this.#init_logging();
|
|
126
236
|
}
|
|
127
237
|
|
|
128
238
|
get #session_id() {
|
|
@@ -188,7 +298,7 @@ export class McpServer {
|
|
|
188
298
|
[...this.#tools].map(async ([name, tool]) => {
|
|
189
299
|
return {
|
|
190
300
|
name,
|
|
191
|
-
title: tool.description,
|
|
301
|
+
title: tool.title || tool.description,
|
|
192
302
|
description: tool.description,
|
|
193
303
|
inputSchema: tool.schema
|
|
194
304
|
? await this.#options.adapter.toJsonSchema(
|
|
@@ -232,8 +342,8 @@ export class McpServer {
|
|
|
232
342
|
*/
|
|
233
343
|
#init_prompts() {
|
|
234
344
|
if (!this.#options.capabilities?.prompts) return;
|
|
235
|
-
this.#server.addMethod('prompts/list', async () => {
|
|
236
|
-
const
|
|
345
|
+
this.#server.addMethod('prompts/list', async ({ cursor } = {}) => {
|
|
346
|
+
const all_prompts = await Promise.all(
|
|
237
347
|
[...this.#prompts].map(async ([name, prompt]) => {
|
|
238
348
|
const arguments_schema = prompt.schema
|
|
239
349
|
? await this.#options.adapter.toJsonSchema(
|
|
@@ -249,7 +359,7 @@ export class McpServer {
|
|
|
249
359
|
const required = arguments_schema.required ?? [];
|
|
250
360
|
return {
|
|
251
361
|
name,
|
|
252
|
-
title: prompt.description,
|
|
362
|
+
title: prompt.title || prompt.description,
|
|
253
363
|
description: prompt.description,
|
|
254
364
|
arguments: keys.map((key) => {
|
|
255
365
|
const property = arguments_schema.properties?.[key];
|
|
@@ -266,8 +376,26 @@ export class McpServer {
|
|
|
266
376
|
};
|
|
267
377
|
}),
|
|
268
378
|
);
|
|
379
|
+
|
|
380
|
+
const pagination_options = this.#options.pagination?.prompts;
|
|
381
|
+
if (!pagination_options || pagination_options.size == null) {
|
|
382
|
+
return { prompts: all_prompts };
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const page_length = pagination_options.size;
|
|
386
|
+
const offset = cursor ? await decode_cursor(cursor) : 0;
|
|
387
|
+
const start_index = offset;
|
|
388
|
+
const end_index = start_index + page_length;
|
|
389
|
+
|
|
390
|
+
const prompts = all_prompts.slice(start_index, end_index);
|
|
391
|
+
const has_next = end_index < all_prompts.length;
|
|
392
|
+
const next_cursor = has_next
|
|
393
|
+
? await encode_cursor(end_index)
|
|
394
|
+
: null;
|
|
395
|
+
|
|
269
396
|
return {
|
|
270
|
-
prompts
|
|
397
|
+
prompts,
|
|
398
|
+
...(next_cursor && { nextCursor: next_cursor }),
|
|
271
399
|
};
|
|
272
400
|
});
|
|
273
401
|
this.#server.addMethod(
|
|
@@ -316,37 +444,61 @@ export class McpServer {
|
|
|
316
444
|
});
|
|
317
445
|
}
|
|
318
446
|
|
|
319
|
-
this.#server.addMethod('resources/list', async () => {
|
|
447
|
+
this.#server.addMethod('resources/list', async ({ cursor } = {}) => {
|
|
448
|
+
const all_resources = [];
|
|
449
|
+
|
|
450
|
+
// Add static resources
|
|
451
|
+
for (const [uri, { description, name, title, ...resource }] of this
|
|
452
|
+
.#resources) {
|
|
453
|
+
if (!resource.template) {
|
|
454
|
+
all_resources.push({
|
|
455
|
+
name,
|
|
456
|
+
title: title || description,
|
|
457
|
+
description,
|
|
458
|
+
uri,
|
|
459
|
+
});
|
|
460
|
+
} else if (resource.list_resources) {
|
|
461
|
+
const template_resources = await resource.list_resources();
|
|
462
|
+
all_resources.push(...template_resources);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const pagination_options = this.#options.pagination?.resources;
|
|
467
|
+
if (!pagination_options || pagination_options.size == null) {
|
|
468
|
+
return { resources: all_resources };
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const page_length = pagination_options.size;
|
|
472
|
+
const offset = cursor ? await decode_cursor(cursor) : 0;
|
|
473
|
+
const start_index = offset;
|
|
474
|
+
const end_index = start_index + page_length;
|
|
475
|
+
|
|
476
|
+
const resources = all_resources.slice(start_index, end_index);
|
|
477
|
+
const has_next = end_index < all_resources.length;
|
|
478
|
+
const next_cursor = has_next
|
|
479
|
+
? await encode_cursor(end_index)
|
|
480
|
+
: null;
|
|
481
|
+
|
|
320
482
|
return {
|
|
321
|
-
resources
|
|
322
|
-
|
|
323
|
-
if (!template) {
|
|
324
|
-
arr.push({
|
|
325
|
-
name,
|
|
326
|
-
description,
|
|
327
|
-
uri,
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
return arr;
|
|
331
|
-
},
|
|
332
|
-
/** @type {Array<{name: string, description: string, uri: string}>} */ ([]),
|
|
333
|
-
),
|
|
483
|
+
resources,
|
|
484
|
+
...(next_cursor && { nextCursor: next_cursor }),
|
|
334
485
|
};
|
|
335
486
|
});
|
|
336
487
|
this.#server.addMethod('resources/templates/list', async () => {
|
|
337
488
|
return {
|
|
338
489
|
resourceTemplates: [...this.#resources].reduce(
|
|
339
|
-
(arr, [uri, { description, name, template }]) => {
|
|
490
|
+
(arr, [uri, { description, name, title, template }]) => {
|
|
340
491
|
if (template) {
|
|
341
492
|
arr.push({
|
|
342
493
|
name,
|
|
494
|
+
title: title || description,
|
|
343
495
|
description,
|
|
344
496
|
uriTemplate: uri,
|
|
345
497
|
});
|
|
346
498
|
}
|
|
347
499
|
return arr;
|
|
348
500
|
},
|
|
349
|
-
/** @type {Array<{name: string, description: string, uriTemplate: string}>} */ ([]),
|
|
501
|
+
/** @type {Array<{name: string, title: string, description: string, uriTemplate: string}>} */ ([]),
|
|
350
502
|
),
|
|
351
503
|
};
|
|
352
504
|
});
|
|
@@ -428,27 +580,39 @@ export class McpServer {
|
|
|
428
580
|
},
|
|
429
581
|
);
|
|
430
582
|
}
|
|
583
|
+
/**
|
|
584
|
+
*
|
|
585
|
+
*/
|
|
586
|
+
#init_logging() {
|
|
587
|
+
if (!this.#options.capabilities?.logging) return;
|
|
588
|
+
|
|
589
|
+
this.#server.addMethod('logging/setLevel', ({ level }) => {
|
|
590
|
+
this.#session_log_levels.set(this.#session_id, level);
|
|
591
|
+
return {};
|
|
592
|
+
});
|
|
593
|
+
}
|
|
431
594
|
/**
|
|
432
595
|
* @template {StandardSchema | undefined} [TSchema=undefined]
|
|
433
|
-
* @param {{ name: string; description: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never }} options
|
|
596
|
+
* @param {{ name: string; description: string; title?: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never }} options
|
|
434
597
|
* @param {TSchema extends undefined ? (()=>Promise<CallToolResult> | CallToolResult) : ((input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<CallToolResult> | CallToolResult)} execute
|
|
435
598
|
*/
|
|
436
|
-
tool({ name, description, schema }, execute) {
|
|
599
|
+
tool({ name, description, title, schema }, execute) {
|
|
437
600
|
if (this.#options.capabilities?.tools?.listChanged) {
|
|
438
601
|
this.#notify('notifications/tools/list_changed', {});
|
|
439
602
|
}
|
|
440
603
|
this.#tools.set(name, {
|
|
441
604
|
description,
|
|
605
|
+
title,
|
|
442
606
|
schema,
|
|
443
607
|
execute,
|
|
444
608
|
});
|
|
445
609
|
}
|
|
446
610
|
/**
|
|
447
611
|
* @template {StandardSchema | undefined} [TSchema=undefined]
|
|
448
|
-
* @param {{ name: string; description: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; complete?: NoInfer<TSchema extends undefined ? never : Partial<Record<keyof (StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>), Completion>>> }} options
|
|
612
|
+
* @param {{ name: string; description: string; title?: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; complete?: NoInfer<TSchema extends undefined ? never : Partial<Record<keyof (StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>), Completion>>> }} options
|
|
449
613
|
* @param {TSchema extends undefined ? (()=>Promise<GetPromptResult> | GetPromptResult) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<GetPromptResult> | GetPromptResult} execute
|
|
450
614
|
*/
|
|
451
|
-
prompt({ name, description, schema, complete }, execute) {
|
|
615
|
+
prompt({ name, description, title, schema, complete }, execute) {
|
|
452
616
|
if (complete) {
|
|
453
617
|
this.#completions['ref/prompt'].set(name, complete);
|
|
454
618
|
}
|
|
@@ -457,12 +621,13 @@ export class McpServer {
|
|
|
457
621
|
}
|
|
458
622
|
this.#prompts.set(name, {
|
|
459
623
|
description,
|
|
624
|
+
title,
|
|
460
625
|
schema,
|
|
461
626
|
execute,
|
|
462
627
|
});
|
|
463
628
|
}
|
|
464
629
|
/**
|
|
465
|
-
* @type {(resource:
|
|
630
|
+
* @type {(resource: StoredResource & {complete?: Partial<Record<string,Completion>>, uri: string})=> void}
|
|
466
631
|
*/
|
|
467
632
|
#resource({ uri, complete, ...resource }) {
|
|
468
633
|
if (complete) {
|
|
@@ -477,13 +642,14 @@ export class McpServer {
|
|
|
477
642
|
this.#resources.set(uri, resource);
|
|
478
643
|
}
|
|
479
644
|
/**
|
|
480
|
-
* @param {{ name: string; description: string; uri: string }} options
|
|
645
|
+
* @param {{ name: string; description: string; title?: string; uri: string }} options
|
|
481
646
|
* @param {(uri: string) => Promise<ReadResourceResult> | ReadResourceResult} execute
|
|
482
647
|
*/
|
|
483
|
-
resource({ name, description, uri }, execute) {
|
|
648
|
+
resource({ name, description, title, uri }, execute) {
|
|
484
649
|
this.#resource({
|
|
485
650
|
name,
|
|
486
651
|
description,
|
|
652
|
+
title,
|
|
487
653
|
uri,
|
|
488
654
|
execute,
|
|
489
655
|
template: false,
|
|
@@ -492,16 +658,21 @@ export class McpServer {
|
|
|
492
658
|
/**
|
|
493
659
|
* @template {string} TUri
|
|
494
660
|
* @template {ExtractURITemplateVariables<TUri>} TVariables
|
|
495
|
-
* @param {{ name: string; description: string; uri: TUri; complete?: NoInfer<TVariables extends never ? never : Partial<Record<TVariables, Completion
|
|
661
|
+
* @param {{ name: string; description: string; title?: string; uri: TUri; complete?: NoInfer<TVariables extends never ? never : Partial<Record<TVariables, Completion>>>; list?: () => Promise<Array<Resource>> | Array<Resource> }} options
|
|
496
662
|
* @param {(uri: string, params: Record<TVariables, string | string[]>) => Promise<ReadResourceResult> | ReadResourceResult} execute
|
|
497
663
|
*/
|
|
498
|
-
template(
|
|
664
|
+
template(
|
|
665
|
+
{ name, description, title, uri, complete, list: list_resources },
|
|
666
|
+
execute,
|
|
667
|
+
) {
|
|
499
668
|
this.#resource({
|
|
500
669
|
name,
|
|
501
670
|
description,
|
|
671
|
+
title,
|
|
502
672
|
uri,
|
|
503
673
|
execute,
|
|
504
674
|
complete,
|
|
675
|
+
list_resources,
|
|
505
676
|
template: true,
|
|
506
677
|
});
|
|
507
678
|
}
|
|
@@ -512,10 +683,25 @@ export class McpServer {
|
|
|
512
683
|
*/
|
|
513
684
|
receive(message, session_id) {
|
|
514
685
|
// Validate the message first
|
|
515
|
-
const validated_message = v.safeParse(
|
|
686
|
+
const validated_message = v.safeParse(
|
|
687
|
+
v.union([JSONRPCRequestSchema, JSONRPCNotificationSchema]),
|
|
688
|
+
message,
|
|
689
|
+
);
|
|
516
690
|
|
|
517
691
|
// Check if it's a request or response
|
|
518
692
|
if (validated_message.success) {
|
|
693
|
+
// we want to set a default log level for each session
|
|
694
|
+
// so that if the user calls `log` it can send a notification
|
|
695
|
+
// to each client that didn't explicitly set a log level too
|
|
696
|
+
if (
|
|
697
|
+
!!this.#options.capabilities?.logging &&
|
|
698
|
+
!this.#session_log_levels.has(session_id)
|
|
699
|
+
) {
|
|
700
|
+
this.#session_log_levels.set(
|
|
701
|
+
session_id,
|
|
702
|
+
this.#options.logging?.default ?? 'info',
|
|
703
|
+
);
|
|
704
|
+
}
|
|
519
705
|
return this.#session_storage.run(
|
|
520
706
|
session_id,
|
|
521
707
|
async () =>
|
|
@@ -593,7 +779,7 @@ export class McpServer {
|
|
|
593
779
|
|
|
594
780
|
/**
|
|
595
781
|
* Request language model sampling from the client
|
|
596
|
-
* @param {
|
|
782
|
+
* @param {CreateMessageRequestParams} request
|
|
597
783
|
* @returns {Promise<CreateMessageResult>}
|
|
598
784
|
*/
|
|
599
785
|
async message(request) {
|
|
@@ -603,7 +789,10 @@ export class McpServer {
|
|
|
603
789
|
this.#lazyily_create_client();
|
|
604
790
|
|
|
605
791
|
// Validate the request
|
|
606
|
-
const validated_request = v.parse(
|
|
792
|
+
const validated_request = v.parse(
|
|
793
|
+
CreateMessageRequestParamsSchema,
|
|
794
|
+
request,
|
|
795
|
+
);
|
|
607
796
|
|
|
608
797
|
// Make the request to the client
|
|
609
798
|
const response = await this.#client?.request(
|
|
@@ -617,4 +806,66 @@ export class McpServer {
|
|
|
617
806
|
// Validate and return the response
|
|
618
807
|
return v.parse(CreateMessageResultSchema, response);
|
|
619
808
|
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* Log a message to the client if logging is enabled and the level is appropriate
|
|
812
|
+
* @param {LoggingLevel} level
|
|
813
|
+
* @param {unknown} data
|
|
814
|
+
* @param {string} [logger]
|
|
815
|
+
*/
|
|
816
|
+
log(level, data, logger) {
|
|
817
|
+
if (!this.#options.capabilities?.logging) {
|
|
818
|
+
throw new Error(
|
|
819
|
+
"The server doesn't support logging, please enable it in capabilities",
|
|
820
|
+
);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
const sessions = [];
|
|
824
|
+
|
|
825
|
+
for (let [session_id, session_log_level] of this.#session_log_levels) {
|
|
826
|
+
// Check if the current log level should be sent based on severity
|
|
827
|
+
if (this.#should_log(level, session_log_level)) {
|
|
828
|
+
sessions.push(session_id);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
if (sessions.length === 0) return;
|
|
833
|
+
|
|
834
|
+
// Send the log notification to the client
|
|
835
|
+
this.#notify(
|
|
836
|
+
'notifications/message',
|
|
837
|
+
{
|
|
838
|
+
level,
|
|
839
|
+
data,
|
|
840
|
+
logger,
|
|
841
|
+
},
|
|
842
|
+
{
|
|
843
|
+
sessions,
|
|
844
|
+
},
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* Check if a log message should be sent based on severity levels
|
|
850
|
+
* @param {LoggingLevel} message_level
|
|
851
|
+
* @param {LoggingLevel} session_level
|
|
852
|
+
* @returns {boolean}
|
|
853
|
+
*/
|
|
854
|
+
#should_log(message_level, session_level) {
|
|
855
|
+
const levels = [
|
|
856
|
+
'debug',
|
|
857
|
+
'info',
|
|
858
|
+
'notice',
|
|
859
|
+
'warning',
|
|
860
|
+
'error',
|
|
861
|
+
'critical',
|
|
862
|
+
'alert',
|
|
863
|
+
'emergency',
|
|
864
|
+
];
|
|
865
|
+
const message_severity = levels.indexOf(message_level);
|
|
866
|
+
const session_severity = levels.indexOf(session_level);
|
|
867
|
+
|
|
868
|
+
// Send if message severity is equal to or higher than session level
|
|
869
|
+
return message_severity >= session_severity;
|
|
870
|
+
}
|
|
620
871
|
}
|
|
@@ -6,12 +6,16 @@ import {
|
|
|
6
6
|
CallToolResult,
|
|
7
7
|
ReadResourceResult,
|
|
8
8
|
CompleteResult,
|
|
9
|
-
|
|
9
|
+
InitializeRequestParams,
|
|
10
|
+
Resource,
|
|
11
|
+
ServerCapabilities,
|
|
12
|
+
LoggingLevel
|
|
10
13
|
} from '../validation/index.js';
|
|
11
14
|
|
|
12
15
|
export type Tool<TSchema extends StandardSchemaV1 = StandardSchemaV1<any>> = {
|
|
13
16
|
description: string;
|
|
14
17
|
schema: TSchema;
|
|
18
|
+
title?: string;
|
|
15
19
|
execute: (
|
|
16
20
|
input?: StandardSchemaV1.InferInput<TSchema>,
|
|
17
21
|
) => Promise<CallToolResult> | CallToolResult;
|
|
@@ -25,16 +29,21 @@ export type Completion = (
|
|
|
25
29
|
export type Prompt<TSchema extends StandardSchemaV1 = StandardSchemaV1<any>> = {
|
|
26
30
|
description: string;
|
|
27
31
|
schema: TSchema;
|
|
32
|
+
title?: string;
|
|
28
33
|
execute: (
|
|
29
34
|
input?: StandardSchemaV1.InferInput<TSchema>,
|
|
30
35
|
) => Promise<GetPromptResult> | GetPromptResult;
|
|
31
36
|
};
|
|
32
37
|
|
|
33
|
-
export type
|
|
38
|
+
export type StoredResource =
|
|
34
39
|
| {
|
|
35
40
|
description: string;
|
|
36
41
|
name: string;
|
|
37
42
|
template: true;
|
|
43
|
+
title?: string;
|
|
44
|
+
list_resources?: () =>
|
|
45
|
+
| Promise<Array<Resource>>
|
|
46
|
+
| Array<Resource>;
|
|
38
47
|
execute: (
|
|
39
48
|
uri: string,
|
|
40
49
|
params: Record<string, string | string[]>,
|
|
@@ -44,19 +53,23 @@ export type Resource =
|
|
|
44
53
|
description: string;
|
|
45
54
|
name: string;
|
|
46
55
|
template: false;
|
|
56
|
+
title?: string;
|
|
47
57
|
execute: (
|
|
48
58
|
uri: string,
|
|
49
59
|
) => Promise<ReadResourceResult> | ReadResourceResult;
|
|
50
60
|
};
|
|
51
61
|
|
|
52
62
|
export type ServerOptions<TSchema extends StandardSchemaV1> = {
|
|
53
|
-
capabilities?:
|
|
54
|
-
tools?: { listChanged?: boolean };
|
|
55
|
-
resources?: { listChanged?: boolean; subscribe?: boolean };
|
|
56
|
-
prompts?: { listChanged?: boolean };
|
|
57
|
-
};
|
|
63
|
+
capabilities?: ServerCapabilities;
|
|
58
64
|
instructions?: string;
|
|
59
65
|
adapter: JsonSchemaAdapter<TSchema>;
|
|
66
|
+
pagination?: {
|
|
67
|
+
resources?: { size?: number };
|
|
68
|
+
prompts?: { size?: number };
|
|
69
|
+
};
|
|
70
|
+
logging?: {
|
|
71
|
+
default: LoggingLevel;
|
|
72
|
+
}
|
|
60
73
|
};
|
|
61
74
|
|
|
62
75
|
export type ServerInfo = {
|
|
@@ -74,5 +87,5 @@ export type McpEvents = {
|
|
|
74
87
|
sessions?: string[] | undefined;
|
|
75
88
|
};
|
|
76
89
|
}) => void;
|
|
77
|
-
initialize: (initialize_request:
|
|
90
|
+
initialize: (initialize_request: InitializeRequestParams) => void;
|
|
78
91
|
};
|