@@ -14,6 +14,7 @@ import {
1414 Client ,
1515 getDisplayName ,
1616 GetPromptResultSchema ,
17+ InMemoryTaskStore ,
1718 ListPromptsResultSchema ,
1819 ListResourcesResultSchema ,
1920 ListToolsResultSchema ,
@@ -65,6 +66,7 @@ function printHelp(): void {
6566 console . log ( ' greet [name] - Call the greet tool' ) ;
6667 console . log ( ' multi-greet [name] - Call the multi-greet tool with notifications' ) ;
6768 console . log ( ' collect-info [type] - Test form elicitation with collect-user-info tool (contact/preferences/feedback)' ) ;
69+ console . log ( ' collect-info-task [type] - Test bidirectional task support (server+client tasks) with elicitation' ) ;
6870 console . log ( ' start-notifications [interval] [count] - Start periodic notifications' ) ;
6971 console . log ( ' run-notifications-tool-with-resumability [interval] [count] - Run notification tool with resumability' ) ;
7072 console . log ( ' list-prompts - List available prompts' ) ;
@@ -140,6 +142,11 @@ function commandLoop(): void {
140142 break ;
141143 }
142144
145+ case 'collect-info-task' : {
146+ await callCollectInfoWithTask ( args [ 1 ] || 'contact' ) ;
147+ break ;
148+ }
149+
143150 case 'start-notifications' : {
144151 const interval = args [ 1 ] ? Number . parseInt ( args [ 1 ] , 10 ) : 2000 ;
145152 const count = args [ 2 ] ? Number . parseInt ( args [ 2 ] , 10 ) : 10 ;
@@ -249,7 +256,10 @@ async function connect(url?: string): Promise<void> {
249256 console . log ( `Connecting to ${ serverUrl } ...` ) ;
250257
251258 try {
252- // Create a new client with form elicitation capability
259+ // Create task store for client-side task support
260+ const clientTaskStore = new InMemoryTaskStore ( ) ;
261+
262+ // Create a new client with form elicitation capability and task support
253263 client = new Client (
254264 {
255265 name : 'example-client' ,
@@ -259,25 +269,49 @@ async function connect(url?: string): Promise<void> {
259269 capabilities : {
260270 elicitation : {
261271 form : { }
272+ } ,
273+ tasks : {
274+ requests : {
275+ elicitation : {
276+ create : { }
277+ }
278+ }
262279 }
263- }
280+ } ,
281+ taskStore : clientTaskStore
264282 }
265283 ) ;
266284 client . onerror = error => {
267285 console . error ( '\u001B[31mClient error:' , error , '\u001B[0m' ) ;
268286 } ;
269287
270- // Set up elicitation request handler with proper validation
271- client . setRequestHandler ( 'elicitation/create' , async request => {
288+ // Set up elicitation request handler with proper validation and task support
289+ client . setRequestHandler ( 'elicitation/create' , async ( request , extra ) => {
272290 if ( request . params . mode !== 'form' ) {
273291 throw new ProtocolError ( ProtocolErrorCode . InvalidParams , `Unsupported elicitation mode: ${ request . params . mode } ` ) ;
274292 }
275293 console . log ( '\n🔔 Elicitation (form) Request Received:' ) ;
276294 console . log ( `Message: ${ request . params . message } ` ) ;
277295 console . log ( `Related Task: ${ request . params . _meta ?. [ RELATED_TASK_META_KEY ] ?. taskId } ` ) ;
296+ console . log ( `Task Creation Requested: ${ request . params . task ? 'yes' : 'no' } ` ) ;
278297 console . log ( 'Requested Schema:' ) ;
279298 console . log ( JSON . stringify ( request . params . requestedSchema , null , 2 ) ) ;
280299
300+ // Helper to return result, optionally creating a task if requested
301+ const returnResult = async ( result : {
302+ action : 'accept' | 'decline' | 'cancel' ;
303+ content ?: Record < string , string | number | boolean | string [ ] > ;
304+ } ) => {
305+ if ( request . params . task && extra . task ?. store ) {
306+ // Create a task and store the result
307+ const task = await extra . task . store . createTask ( { ttl : extra . task . requestedTtl } ) ;
308+ await extra . task . store . storeTaskResult ( task . taskId , 'completed' , result ) ;
309+ console . log ( `📋 Created client-side task: ${ task . taskId } ` ) ;
310+ return { task } ;
311+ }
312+ return result ;
313+ } ;
314+
281315 const schema = request . params . requestedSchema ;
282316 const properties = schema . properties ;
283317 const required = schema . required || [ ] ;
@@ -411,7 +445,7 @@ async function connect(url?: string): Promise<void> {
411445 }
412446
413447 if ( inputCancelled ) {
414- return { action : 'cancel' } ;
448+ return returnResult ( { action : 'cancel' } ) ;
415449 }
416450
417451 // If we didn't complete all fields due to an error, try again
@@ -424,7 +458,7 @@ async function connect(url?: string): Promise<void> {
424458 continue ;
425459 } else {
426460 console . log ( 'Maximum attempts reached. Declining request.' ) ;
427- return { action : 'decline' } ;
461+ return returnResult ( { action : 'decline' } ) ;
428462 }
429463 }
430464
@@ -443,7 +477,7 @@ async function connect(url?: string): Promise<void> {
443477 continue ;
444478 } else {
445479 console . log ( 'Maximum attempts reached. Declining request.' ) ;
446- return { action : 'decline' } ;
480+ return returnResult ( { action : 'decline' } ) ;
447481 }
448482 }
449483
@@ -460,22 +494,22 @@ async function connect(url?: string): Promise<void> {
460494 switch ( confirmAnswer ) {
461495 case 'yes' :
462496 case 'y' : {
463- return {
497+ return returnResult ( {
464498 action : 'accept' ,
465499 content
466- } ;
500+ } ) ;
467501 }
468502 case 'cancel' :
469503 case 'c' : {
470- return { action : 'cancel' } ;
504+ return returnResult ( { action : 'cancel' } ) ;
471505 }
472506 case 'no' :
473507 case 'n' : {
474508 if ( attempts < maxAttempts ) {
475509 console . log ( 'Please re-enter the information...' ) ;
476510 continue ;
477511 } else {
478- return { action : 'decline' } ;
512+ return returnResult ( { action : 'decline' } ) ;
479513 }
480514
481515 break ;
@@ -485,7 +519,7 @@ async function connect(url?: string): Promise<void> {
485519 }
486520
487521 console . log ( 'Maximum attempts reached. Declining request.' ) ;
488- return { action : 'decline' } ;
522+ return returnResult ( { action : 'decline' } ) ;
489523 } ) ;
490524
491525 transport = new StreamableHTTPClientTransport ( new URL ( serverUrl ) , {
@@ -699,6 +733,12 @@ async function callCollectInfoTool(infoType: string): Promise<void> {
699733 await callTool ( 'collect-user-info' , { infoType } ) ;
700734}
701735
736+ async function callCollectInfoWithTask ( infoType : string ) : Promise < void > {
737+ console . log ( `\n🔄 Testing bidirectional task support with collect-user-info-task tool (${ infoType } )...` ) ;
738+ console . log ( 'This will create a task on the server, which will elicit input and create a task on the client.\n' ) ;
739+ await callToolTask ( 'collect-user-info-task' , { infoType } ) ;
740+ }
741+
702742async function startNotifications ( interval : number , count : number ) : Promise < void > {
703743 console . log ( `Starting notification stream: interval=${ interval } ms, count=${ count || 'unlimited' } ` ) ;
704744 await callTool ( 'start-notification-stream' , { interval, count } ) ;
0 commit comments