Module: ActionMCP::Server::Tasks

Included in:
TransportHandler
Defined in:
lib/action_mcp/server/tasks.rb

Overview

Tasks module for MCP 2025-11-25 specification Provides methods for handling task-related requests:

  • tasks/get: Get task status and data

  • tasks/result: Get task result (blocking until terminal state)

  • tasks/list: List tasks for the session

  • tasks/cancel: Cancel a task

Instance Method Summary collapse

Instance Method Details

#send_task_status_notification(task) ⇒ Object

Send task status notification

Parameters:



101
102
103
104
105
106
# File 'lib/action_mcp/server/tasks.rb', line 101

def send_task_status_notification(task)
  send_jsonrpc_notification(
    JsonRpcHandlerBase::Methods::NOTIFICATIONS_TASKS_STATUS,
    { task: task.to_task_data }
  )
end

#send_tasks_cancel(request_id, task_id) ⇒ Object

Cancel a task

Parameters:

  • request_id (String, Integer)

    JSON-RPC request ID

  • task_id (String)

    Task ID to cancel



60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/action_mcp/server/tasks.rb', line 60

def send_tasks_cancel(request_id, task_id)
  task = find_task(task_id)
  return unless task

  if task.terminal?
    send_jsonrpc_error(request_id, :invalid_params,
                       "Cannot cancel task in terminal status: #{task.status}")
    return
  end

  task.cancel!
  send_jsonrpc_response(request_id, result: { task: task.to_task_data })
end

#send_tasks_get(request_id, task_id) ⇒ Object

Get task status and metadata

Parameters:

  • request_id (String, Integer)

    JSON-RPC request ID

  • task_id (String)

    Task ID to retrieve



15
16
17
18
19
20
# File 'lib/action_mcp/server/tasks.rb', line 15

def send_tasks_get(request_id, task_id)
  task = find_task(task_id)
  return unless task

  send_jsonrpc_response(request_id, result: { task: task.to_task_data })
end

#send_tasks_list(request_id, cursor: nil) ⇒ Object

List tasks for the session with keyset pagination. Tasks always paginate (AR-backed, can grow unbounded). Cursor is the last task id from the previous page. We resolve it through AR so the boundary matches the recent scope exactly.

Parameters:

  • request_id (String, Integer)

    JSON-RPC request ID

  • cursor (String, nil) (defaults to: nil)

    Pagination cursor



46
47
48
49
50
51
52
53
54
55
# File 'lib/action_mcp/server/tasks.rb', line 46

def send_tasks_list(request_id, cursor: nil)
  page, next_cursor = paginate_tasks_by_recent(cursor: cursor, page_size: pagination_page_size || 50)

  result = { tasks: page.map(&:to_task_data) }
  result[:nextCursor] = next_cursor if next_cursor

  send_jsonrpc_response(request_id, result: result)
rescue Server::CursorError => e
  send_jsonrpc_error(request_id, :invalid_params, e.message)
end

#send_tasks_result(request_id, task_id) ⇒ Object

Get task result, blocking until task reaches terminal state

Parameters:

  • request_id (String, Integer)

    JSON-RPC request ID

  • task_id (String)

    Task ID to get result for



25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/action_mcp/server/tasks.rb', line 25

def send_tasks_result(request_id, task_id)
  task = find_task(task_id)
  return unless task

  # If task is not in terminal state, wait for it
  # In async execution, client should poll or use SSE for notifications
  unless task.terminal?
    send_jsonrpc_error(request_id, :invalid_request,
                       "Task is not yet complete. Current status: #{task.status}")
    return
  end

  send_jsonrpc_response(request_id, result: task.to_task_result)
end

#send_tasks_resume(request_id, task_id, input) ⇒ Object

Resume a task from input_required state

Parameters:

  • request_id (String, Integer)

    JSON-RPC request ID

  • task_id (String)

    Task ID to resume

  • input (Object)

    Input data for the task



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/action_mcp/server/tasks.rb', line 78

def send_tasks_resume(request_id, task_id, input)
  task = find_task(task_id)
  return unless task

  unless task.input_required?
    send_jsonrpc_error(request_id, :invalid_params,
                       "Task is not awaiting input. Current status: #{task.status}")
    return
  end

  # Store input in continuation state
  continuation = task.continuation_state || {}
  continuation[:input] = input
  task.update!(continuation_state: continuation)

  # Resume task and re-enqueue job
  task.resume_from_continuation!

  send_jsonrpc_response(request_id, result: { task: task.to_task_data })
end