tmcp 1.2.0 → 1.4.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/README.md CHANGED
@@ -1,3 +1,6 @@
1
+ > [!WARNING]
2
+ > Unfortunately i published the 1.0 by mistake...this package is currently under heavy development so there will be breaking changes in minors...threat this `1.x` as the `0.x` of any other package. Sorry for the disservice, every breaking will be properly labeled in the PR name.
3
+
1
4
  # tmcp
2
5
 
3
6
  A lightweight, schema-agnostic Model Context Protocol (MCP) server implementation with unified API design.
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "tmcp",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "The main tmcp library",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
7
7
  "keywords": [],
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "https://github.com/paoloricciuti/tmcp.git",
10
+ "url": "git+https://github.com/paoloricciuti/tmcp.git",
11
11
  "directory": "packages/tmcp"
12
12
  },
13
13
  "author": "Paolo Ricciuti",
@@ -34,12 +34,13 @@
34
34
  "devDependencies": {
35
35
  "@standard-schema/spec": "^1.0.0",
36
36
  "@types/json-schema": "^7.0.15",
37
+ "@types/node": "^24.0.13",
37
38
  "publint": "^0.3.12"
38
39
  },
39
40
  "scripts": {
40
41
  "test": "echo \"Error: no test specified\" && exit 1",
41
42
  "typecheck": "tsc --noEmit",
42
- "generate:types": "rm -f ./src/*.d.ts && tsc ./src/*.js --declaration --emitDeclarationOnly --target esnext --allowJs --moduleresolution nodenext --module nodenext && publint",
43
+ "generate:types": "find ./src -name \"*.d.ts\" -not -path \"./src/internal/*\" -delete && tsc ./src/*.js --declaration --emitDeclarationOnly --target esnext --allowJs --moduleresolution nodenext --module nodenext && publint",
43
44
  "prepublish": "pnpm generate:types"
44
45
  }
45
46
  }
package/src/index.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ /**
2
+ * @typedef {ClientCapabilitiesType} ClientCapabilities
3
+ */
1
4
  /**
2
5
  * @template {StandardSchemaV1} StandardSchema
3
6
  */
@@ -7,6 +10,21 @@ export class McpServer<StandardSchema extends StandardSchemaV1> {
7
10
  * @param {ServerOptions<StandardSchema>} options
8
11
  */
9
12
  constructor(server_info: ServerInfo, options: ServerOptions<StandardSchema>);
13
+ currentClientCapabilities(): {
14
+ roots?: {
15
+ listChanged?: boolean;
16
+ } & {
17
+ [key: string]: unknown;
18
+ };
19
+ sampling?: {} & {
20
+ [key: string]: unknown;
21
+ };
22
+ elicitation?: {} & {
23
+ [key: string]: unknown;
24
+ };
25
+ } & {
26
+ [key: string]: unknown;
27
+ };
10
28
  /**
11
29
  * @template {keyof McpEvents} TEvent
12
30
  * @param {TEvent} event
@@ -17,64 +35,76 @@ export class McpServer<StandardSchema extends StandardSchemaV1> {
17
35
  /**
18
36
  * @template {StandardSchema | undefined} [TSchema=undefined]
19
37
  * @param {{ name: string; description: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never }} options
20
- * @param {TSchema extends undefined ? (()=>Promise<unknown> | unknown) : ((input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<unknown> | unknown)} execute
38
+ * @param {TSchema extends undefined ? (()=>Promise<CallToolResult> | CallToolResult) : ((input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<CallToolResult> | CallToolResult)} execute
21
39
  */
22
40
  tool<TSchema extends StandardSchema | undefined = undefined>({ name, description, schema }: {
23
41
  name: string;
24
42
  description: string;
25
43
  schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never;
26
- }, execute: TSchema extends undefined ? (() => Promise<unknown> | unknown) : ((input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<unknown> | unknown)): void;
44
+ }, execute: TSchema extends undefined ? (() => Promise<CallToolResult> | CallToolResult) : ((input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<CallToolResult> | CallToolResult)): void;
27
45
  /**
28
46
  * @template {StandardSchema | undefined} [TSchema=undefined]
29
- * @param {{ name: string; description: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; complete?: TSchema extends undefined ? never : Partial<Record<keyof (StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>), Completion>> }} options
30
- * @param {TSchema extends undefined ? (()=>Promise<unknown> | unknown) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<unknown> | unknown} execute
47
+ * @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
48
+ * @param {TSchema extends undefined ? (()=>Promise<GetPromptResult> | GetPromptResult) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<GetPromptResult> | GetPromptResult} execute
31
49
  */
32
50
  prompt<TSchema extends StandardSchema | undefined = undefined>({ name, description, schema, complete }: {
33
51
  name: string;
34
52
  description: string;
35
53
  schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never;
36
- complete?: TSchema extends undefined ? never : Partial<Record<keyof StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>, Completion>>;
37
- }, execute: TSchema extends undefined ? (() => Promise<unknown> | unknown) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<unknown> | unknown): void;
54
+ complete?: NoInfer<TSchema extends undefined ? never : Partial<Record<keyof StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>, Completion>>>;
55
+ }, execute: TSchema extends undefined ? (() => Promise<GetPromptResult> | GetPromptResult) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<GetPromptResult> | GetPromptResult): void;
38
56
  /**
39
57
  * @param {{ name: string; description: string; uri: string }} options
40
- * @param {(uri: string) => Promise<unknown> | unknown} execute
58
+ * @param {(uri: string) => Promise<ReadResourceResult> | ReadResourceResult} execute
41
59
  */
42
60
  resource({ name, description, uri }: {
43
61
  name: string;
44
62
  description: string;
45
63
  uri: string;
46
- }, execute: (uri: string) => Promise<unknown> | unknown): void;
64
+ }, execute: (uri: string) => Promise<ReadResourceResult> | ReadResourceResult): void;
47
65
  /**
48
66
  * @template {string} TUri
49
67
  * @template {ExtractURITemplateVariables<TUri>} TVariables
50
- * @param {{ name: string; description: string; uri: TUri; complete?: TVariables extends never ? never : Partial<Record<TVariables, Completion>> }} options
51
- * @param {(uri: string, params: Record<TVariables, string | string[]>) => Promise<unknown> | unknown} execute
68
+ * @param {{ name: string; description: string; uri: TUri; complete?: NoInfer<TVariables extends never ? never : Partial<Record<TVariables, Completion>>> }} options
69
+ * @param {(uri: string, params: Record<TVariables, string | string[]>) => Promise<ReadResourceResult> | ReadResourceResult} execute
52
70
  */
53
71
  template<TUri extends string, TVariables extends ExtractURITemplateVariables<TUri>>({ name, description, uri, complete }: {
54
72
  name: string;
55
73
  description: string;
56
74
  uri: TUri;
57
- complete?: TVariables extends never ? never : Partial<Record<TVariables, Completion>>;
58
- }, execute: (uri: string, params: Record<TVariables, string | string[]>) => Promise<unknown> | unknown): void;
75
+ complete?: NoInfer<TVariables extends never ? never : Partial<Record<TVariables, Completion>>>;
76
+ }, execute: (uri: string, params: Record<TVariables, string | string[]>) => Promise<ReadResourceResult> | ReadResourceResult): void;
59
77
  /**
60
78
  * @param {JSONRPCRequest} request
79
+ * @param {string} [session_id]
61
80
  * @returns {ReturnType<JSONRPCServer['receive']>}
62
81
  */
63
- receive(request: JSONRPCRequest): ReturnType<JSONRPCServer["receive"]>;
82
+ receive(request: JSONRPCRequest, session_id?: string): ReturnType<JSONRPCServer["receive"]>;
64
83
  /**
65
84
  * Send a notification for subscriptions
66
85
  * @param {SubscriptionsKeys} what
67
86
  * @param {string} id
68
87
  */
69
88
  changed(what: SubscriptionsKeys, id: string): void;
89
+ /**
90
+ * @template {StandardSchema} TSchema
91
+ * @param {TSchema} schema
92
+ * @returns {Promise<StandardSchemaV1.InferOutput<TSchema>>}
93
+ */
94
+ elicitation<TSchema extends StandardSchema>(schema: TSchema): Promise<StandardSchemaV1.InferOutput<TSchema>>;
70
95
  #private;
71
96
  }
97
+ export type ClientCapabilities = ClientCapabilitiesType;
72
98
  import type { StandardSchemaV1 } from "@standard-schema/spec";
73
99
  import type { McpEvents } from "./internal/internal.js";
100
+ import type { CallToolResult } from "./validation/index.js";
74
101
  import type { Completion } from "./internal/internal.js";
102
+ import type { GetPromptResult } from "./validation/index.js";
103
+ import type { ReadResourceResult } from "./validation/index.js";
75
104
  import type { ExtractURITemplateVariables } from "./internal/uri-template.js";
76
105
  import type { JSONRPCRequest } from "json-rpc-2.0";
77
106
  import { JSONRPCServer } from 'json-rpc-2.0';
78
107
  import type { SubscriptionsKeys } from "./internal/internal.js";
79
108
  import type { ServerInfo } from "./internal/internal.js";
80
109
  import type { ServerOptions } from "./internal/internal.js";
110
+ import type { ClientCapabilities as ClientCapabilitiesType } from "./validation/index.js";
package/src/index.js CHANGED
@@ -2,10 +2,24 @@
2
2
  * @import { StandardSchemaV1 } from "@standard-schema/spec";
3
3
  * @import { JSONRPCRequest, JSONRPCParams } from "json-rpc-2.0";
4
4
  * @import { ExtractURITemplateVariables } from "./internal/uri-template.js";
5
+ * @import { CallToolResult, ReadResourceResult, GetPromptResult, ClientCapabilities as ClientCapabilitiesType } from "./validation/index.js";
5
6
  * @import { Tool, Completion, Prompt, Resource, ServerOptions, ServerInfo, SubscriptionsKeys, McpEvents } from "./internal/internal.js";
6
7
  */
7
8
  import { JSONRPCServer, JSONRPCClient } from 'json-rpc-2.0';
8
9
  import { UriTemplateMatcher } from 'uri-template-matcher';
10
+ import {
11
+ CallToolResultSchema,
12
+ ReadResourceResultSchema,
13
+ GetPromptResultSchema,
14
+ CompleteResultSchema,
15
+ InitializeRequestSchema,
16
+ } from './validation/index.js';
17
+ import { AsyncLocalStorage } from 'node:async_hooks';
18
+ import * as v from 'valibot';
19
+
20
+ /**
21
+ * @typedef {ClientCapabilitiesType} ClientCapabilities
22
+ */
9
23
 
10
24
  /**
11
25
  * @template {StandardSchemaV1} StandardSchema
@@ -13,7 +27,7 @@ import { UriTemplateMatcher } from 'uri-template-matcher';
13
27
  export class McpServer {
14
28
  #server = new JSONRPCServer();
15
29
  /**
16
- * @type {JSONRPCClient | undefined}
30
+ * @type {JSONRPCClient<Record<string, any> | undefined> | undefined}
17
31
  */
18
32
  #client;
19
33
  #options;
@@ -41,24 +55,47 @@ export class McpServer {
41
55
  #event_target = new EventTarget();
42
56
 
43
57
  /**
44
- * @type {Record<SubscriptionsKeys, Set<string>>}
58
+ * @type {Record<SubscriptionsKeys, Map<string, Set<string | undefined>>>}
45
59
  */
46
60
  #subscriptions = {
47
61
  /**
48
- * @type {Set<string>}
62
+ * @type {Map<string, Set<string>>}
49
63
  */
50
- resource: new Set(),
64
+ resource: new Map(),
51
65
  };
52
66
 
67
+ /**
68
+ * @type {AsyncLocalStorage<string | undefined>}
69
+ */
70
+ #session_storage = new AsyncLocalStorage();
71
+
72
+ /**
73
+ * @type {Map<string|undefined, ClientCapabilities>}
74
+ */
75
+ #client_capabilities_map = new Map();
76
+
53
77
  /**
54
78
  * @param {ServerInfo} server_info
55
79
  * @param {ServerOptions<StandardSchema>} options
56
80
  */
57
81
  constructor(server_info, options) {
58
82
  this.#options = options;
59
- this.#server.addMethod('initialize', ({ protocolVersion }) => {
83
+ this.#server.addMethod('initialize', (initialize_request) => {
84
+ const validated_initialize = v.parse(
85
+ InitializeRequestSchema,
86
+ initialize_request,
87
+ );
88
+ this.#client_capabilities_map.set(
89
+ this.#session_id,
90
+ validated_initialize.capabilities,
91
+ );
92
+ this.#event_target.dispatchEvent(
93
+ new CustomEvent('initialize', {
94
+ detail: validated_initialize,
95
+ }),
96
+ );
60
97
  return {
61
- protocolVersion,
98
+ protocolVersion: validated_initialize.protocolVersion,
62
99
  ...options,
63
100
  serverInfo: server_info,
64
101
  };
@@ -75,6 +112,30 @@ export class McpServer {
75
112
  this.#init_completion();
76
113
  }
77
114
 
115
+ get #session_id() {
116
+ return this.#session_storage.getStore();
117
+ }
118
+
119
+ get #client_capabilities() {
120
+ return this.#client_capabilities_map.get(this.#session_id);
121
+ }
122
+
123
+ currentClientCapabilities() {
124
+ return this.#client_capabilities;
125
+ }
126
+
127
+ #lazyily_create_client() {
128
+ if (!this.#client) {
129
+ this.#client = new JSONRPCClient((payload, context = {}) => {
130
+ this.#event_target.dispatchEvent(
131
+ new CustomEvent('send', {
132
+ detail: { request: payload, context },
133
+ }),
134
+ );
135
+ });
136
+ }
137
+ }
138
+
78
139
  /**
79
140
  * @template {keyof McpEvents} TEvent
80
141
  * @param {TEvent} event
@@ -82,12 +143,8 @@ export class McpServer {
82
143
  * @param {AddEventListenerOptions} [options]
83
144
  */
84
145
  on(event, callback, options) {
85
- if (event === 'send' && !this.#client) {
86
- this.#client = new JSONRPCClient((payload) => {
87
- this.#event_target.dispatchEvent(
88
- new CustomEvent('send', { detail: payload }),
89
- );
90
- });
146
+ if (event === 'send') {
147
+ this.#lazyily_create_client();
91
148
  }
92
149
 
93
150
  this.#event_target.addEventListener(
@@ -102,9 +159,10 @@ export class McpServer {
102
159
  /**
103
160
  * @param {string} method
104
161
  * @param {JSONRPCParams} [params]
162
+ * @param {Record<string, any>} [extra]
105
163
  */
106
- #notify(method, params) {
107
- this.#client?.notify(method, params);
164
+ #notify(method, params, extra) {
165
+ this.#client?.notify(method, params, extra);
108
166
  }
109
167
 
110
168
  /**
@@ -139,7 +197,7 @@ export class McpServer {
139
197
  throw new Error(`Tool ${name} not found`);
140
198
  }
141
199
  if (!tool.schema) {
142
- return tool.execute();
200
+ return v.parse(CallToolResultSchema, await tool.execute());
143
201
  }
144
202
  let validated_args = tool.schema['~standard'].validate(args);
145
203
  if (validated_args instanceof Promise)
@@ -149,7 +207,10 @@ export class McpServer {
149
207
  `Invalid arguments for tool ${name}: ${JSON.stringify(validated_args.issues)}`,
150
208
  );
151
209
  }
152
- return tool.execute(validated_args.value);
210
+ return v.parse(
211
+ CallToolResultSchema,
212
+ await tool.execute(validated_args.value),
213
+ );
153
214
  },
154
215
  );
155
216
  }
@@ -204,7 +265,10 @@ export class McpServer {
204
265
  throw new Error(`Tool ${name} not found`);
205
266
  }
206
267
  if (!prompt.schema) {
207
- return prompt.execute();
268
+ return v.parse(
269
+ GetPromptResultSchema,
270
+ await prompt.execute(),
271
+ );
208
272
  }
209
273
  let validated_args = prompt.schema['~standard'].validate(args);
210
274
  if (validated_args instanceof Promise)
@@ -214,7 +278,10 @@ export class McpServer {
214
278
  `Invalid arguments for tool ${name}: ${JSON.stringify(validated_args.issues)}`,
215
279
  );
216
280
  }
217
- return prompt.execute(validated_args.value);
281
+ return v.parse(
282
+ GetPromptResultSchema,
283
+ await prompt.execute(validated_args.value),
284
+ );
218
285
  },
219
286
  );
220
287
  }
@@ -226,7 +293,12 @@ export class McpServer {
226
293
 
227
294
  if (this.#options.capabilities?.resources?.subscribe) {
228
295
  this.#server.addMethod('resources/subscribe', async ({ uri }) => {
229
- this.#subscriptions.resource.add(uri);
296
+ let subscriptions = this.#subscriptions.resource.get(uri);
297
+ if (!subscriptions) {
298
+ subscriptions = new Set();
299
+ this.#subscriptions.resource.set(uri, subscriptions);
300
+ }
301
+ subscriptions.add(this.#session_id);
230
302
  return {};
231
303
  });
232
304
  }
@@ -281,9 +353,15 @@ export class McpServer {
281
353
  if (resource.template) {
282
354
  if (!params)
283
355
  throw new Error('Missing parameters for template resource');
284
- return resource.execute(uri, params);
356
+ return v.parse(
357
+ ReadResourceResultSchema,
358
+ await resource.execute(uri, params),
359
+ );
285
360
  }
286
- return resource.execute(uri);
361
+ return v.parse(
362
+ ReadResourceResultSchema,
363
+ await resource.execute(uri),
364
+ );
287
365
  });
288
366
  }
289
367
  /**
@@ -299,19 +377,17 @@ export class McpServer {
299
377
  if (!complete) return null;
300
378
  const actual_complete = complete[argument.name];
301
379
  if (!actual_complete) return null;
302
- return {
303
- completion: {
304
- values: actual_complete(argument.value, context),
305
- hasMore: false,
306
- },
307
- };
380
+ return v.parse(
381
+ CompleteResultSchema,
382
+ actual_complete(argument.value, context),
383
+ );
308
384
  },
309
385
  );
310
386
  }
311
387
  /**
312
388
  * @template {StandardSchema | undefined} [TSchema=undefined]
313
389
  * @param {{ name: string; description: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never }} options
314
- * @param {TSchema extends undefined ? (()=>Promise<unknown> | unknown) : ((input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<unknown> | unknown)} execute
390
+ * @param {TSchema extends undefined ? (()=>Promise<CallToolResult> | CallToolResult) : ((input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<CallToolResult> | CallToolResult)} execute
315
391
  */
316
392
  tool({ name, description, schema }, execute) {
317
393
  if (this.#options.capabilities?.tools?.listChanged) {
@@ -325,8 +401,8 @@ export class McpServer {
325
401
  }
326
402
  /**
327
403
  * @template {StandardSchema | undefined} [TSchema=undefined]
328
- * @param {{ name: string; description: string; schema?: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema> extends Record<string, unknown> ? TSchema : never; complete?: TSchema extends undefined ? never : Partial<Record<keyof (StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>), Completion>> }} options
329
- * @param {TSchema extends undefined ? (()=>Promise<unknown> | unknown) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<unknown> | unknown} execute
404
+ * @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
405
+ * @param {TSchema extends undefined ? (()=>Promise<GetPromptResult> | GetPromptResult) : (input: StandardSchemaV1.InferInput<TSchema extends undefined ? never : TSchema>) => Promise<GetPromptResult> | GetPromptResult} execute
330
406
  */
331
407
  prompt({ name, description, schema, complete }, execute) {
332
408
  if (complete) {
@@ -358,7 +434,7 @@ export class McpServer {
358
434
  }
359
435
  /**
360
436
  * @param {{ name: string; description: string; uri: string }} options
361
- * @param {(uri: string) => Promise<unknown> | unknown} execute
437
+ * @param {(uri: string) => Promise<ReadResourceResult> | ReadResourceResult} execute
362
438
  */
363
439
  resource({ name, description, uri }, execute) {
364
440
  this.#resource({
@@ -372,8 +448,8 @@ export class McpServer {
372
448
  /**
373
449
  * @template {string} TUri
374
450
  * @template {ExtractURITemplateVariables<TUri>} TVariables
375
- * @param {{ name: string; description: string; uri: TUri; complete?: TVariables extends never ? never : Partial<Record<TVariables, Completion>> }} options
376
- * @param {(uri: string, params: Record<TVariables, string | string[]>) => Promise<unknown> | unknown} execute
451
+ * @param {{ name: string; description: string; uri: TUri; complete?: NoInfer<TVariables extends never ? never : Partial<Record<TVariables, Completion>>> }} options
452
+ * @param {(uri: string, params: Record<TVariables, string | string[]>) => Promise<ReadResourceResult> | ReadResourceResult} execute
377
453
  */
378
454
  template({ name, description, uri, complete }, execute) {
379
455
  this.#resource({
@@ -387,10 +463,14 @@ export class McpServer {
387
463
  }
388
464
  /**
389
465
  * @param {JSONRPCRequest} request
466
+ * @param {string} [session_id]
390
467
  * @returns {ReturnType<JSONRPCServer['receive']>}
391
468
  */
392
- receive(request) {
393
- return this.#server.receive(request);
469
+ receive(request, session_id) {
470
+ return this.#session_storage.run(
471
+ session_id,
472
+ async () => await this.#server.receive(request),
473
+ );
394
474
  }
395
475
 
396
476
  /**
@@ -402,10 +482,48 @@ export class McpServer {
402
482
  if (this.#subscriptions[what].has(id)) {
403
483
  const resource = this.#resources.get(id);
404
484
  if (!resource) return;
405
- this.#notify(`notifications/resources/updated`, {
406
- uri: id,
407
- title: resource.name,
408
- });
485
+ const sessions = this.#subscriptions[what].get(id);
486
+ this.#notify(
487
+ `notifications/resources/updated`,
488
+ {
489
+ uri: id,
490
+ title: resource.name,
491
+ },
492
+ {
493
+ sessions: sessions ? [...sessions] : undefined,
494
+ },
495
+ );
496
+ }
497
+ }
498
+
499
+ /**
500
+ * @template {StandardSchema} TSchema
501
+ * @param {TSchema} schema
502
+ * @returns {Promise<StandardSchemaV1.InferOutput<TSchema>>}
503
+ */
504
+ async elicitation(schema) {
505
+ if (!this.#client_capabilities?.elicitation)
506
+ throw new Error("Client doesn't support elicitation");
507
+
508
+ this.#lazyily_create_client();
509
+ let validated_result = schema['~standard'].validate(
510
+ await this.#client?.request(
511
+ 'elicitation/create',
512
+ {
513
+ params: await this.#options.adapter.toJsonSchema(schema),
514
+ },
515
+ {
516
+ sessions: [this.#session_id],
517
+ },
518
+ ),
519
+ );
520
+ if (validated_result instanceof Promise)
521
+ validated_result = await validated_result;
522
+ if (validated_result.issues) {
523
+ throw new Error(
524
+ `Invalid elicitation result: ${JSON.stringify(validated_result.issues)}`,
525
+ );
409
526
  }
527
+ return validated_result.value;
410
528
  }
411
529
  }
@@ -1,26 +1,33 @@
1
1
  import { StandardSchemaV1 } from '@standard-schema/spec';
2
2
  import { JSONRPCRequest } from 'json-rpc-2.0';
3
3
  import { JsonSchemaAdapter } from '../adapter.js';
4
+ import {
5
+ GetPromptResult,
6
+ CallToolResult,
7
+ ReadResourceResult,
8
+ CompleteResult,
9
+ InitializeRequest,
10
+ } from '../validation/index.js';
4
11
 
5
12
  export type Tool<TSchema extends StandardSchemaV1 = StandardSchemaV1<any>> = {
6
13
  description: string;
7
14
  schema: TSchema;
8
15
  execute: (
9
16
  input?: StandardSchemaV1.InferInput<TSchema>,
10
- ) => Promise<unknown> | unknown;
17
+ ) => Promise<CallToolResult> | CallToolResult;
11
18
  };
12
19
 
13
20
  export type Completion = (
14
21
  query: string,
15
22
  context: { arguments: Record<string, string> },
16
- ) => string[];
23
+ ) => CompleteResult;
17
24
 
18
25
  export type Prompt<TSchema extends StandardSchemaV1 = StandardSchemaV1<any>> = {
19
26
  description: string;
20
27
  schema: TSchema;
21
28
  execute: (
22
29
  input?: StandardSchemaV1.InferInput<TSchema>,
23
- ) => Promise<unknown> | unknown;
30
+ ) => Promise<GetPromptResult> | GetPromptResult;
24
31
  };
25
32
 
26
33
  export type Resource =
@@ -31,13 +38,15 @@ export type Resource =
31
38
  execute: (
32
39
  uri: string,
33
40
  params: Record<string, string | string[]>,
34
- ) => Promise<unknown> | unknown;
41
+ ) => Promise<ReadResourceResult> | ReadResourceResult;
35
42
  }
36
43
  | {
37
44
  description: string;
38
45
  name: string;
39
46
  template: false;
40
- execute: (uri: string) => Promise<unknown> | unknown;
47
+ execute: (
48
+ uri: string,
49
+ ) => Promise<ReadResourceResult> | ReadResourceResult;
41
50
  };
42
51
 
43
52
  export type ServerOptions<TSchema extends StandardSchemaV1> = {
@@ -59,5 +68,11 @@ export type ServerInfo = {
59
68
  export type SubscriptionsKeys = 'resource';
60
69
 
61
70
  export type McpEvents = {
62
- send: (message: JSONRPCRequest) => void;
71
+ send: (message: {
72
+ request: JSONRPCRequest;
73
+ context: {
74
+ sessions?: string[] | undefined;
75
+ };
76
+ }) => void;
77
+ initialize: (initialize_request: InitializeRequest) => void;
63
78
  };