Class: ActionMCP::Session::Task
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- ActionMCP::Session::Task
- Defined in:
- app/models/action_mcp/session/task.rb
Overview
Represents a Task in an MCP session as per MCP 2025-11-25 specification. Tasks provide durable state machines for tracking async request execution.
State Machine:
working -> input_required -> working (via resume)
working -> completed | failed | cancelled
input_required -> completed | failed | cancelled
Instance Method Summary collapse
-
#await_input!(prompt:, context: {}) ⇒ Object
Transition to input_required state and store pending input prompt.
-
#broadcast_status_change(transition = nil) ⇒ Object
Broadcast status change notification to the session.
-
#expired? ⇒ Boolean
TTL management.
-
#non_terminal? ⇒ Boolean
Check if task is in a non-terminal state.
-
#record_step!(step_name, cursor: nil, data: {}) ⇒ Object
Record step execution state for job resumption.
-
#resume_from_continuation! ⇒ void
Resume task from input_required state and re-enqueue job.
-
#store_partial_result!(result_fragment) ⇒ Object
Store partial result fragment (for streaming/incremental results).
-
#terminal? ⇒ Boolean
Check if task is in a terminal state.
-
#to_task_data ⇒ Hash
Convert to task data format per MCP spec.
-
#to_task_result ⇒ Hash
Convert to full task result format.
-
#update_progress!(percent:, message: nil) ⇒ Object
Update progress indicators for long-running tasks.
Instance Method Details
#await_input!(prompt:, context: {}) ⇒ Object
Transition to input_required state and store pending input prompt
182 183 184 185 |
# File 'app/models/action_mcp/session/task.rb', line 182 def await_input!(prompt:, context: {}) record_step!(:awaiting_input, data: { prompt: prompt, context: context }) require_input! end |
#broadcast_status_change(transition = nil) ⇒ Object
Broadcast status change notification to the session
132 133 134 135 136 137 138 139 |
# File 'app/models/action_mcp/session/task.rb', line 132 def broadcast_status_change(transition = nil) return unless session handler = ActionMCP::Server::TransportHandler.new(session) handler.send_task_status_notification(self) rescue StandardError => e Rails.logger.warn "Failed to broadcast task status change: #{e.message}" end |
#expired? ⇒ Boolean
TTL management
83 84 85 86 87 88 |
# File 'app/models/action_mcp/session/task.rb', line 83 def expired? return false if ttl.nil? # TTL is stored in milliseconds (MCP spec) created_at + (ttl / 1000.0).seconds < Time.current end |
#non_terminal? ⇒ Boolean
Check if task is in a non-terminal state
96 97 98 |
# File 'app/models/action_mcp/session/task.rb', line 96 def non_terminal? !terminal? end |
#record_step!(step_name, cursor: nil, data: {}) ⇒ Object
Record step execution state for job resumption
147 148 149 150 151 152 153 154 155 156 157 |
# File 'app/models/action_mcp/session/task.rb', line 147 def record_step!(step_name, cursor: nil, data: {}) update!( continuation_state: { step: step_name, cursor: cursor, data: data, timestamp: Time.current.iso8601 }, last_step_at: Time.current ) end |
#resume_from_continuation! ⇒ void
This method returns an undefined value.
Resume task from input_required state and re-enqueue job
189 190 191 192 193 194 195 |
# File 'app/models/action_mcp/session/task.rb', line 189 def resume_from_continuation! return unless input_required? resume! # Re-enqueue the job to continue execution ActionMCP::ToolExecutionJob.perform_later(id, request_name, request_params, {}) end |
#store_partial_result!(result_fragment) ⇒ Object
Store partial result fragment (for streaming/incremental results)
161 162 163 164 165 166 |
# File 'app/models/action_mcp/session/task.rb', line 161 def store_partial_result!(result_fragment) payload = result_payload || {} payload[:partial] ||= [] payload[:partial] << result_fragment update!(result_payload: payload) end |
#terminal? ⇒ Boolean
Check if task is in a terminal state
91 92 93 |
# File 'app/models/action_mcp/session/task.rb', line 91 def terminal? status.in?(%w[completed failed cancelled]) end |
#to_task_data ⇒ Hash
Convert to task data format per MCP spec
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'app/models/action_mcp/session/task.rb', line 102 def to_task_data data = { id: id, status: status, lastUpdatedAt: last_updated_at.iso8601(3) } data[:statusMessage] = if .present? # Add progress if available (ActiveJob::Continuable support) if progress_percent.present? || .present? data[:progress] = {}.tap do |progress| progress[:percent] = progress_percent if progress_percent.present? progress[:message] = if .present? end end data end |
#to_task_result ⇒ Hash
Convert to full task result format
123 124 125 126 127 128 |
# File 'app/models/action_mcp/session/task.rb', line 123 def to_task_result { task: to_task_data, result: result_payload } end |
#update_progress!(percent:, message: nil) ⇒ Object
Update progress indicators for long-running tasks
171 172 173 174 175 176 177 |
# File 'app/models/action_mcp/session/task.rb', line 171 def update_progress!(percent:, message: nil) update!( progress_percent: percent.clamp(0, 100), progress_message: , last_step_at: Time.current ) end |