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
229 230 231 232 |
# File 'app/models/action_mcp/session/task.rb', line 229 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
179 180 181 182 183 184 185 186 |
# File 'app/models/action_mcp/session/task.rb', line 179 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.}" end |
#expired? ⇒ Boolean
TTL management
130 131 132 133 134 135 |
# File 'app/models/action_mcp/session/task.rb', line 130 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
143 144 145 |
# File 'app/models/action_mcp/session/task.rb', line 143 def non_terminal? !terminal? end |
#record_step!(step_name, cursor: nil, data: {}) ⇒ Object
Record step execution state for job resumption
194 195 196 197 198 199 200 201 202 203 204 |
# File 'app/models/action_mcp/session/task.rb', line 194 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
236 237 238 239 240 241 242 |
# File 'app/models/action_mcp/session/task.rb', line 236 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)
208 209 210 211 212 213 |
# File 'app/models/action_mcp/session/task.rb', line 208 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
138 139 140 |
# File 'app/models/action_mcp/session/task.rb', line 138 def terminal? status.in?(%w[completed failed cancelled]) end |
#to_task_data ⇒ Hash
Convert to task data format per MCP spec
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'app/models/action_mcp/session/task.rb', line 149 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
170 171 172 173 174 175 |
# File 'app/models/action_mcp/session/task.rb', line 170 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
218 219 220 221 222 223 224 |
# File 'app/models/action_mcp/session/task.rb', line 218 def update_progress!(percent:, message: nil) update!( progress_percent: percent.clamp(0, 100), progress_message: , last_step_at: Time.current ) end |