@@ -10,6 +10,8 @@ import {
1010 CreateTaskResultSchema ,
1111 ElicitRequestSchema ,
1212 ElicitResultSchema ,
13+ ErrorCode ,
14+ McpError ,
1315 RELATED_TASK_META_KEY ,
1416 TaskSchema
1517} from '../types.js' ;
@@ -316,7 +318,7 @@ describe('Task Lifecycle Integration Tests', () => {
316318 } ) ;
317319
318320 describe ( 'Task Cancellation' , ( ) => {
319- it ( 'should cancel a working task' , async ( ) => {
321+ it ( 'should cancel a working task and return the cancelled task ' , async ( ) => {
320322 const client = new Client ( {
321323 name : 'test-client' ,
322324 version : '1.0.0'
@@ -348,17 +350,24 @@ describe('Task Lifecycle Integration Tests', () => {
348350 let task = await taskStore . getTask ( taskId ) ;
349351 expect ( task ?. status ) . toBe ( 'working' ) ;
350352
351- // Cancel the task
352- await taskStore . updateTaskStatus ( taskId , 'cancelled' ) ;
353+ // Cancel the task via client.cancelTask - per spec, returns Result & Task
354+ const cancelResult = await client . cancelTask ( { taskId } ) ;
353355
354- // Verify task is cancelled
356+ // Verify the cancel response includes the cancelled task (per MCP spec CancelTaskResult is Result & Task)
357+ expect ( cancelResult . taskId ) . toBe ( taskId ) ;
358+ expect ( cancelResult . status ) . toBe ( 'cancelled' ) ;
359+ expect ( cancelResult . createdAt ) . toBeDefined ( ) ;
360+ expect ( cancelResult . lastUpdatedAt ) . toBeDefined ( ) ;
361+ expect ( cancelResult . ttl ) . toBeDefined ( ) ;
362+
363+ // Verify task is cancelled in store as well
355364 task = await taskStore . getTask ( taskId ) ;
356365 expect ( task ?. status ) . toBe ( 'cancelled' ) ;
357366
358367 await transport . close ( ) ;
359368 } ) ;
360369
361- it ( 'should reject cancellation of completed task' , async ( ) => {
370+ it ( 'should reject cancellation of completed task with error code -32602 ' , async ( ) => {
362371 const client = new Client ( {
363372 name : 'test-client' ,
364373 version : '1.0.0'
@@ -393,8 +402,13 @@ describe('Task Lifecycle Integration Tests', () => {
393402 const task = await taskStore . getTask ( taskId ) ;
394403 expect ( task ?. status ) . toBe ( 'completed' ) ;
395404
396- // Try to cancel (should fail)
397- await expect ( taskStore . updateTaskStatus ( taskId , 'cancelled' ) ) . rejects . toThrow ( ) ;
405+ // Try to cancel via tasks/cancel request (should fail with -32602)
406+ await expect ( client . cancelTask ( { taskId } ) ) . rejects . toSatisfy ( ( error : McpError ) => {
407+ expect ( error ) . toBeInstanceOf ( McpError ) ;
408+ expect ( error . code ) . toBe ( ErrorCode . InvalidParams ) ;
409+ expect ( error . message ) . toContain ( 'Cannot cancel task in terminal status' ) ;
410+ return true ;
411+ } ) ;
398412
399413 await transport . close ( ) ;
400414 } ) ;
@@ -775,7 +789,7 @@ describe('Task Lifecycle Integration Tests', () => {
775789 } ) ;
776790
777791 describe ( 'Error Handling' , ( ) => {
778- it ( 'should return null for non-existent task' , async ( ) => {
792+ it ( 'should return error code -32602 for non-existent task in tasks/get ' , async ( ) => {
779793 const client = new Client ( {
780794 name : 'test-client' ,
781795 version : '1.0.0'
@@ -784,14 +798,18 @@ describe('Task Lifecycle Integration Tests', () => {
784798 const transport = new StreamableHTTPClientTransport ( baseUrl ) ;
785799 await client . connect ( transport ) ;
786800
787- // Try to get non-existent task
788- const task = await taskStore . getTask ( 'non-existent' ) ;
789- expect ( task ) . toBeNull ( ) ;
801+ // Try to get non-existent task via tasks/get request
802+ await expect ( client . getTask ( { taskId : 'non-existent-task-id' } ) ) . rejects . toSatisfy ( ( error : McpError ) => {
803+ expect ( error ) . toBeInstanceOf ( McpError ) ;
804+ expect ( error . code ) . toBe ( ErrorCode . InvalidParams ) ;
805+ expect ( error . message ) . toContain ( 'Task not found' ) ;
806+ return true ;
807+ } ) ;
790808
791809 await transport . close ( ) ;
792810 } ) ;
793811
794- it ( 'should return error for invalid task operation ' , async ( ) => {
812+ it ( 'should return error code -32602 for non-existent task in tasks/cancel ' , async ( ) => {
795813 const client = new Client ( {
796814 name : 'test-client' ,
797815 version : '1.0.0'
@@ -800,30 +818,41 @@ describe('Task Lifecycle Integration Tests', () => {
800818 const transport = new StreamableHTTPClientTransport ( baseUrl ) ;
801819 await client . connect ( transport ) ;
802820
803- // Create and complete a task
804- const createResult = await client . request (
805- {
806- method : 'tools/call' ,
807- params : {
808- name : 'long-task' ,
809- arguments : {
810- duration : 100
811- } ,
812- task : {
813- ttl : 60000
814- }
815- }
816- } ,
817- CreateTaskResultSchema
818- ) ;
821+ // Try to cancel non-existent task via tasks/cancel request
822+ await expect ( client . cancelTask ( { taskId : 'non-existent-task-id' } ) ) . rejects . toSatisfy ( ( error : McpError ) => {
823+ expect ( error ) . toBeInstanceOf ( McpError ) ;
824+ expect ( error . code ) . toBe ( ErrorCode . InvalidParams ) ;
825+ expect ( error . message ) . toContain ( 'Task not found' ) ;
826+ return true ;
827+ } ) ;
819828
820- const taskId = createResult . task . taskId ;
829+ await transport . close ( ) ;
830+ } ) ;
821831
822- // Wait for completion
823- await new Promise ( resolve => setTimeout ( resolve , 200 ) ) ;
832+ it ( 'should return error code -32602 for non-existent task in tasks/result' , async ( ) => {
833+ const client = new Client ( {
834+ name : 'test-client' ,
835+ version : '1.0.0'
836+ } ) ;
824837
825- // Try to cancel completed task (should fail)
826- await expect ( taskStore . updateTaskStatus ( taskId , 'cancelled' ) ) . rejects . toThrow ( ) ;
838+ const transport = new StreamableHTTPClientTransport ( baseUrl ) ;
839+ await client . connect ( transport ) ;
840+
841+ // Try to get result of non-existent task via tasks/result request
842+ await expect (
843+ client . request (
844+ {
845+ method : 'tasks/result' ,
846+ params : { taskId : 'non-existent-task-id' }
847+ } ,
848+ CallToolResultSchema
849+ )
850+ ) . rejects . toSatisfy ( ( error : McpError ) => {
851+ expect ( error ) . toBeInstanceOf ( McpError ) ;
852+ expect ( error . code ) . toBe ( ErrorCode . InvalidParams ) ;
853+ expect ( error . message ) . toContain ( 'Task not found' ) ;
854+ return true ;
855+ } ) ;
827856
828857 await transport . close ( ) ;
829858 } ) ;
0 commit comments