@@ -47,6 +47,22 @@ export type ClientOptions = ProtocolOptions & {
47
47
* Capabilities to advertise as being supported by this client.
48
48
*/
49
49
capabilities ?: ClientCapabilities ;
50
+ /**
51
+ * Configure automatic refresh behavior for tool list changes
52
+ */
53
+ toolRefreshOptions ?: {
54
+ /**
55
+ * Whether to automatically refresh the tools list when a change notification is received.
56
+ * Default: true
57
+ */
58
+ autoRefresh ?: boolean ;
59
+ /**
60
+ * Debounce time in milliseconds for tool list refresh operations.
61
+ * Multiple notifications received within this timeframe will only trigger one refresh.
62
+ * Default: 300
63
+ */
64
+ debounceMs ?: number ;
65
+ } ;
50
66
} ;
51
67
52
68
/**
@@ -77,7 +93,7 @@ export type ClientOptions = ProtocolOptions & {
77
93
export class Client <
78
94
RequestT extends Request = Request ,
79
95
NotificationT extends Notification = Notification ,
80
- ResultT extends Result = Result
96
+ ResultT extends Result = Result ,
81
97
> extends Protocol <
82
98
ClientRequest | RequestT ,
83
99
ClientNotification | NotificationT ,
@@ -87,6 +103,10 @@ export class Client<
87
103
private _serverVersion ?: Implementation ;
88
104
private _capabilities : ClientCapabilities ;
89
105
private _instructions ?: string ;
106
+ private _toolRefreshOptions : Required <
107
+ NonNullable < ClientOptions [ "toolRefreshOptions" ] >
108
+ > ;
109
+ private _toolRefreshDebounceTimer ?: ReturnType < typeof setTimeout > ;
90
110
91
111
/**
92
112
* Callback for when the server indicates that the tools list has changed.
@@ -97,31 +117,61 @@ export class Client<
97
117
/**
98
118
* Initializes this client with the given name and version information.
99
119
*/
100
- constructor ( private _clientInfo : Implementation , options ?: ClientOptions ) {
120
+ constructor (
121
+ private _clientInfo : Implementation ,
122
+ options ?: ClientOptions
123
+ ) {
101
124
super ( options ) ;
102
125
this . _capabilities = options ?. capabilities ?? { } ;
126
+ this . _toolRefreshOptions = {
127
+ autoRefresh : options ?. toolRefreshOptions ?. autoRefresh ?? true ,
128
+ debounceMs : options ?. toolRefreshOptions ?. debounceMs ?? 500 ,
129
+ } ;
103
130
104
131
// Set up notification handlers
105
132
this . setNotificationHandler (
106
133
"notifications/tools/list_changed" ,
107
134
async ( ) => {
108
- // Automatically refresh the tools list when the server indicates a change
109
- try {
110
- // Only refresh if the server supports tools
111
- if ( this . _serverCapabilities ?. tools ) {
112
- const result = await this . listTools ( ) ;
113
- // Call the user's callback with the updated tools list
114
- this . onToolListChanged ?.( result . tools ) ;
115
- }
116
- } catch ( error ) {
117
- console . error ( "Failed to refresh tools list:" , error ) ;
118
- // Still call the callback even if refresh failed
135
+ // Only proceed with refresh if auto-refresh is enabled
136
+ if ( ! this . _toolRefreshOptions . autoRefresh ) {
137
+ // Still call callback to notify about the change, but without tools data
119
138
this . onToolListChanged ?.( undefined ) ;
139
+ return ;
140
+ }
141
+
142
+ // Clear any pending refresh timer
143
+ if ( this . _toolRefreshDebounceTimer ) {
144
+ clearTimeout ( this . _toolRefreshDebounceTimer ) ;
120
145
}
146
+
147
+ // Set up debounced refresh
148
+ this . _toolRefreshDebounceTimer = setTimeout ( ( ) => {
149
+ this . _refreshToolsList ( ) . catch ( ( error ) => {
150
+ console . error ( "Failed to refresh tools list:" , error ) ;
151
+ } ) ;
152
+ } , this . _toolRefreshOptions . debounceMs ) ;
121
153
}
122
154
) ;
123
155
}
124
156
157
+ /**
158
+ * Private method to handle tools list refresh
159
+ */
160
+ private async _refreshToolsList ( ) : Promise < void > {
161
+ try {
162
+ // Only refresh if the server supports tools
163
+ if ( this . _serverCapabilities ?. tools ) {
164
+ const result = await this . listTools ( ) ;
165
+ // Call the user's callback with the updated tools list
166
+ this . onToolListChanged ?.( result . tools ) ;
167
+ }
168
+ } catch ( error ) {
169
+ console . error ( "Failed to refresh tools list:" , error ) ;
170
+ // Still call the callback even if refresh failed
171
+ this . onToolListChanged ?.( undefined ) ;
172
+ }
173
+ }
174
+
125
175
/**
126
176
* Registers new capabilities. This can only be called before connecting to a transport.
127
177
*
@@ -130,20 +180,64 @@ export class Client<
130
180
public registerCapabilities ( capabilities : ClientCapabilities ) : void {
131
181
if ( this . transport ) {
132
182
throw new Error (
133
- "Cannot register capabilities after connecting to transport"
183
+ "Cannot register capabilities after connecting to transport" ,
134
184
) ;
135
185
}
136
186
137
187
this . _capabilities = mergeCapabilities ( this . _capabilities , capabilities ) ;
138
188
}
139
189
190
+ /**
191
+ * Updates the tool refresh options
192
+ */
193
+ public setToolRefreshOptions (
194
+ options : ClientOptions [ "toolRefreshOptions" ]
195
+ ) : void {
196
+ if ( options ) {
197
+ if ( options . autoRefresh !== undefined ) {
198
+ this . _toolRefreshOptions . autoRefresh = options . autoRefresh ;
199
+ }
200
+ if ( options . debounceMs !== undefined ) {
201
+ this . _toolRefreshOptions . debounceMs = options . debounceMs ;
202
+ }
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Gets the current tool refresh options
208
+ */
209
+ public getToolRefreshOptions ( ) : Required <
210
+ NonNullable < ClientOptions [ "toolRefreshOptions" ] >
211
+ > {
212
+ return { ...this . _toolRefreshOptions } ;
213
+ }
214
+
215
+ /**
216
+ * Manually triggers a refresh of the tools list
217
+ */
218
+ public async refreshToolsList ( ) : Promise <
219
+ ListToolsResult [ "tools" ] | undefined
220
+ > {
221
+ if ( ! this . _serverCapabilities ?. tools ) {
222
+ return undefined ;
223
+ }
224
+
225
+ try {
226
+ const result = await this . listTools ( ) ;
227
+ return result . tools ;
228
+ } catch ( error ) {
229
+ console . error ( "Failed to manually refresh tools list:" , error ) ;
230
+ return undefined ;
231
+ }
232
+ }
233
+
140
234
protected assertCapability (
141
235
capability : keyof ServerCapabilities ,
142
236
method : string
143
237
) : void {
144
238
if ( ! this . _serverCapabilities ?. [ capability ] ) {
145
239
throw new Error (
146
- `Server does not support ${ String ( capability ) } (required for ${ method } )`
240
+ `Server does not support ${ String ( capability ) } (required for ${ method } )` ,
147
241
) ;
148
242
}
149
243
}
@@ -161,7 +255,7 @@ export class Client<
161
255
clientInfo : this . _clientInfo ,
162
256
} ,
163
257
} ,
164
- InitializeResultSchema
258
+ InitializeResultSchema ,
165
259
) ;
166
260
167
261
if ( result === undefined ) {
@@ -170,7 +264,7 @@ export class Client<
170
264
171
265
if ( ! SUPPORTED_PROTOCOL_VERSIONS . includes ( result . protocolVersion ) ) {
172
266
throw new Error (
173
- `Server's protocol version is not supported: ${ result . protocolVersion } `
267
+ `Server's protocol version is not supported: ${ result . protocolVersion } ` ,
174
268
) ;
175
269
}
176
270
@@ -215,7 +309,7 @@ export class Client<
215
309
case "logging/setLevel" :
216
310
if ( ! this . _serverCapabilities ?. logging ) {
217
311
throw new Error (
218
- `Server does not support logging (required for ${ method } )`
312
+ `Server does not support logging (required for ${ method } )` ,
219
313
) ;
220
314
}
221
315
break ;
@@ -224,7 +318,7 @@ export class Client<
224
318
case "prompts/list" :
225
319
if ( ! this . _serverCapabilities ?. prompts ) {
226
320
throw new Error (
227
- `Server does not support prompts (required for ${ method } )`
321
+ `Server does not support prompts (required for ${ method } )` ,
228
322
) ;
229
323
}
230
324
break ;
@@ -236,7 +330,7 @@ export class Client<
236
330
case "resources/unsubscribe" :
237
331
if ( ! this . _serverCapabilities ?. resources ) {
238
332
throw new Error (
239
- `Server does not support resources (required for ${ method } )`
333
+ `Server does not support resources (required for ${ method } )` ,
240
334
) ;
241
335
}
242
336
@@ -245,7 +339,7 @@ export class Client<
245
339
! this . _serverCapabilities . resources . subscribe
246
340
) {
247
341
throw new Error (
248
- `Server does not support resource subscriptions (required for ${ method } )`
342
+ `Server does not support resource subscriptions (required for ${ method } )` ,
249
343
) ;
250
344
}
251
345
@@ -255,15 +349,15 @@ export class Client<
255
349
case "tools/list" :
256
350
if ( ! this . _serverCapabilities ?. tools ) {
257
351
throw new Error (
258
- `Server does not support tools (required for ${ method } )`
352
+ `Server does not support tools (required for ${ method } )` ,
259
353
) ;
260
354
}
261
355
break ;
262
356
263
357
case "completion/complete" :
264
358
if ( ! this . _serverCapabilities ?. prompts ) {
265
359
throw new Error (
266
- `Server does not support prompts (required for ${ method } )`
360
+ `Server does not support prompts (required for ${ method } )` ,
267
361
) ;
268
362
}
269
363
break ;
@@ -319,15 +413,15 @@ export class Client<
319
413
case "sampling/createMessage" :
320
414
if ( ! this . _capabilities . sampling ) {
321
415
throw new Error (
322
- `Client does not support sampling capability (required for ${ method } )`
416
+ `Client does not support sampling capability (required for ${ method } )` ,
323
417
) ;
324
418
}
325
419
break ;
326
420
327
421
case "roots/list" :
328
422
if ( ! this . _capabilities . roots ) {
329
423
throw new Error (
330
- `Client does not support roots capability (required for ${ method } )`
424
+ `Client does not support roots capability (required for ${ method } )` ,
331
425
) ;
332
426
}
333
427
break ;
@@ -346,15 +440,15 @@ export class Client<
346
440
return this . request (
347
441
{ method : "completion/complete" , params } ,
348
442
CompleteResultSchema ,
349
- options
443
+ options ,
350
444
) ;
351
445
}
352
446
353
447
async setLoggingLevel ( level : LoggingLevel , options ?: RequestOptions ) {
354
448
return this . request (
355
449
{ method : "logging/setLevel" , params : { level } } ,
356
450
EmptyResultSchema ,
357
- options
451
+ options ,
358
452
) ;
359
453
}
360
454
@@ -365,7 +459,7 @@ export class Client<
365
459
return this . request (
366
460
{ method : "prompts/get" , params } ,
367
461
GetPromptResultSchema ,
368
- options
462
+ options ,
369
463
) ;
370
464
}
371
465
@@ -376,7 +470,7 @@ export class Client<
376
470
return this . request (
377
471
{ method : "prompts/list" , params } ,
378
472
ListPromptsResultSchema ,
379
- options
473
+ options ,
380
474
) ;
381
475
}
382
476
@@ -387,7 +481,7 @@ export class Client<
387
481
return this . request (
388
482
{ method : "resources/list" , params } ,
389
483
ListResourcesResultSchema ,
390
- options
484
+ options ,
391
485
) ;
392
486
}
393
487
@@ -398,7 +492,7 @@ export class Client<
398
492
return this . request (
399
493
{ method : "resources/templates/list" , params } ,
400
494
ListResourceTemplatesResultSchema ,
401
- options
495
+ options ,
402
496
) ;
403
497
}
404
498
@@ -409,7 +503,7 @@ export class Client<
409
503
return this . request (
410
504
{ method : "resources/read" , params } ,
411
505
ReadResourceResultSchema ,
412
- options
506
+ options ,
413
507
) ;
414
508
}
415
509
@@ -420,7 +514,7 @@ export class Client<
420
514
return this . request (
421
515
{ method : "resources/subscribe" , params } ,
422
516
EmptyResultSchema ,
423
- options
517
+ options ,
424
518
) ;
425
519
}
426
520
@@ -431,7 +525,7 @@ export class Client<
431
525
return this . request (
432
526
{ method : "resources/unsubscribe" , params } ,
433
527
EmptyResultSchema ,
434
- options
528
+ options ,
435
529
) ;
436
530
}
437
531
@@ -440,23 +534,23 @@ export class Client<
440
534
resultSchema :
441
535
| typeof CallToolResultSchema
442
536
| typeof CompatibilityCallToolResultSchema = CallToolResultSchema ,
443
- options ?: RequestOptions
537
+ options ?: RequestOptions ,
444
538
) {
445
539
return this . request (
446
540
{ method : "tools/call" , params } ,
447
541
resultSchema ,
448
- options
542
+ options ,
449
543
) ;
450
544
}
451
545
452
546
async listTools (
453
547
params ?: ListToolsRequest [ "params" ] ,
454
- options ?: RequestOptions
548
+ options ?: RequestOptions ,
455
549
) {
456
550
return this . request (
457
551
{ method : "tools/list" , params } ,
458
552
ListToolsResultSchema ,
459
- options
553
+ options ,
460
554
) ;
461
555
}
462
556
0 commit comments