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 a 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:



95
96
97
98
99
100
# File 'lib/action_mcp/server/tasks.rb', line 95

def send_task_status_notification(task)
  send_jsonrpc_notification(
    JsonRpcHandlerBase::Methods::NOTIFICATIONS_TASKS_STATUS,
    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



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/action_mcp/server/tasks.rb', line 64

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

  terminal_status = nil
  task.with_lock do
    if task.terminal?
      terminal_status = task.status
      next
    end

    task.status_message = "The task was cancelled by request." if task.status_message.blank?
    task.result_payload ||= {
      code: -32_000,
      message: "Task was cancelled"
    }
    task.save! if task.changed?
    task.cancel!
  end

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

  send_jsonrpc_response(request_id, result: 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.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



50
51
52
53
54
55
56
57
58
59
# File 'lib/action_mcp/server/tasks.rb', line 50

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 the task reaches a 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
39
40
41
42
# 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

  task = wait_for_terminal_task(task_id) unless task.terminal?
  unless task
    send_jsonrpc_error(request_id, :invalid_params, "Task '#{task_id}' not found")
    return
  end

  if (error = task.to_task_error)
    send_jsonrpc_response(request_id, error: error)
  else
    send_jsonrpc_response(request_id, result: task.to_task_result)
  end
rescue ActiveRecord::RecordNotFound
  send_jsonrpc_error(request_id, :invalid_params, "Task '#{task_id}' not found")
end