@@ -6,10 +6,10 @@ import type {
66 ClientContext ,
77 ClientNotification ,
88 ClientRequest ,
9- ClientResult ,
109 CompleteRequest ,
1110 GetPromptRequest ,
1211 Implementation ,
12+ JSONRPCRequest ,
1313 JsonSchemaType ,
1414 JsonSchemaValidator ,
1515 jsonSchemaValidator ,
@@ -24,10 +24,8 @@ import type {
2424 NotificationMethod ,
2525 ProtocolOptions ,
2626 ReadResourceRequest ,
27- RequestMethod ,
2827 RequestOptions ,
29- RequestTypeMap ,
30- ResultTypeMap ,
28+ Result ,
3129 ServerCapabilities ,
3230 SubscribeRequest ,
3331 TaskManagerOptions ,
@@ -200,6 +198,28 @@ export type ClientOptions = ProtocolOptions & {
200198 *
201199 * The client will automatically begin the initialization flow with the server when {@linkcode connect} is called.
202200 *
201+ * To handle server-initiated requests (sampling, elicitation, roots), call {@linkcode setRequestHandler}.
202+ * The client must declare the corresponding capability for the handler to be accepted. For
203+ * `sampling/createMessage` and `elicitation/create`, the handler is automatically wrapped with
204+ * schema validation for both the incoming request and the returned result.
205+ *
206+ * @example Handling a sampling request
207+ * ```ts source="./client.examples.ts#Client_setRequestHandler_sampling"
208+ * client.setRequestHandler('sampling/createMessage', async request => {
209+ * const lastMessage = request.params.messages.at(-1);
210+ * console.log('Sampling request:', lastMessage);
211+ *
212+ * // In production, send messages to your LLM here
213+ * return {
214+ * model: 'my-model',
215+ * role: 'assistant' as const,
216+ * content: {
217+ * type: 'text' as const,
218+ * text: 'Response from the model'
219+ * }
220+ * };
221+ * });
222+ * ```
203223 */
204224export class Client extends Protocol < ClientContext > {
205225 private _serverCapabilities ?: ServerCapabilities ;
@@ -308,37 +328,15 @@ export class Client extends Protocol<ClientContext> {
308328 }
309329
310330 /**
311- * Registers a handler for server-initiated requests (sampling, elicitation, roots).
312- * The client must declare the corresponding capability for the handler to be accepted.
313- * Replaces any previously registered handler for the same method.
314- *
315- * For `sampling/createMessage` and `elicitation/create`, the handler is automatically
316- * wrapped with schema validation for both the incoming request and the returned result.
317- *
318- * @example Handling a sampling request
319- * ```ts source="./client.examples.ts#Client_setRequestHandler_sampling"
320- * client.setRequestHandler('sampling/createMessage', async request => {
321- * const lastMessage = request.params.messages.at(-1);
322- * console.log('Sampling request:', lastMessage);
323- *
324- * // In production, send messages to your LLM here
325- * return {
326- * model: 'my-model',
327- * role: 'assistant' as const,
328- * content: {
329- * type: 'text' as const,
330- * text: 'Response from the model'
331- * }
332- * };
333- * });
334- * ```
331+ * Enforces client-side validation for `elicitation/create` and `sampling/createMessage`
332+ * regardless of how the handler was registered.
335333 */
336- public override setRequestHandler < M extends RequestMethod > (
337- method : M ,
338- handler : ( request : RequestTypeMap [ M ] , ctx : ClientContext ) => ResultTypeMap [ M ] | Promise < ResultTypeMap [ M ] >
339- ) : void {
334+ protected override _wrapHandler (
335+ method : string ,
336+ handler : ( request : JSONRPCRequest , ctx : ClientContext ) => Promise < Result >
337+ ) : ( request : JSONRPCRequest , ctx : ClientContext ) => Promise < Result > {
340338 if ( method === 'elicitation/create' ) {
341- const wrappedHandler = async ( request : RequestTypeMap [ M ] , ctx : ClientContext ) : Promise < ClientResult > => {
339+ return async ( request , ctx ) => {
342340 const validatedRequest = parseSchema ( ElicitRequestSchema , request ) ;
343341 if ( ! validatedRequest . success ) {
344342 // Type guard: if success is false, error is guaranteed to exist
@@ -359,7 +357,7 @@ export class Client extends Protocol<ClientContext> {
359357 throw new ProtocolError ( ProtocolErrorCode . InvalidParams , 'Client does not support URL-mode elicitation requests' ) ;
360358 }
361359
362- const result = await Promise . resolve ( handler ( request , ctx ) ) ;
360+ const result = await handler ( request , ctx ) ;
363361
364362 // When task creation is requested, validate and return CreateTaskResult
365363 if ( params . task ) {
@@ -402,13 +400,10 @@ export class Client extends Protocol<ClientContext> {
402400
403401 return validatedResult ;
404402 } ;
405-
406- // Install the wrapped handler
407- return super . setRequestHandler ( method , wrappedHandler ) ;
408403 }
409404
410405 if ( method === 'sampling/createMessage' ) {
411- const wrappedHandler = async ( request : RequestTypeMap [ M ] , ctx : ClientContext ) : Promise < ClientResult > => {
406+ return async ( request , ctx ) => {
412407 const validatedRequest = parseSchema ( CreateMessageRequestSchema , request ) ;
413408 if ( ! validatedRequest . success ) {
414409 const errorMessage =
@@ -418,7 +413,7 @@ export class Client extends Protocol<ClientContext> {
418413
419414 const { params } = validatedRequest . data ;
420415
421- const result = await Promise . resolve ( handler ( request , ctx ) ) ;
416+ const result = await handler ( request , ctx ) ;
422417
423418 // When task creation is requested, validate and return CreateTaskResult
424419 if ( params . task ) {
@@ -445,13 +440,9 @@ export class Client extends Protocol<ClientContext> {
445440
446441 return validationResult . data ;
447442 } ;
448-
449- // Install the wrapped handler
450- return super . setRequestHandler ( method , wrappedHandler ) ;
451443 }
452444
453- // Other handlers use default behavior
454- return super . setRequestHandler ( method , handler ) ;
445+ return handler ;
455446 }
456447
457448 protected assertCapability ( capability : keyof ServerCapabilities , method : string ) : void {
@@ -578,7 +569,7 @@ export class Client extends Protocol<ClientContext> {
578569 return this . _instructions ;
579570 }
580571
581- protected assertCapabilityForMethod ( method : RequestMethod ) : void {
572+ protected assertCapabilityForMethod ( method : string ) : void {
582573 switch ( method as ClientRequest [ 'method' ] ) {
583574 case 'logging/setLevel' : {
584575 if ( ! this . _serverCapabilities ?. logging ) {
@@ -641,7 +632,7 @@ export class Client extends Protocol<ClientContext> {
641632 }
642633 }
643634
644- protected assertNotificationCapability ( method : NotificationMethod ) : void {
635+ protected assertNotificationCapability ( method : string ) : void {
645636 switch ( method as ClientNotification [ 'method' ] ) {
646637 case 'notifications/roots/list_changed' : {
647638 if ( ! this . _capabilities . roots ?. listChanged ) {
0 commit comments