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