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 or input_required 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:



104
105
106
107
108
109
# File 'lib/action_mcp/server/tasks.rb', line 104

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



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

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.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!
  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



68
69
70
71
72
73
74
75
76
77
# File 'lib/action_mcp/server/tasks.rb', line 68

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 or input_required 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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 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

  unless task.result_ready?
    case ActionMCP.configuration.tasks_result_strategy
    when :polling_only
      send_jsonrpc_error(
        request_id,
        :invalid_request,
        "Task is not ready. Poll tasks/get over HTTP until the task reaches a terminal or input_required status, then retry tasks/result."
      )
      return
    else
      task = wait_for_result_ready_task(task_id)
      unless task&.result_ready?
        send_jsonrpc_response(
          request_id,
          error: {
            code: -32_000,
            message: "Timed out waiting for task '#{task_id}' to reach a terminal or input_required status"
          }
        )
        return
      end
    end
  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