Skip to content

Commit a8aa1e0

Browse files
committed
feat(task): add task support (SEP-1686)
1 parent 03040f8 commit a8aa1e0

File tree

3 files changed

+541
-0
lines changed

3 files changed

+541
-0
lines changed

crates/rmcp/src/model.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod elicitation_schema;
66
mod extension;
77
mod meta;
88
mod prompt;
9+
mod task;
910
mod resource;
1011
mod serde_impl;
1112
mod tool;
@@ -17,6 +18,7 @@ pub use extension::*;
1718
pub use meta::*;
1819
pub use prompt::*;
1920
pub use resource::*;
21+
pub use task::*;
2022
use serde::{Deserialize, Serialize, de::DeserializeOwned};
2123
use serde_json::Value;
2224
pub use tool::*;

crates/rmcp/src/model/task.rs

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
use serde::{Deserialize, Serialize};
2+
use serde_json::Value;
3+
4+
use super::{JsonObject, Meta};
5+
6+
/// Task lifecycle status
7+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8+
#[serde(rename_all = "snake_case")]
9+
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
10+
pub enum TaskStatus {
11+
/// Created but not started yet
12+
Pending,
13+
/// Currently running
14+
Running,
15+
/// Waiting for dependencies or external input
16+
Waiting,
17+
/// Cancellation requested and in progress
18+
Cancelling,
19+
/// Completed successfully
20+
Succeeded,
21+
/// Completed with failure
22+
Failed,
23+
/// Cancelled before completion
24+
Cancelled,
25+
}
26+
27+
/// High-level task kind. Exact set may evolve with the SEP.
28+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize,Default)]
29+
#[serde(rename_all = "snake_case")]
30+
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
31+
pub enum TaskKind {
32+
#[default]
33+
Generation,
34+
Retrieval,
35+
Aggregation,
36+
Orchestration,
37+
ToolCall,
38+
/// Custom kind identifier
39+
Custom(String),
40+
}
41+
42+
/// Progress information for long-running tasks
43+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
44+
#[serde(rename_all = "camelCase")]
45+
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
46+
pub struct TaskProgress {
47+
/// Percentage progress in the range [0.0, 100.0]
48+
#[serde(skip_serializing_if = "Option::is_none")]
49+
pub percent: Option<f32>,
50+
/// Current stage identifier
51+
#[serde(skip_serializing_if = "Option::is_none")]
52+
pub stage: Option<String>,
53+
/// Human-readable status message
54+
#[serde(skip_serializing_if = "Option::is_none")]
55+
pub message: Option<String>,
56+
/// Arbitrary structured details, protocol-specific
57+
#[serde(skip_serializing_if = "Option::is_none")]
58+
pub details: Option<Value>,
59+
}
60+
61+
/// Error information for failed tasks
62+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
63+
#[serde(rename_all = "camelCase")]
64+
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
65+
pub struct TaskError {
66+
/// Machine-readable error code
67+
pub code: String,
68+
/// Human-readable error message
69+
pub message: String,
70+
/// Whether the operation can be retried safely
71+
#[serde(skip_serializing_if = "Option::is_none")]
72+
pub retryable: Option<bool>,
73+
/// Arbitrary error data for debugging
74+
#[serde(skip_serializing_if = "Option::is_none")]
75+
pub data: Option<Value>,
76+
}
77+
78+
/// Final result for a succeeded task
79+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
80+
#[serde(rename_all = "camelCase")]
81+
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
82+
pub struct TaskResult {
83+
/// MIME type or custom content-type identifier
84+
pub content_type: String,
85+
/// The actual result payload
86+
pub value: Value,
87+
/// Optional short summary for UI
88+
#[serde(skip_serializing_if = "Option::is_none")]
89+
pub summary: Option<String>,
90+
}
91+
92+
/// Primary Task object used across client/server
93+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
94+
#[serde(rename_all = "camelCase")]
95+
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
96+
pub struct Task {
97+
/// Unique task identifier
98+
pub id: String,
99+
/// Task kind/category
100+
pub kind: TaskKind,
101+
/// Current status
102+
pub status: TaskStatus,
103+
/// ISO8601 creation time
104+
pub created_at: String,
105+
/// ISO8601 last update time
106+
#[serde(skip_serializing_if = "Option::is_none")]
107+
pub updated_at: Option<String>,
108+
/// ISO8601 start time
109+
#[serde(skip_serializing_if = "Option::is_none")]
110+
pub started_at: Option<String>,
111+
/// ISO8601 completion time
112+
#[serde(skip_serializing_if = "Option::is_none")]
113+
pub completed_at: Option<String>,
114+
/// Parent task identifier for hierarchical tasks
115+
#[serde(skip_serializing_if = "Option::is_none")]
116+
pub parent_id: Option<String>,
117+
/// List of prerequisite task ids
118+
#[serde(default, skip_serializing_if = "Vec::is_empty")]
119+
pub depends_on: Vec<String>,
120+
/// Optional labels for filtering and grouping
121+
#[serde(skip_serializing_if = "Option::is_none")]
122+
pub labels: Option<Vec<String>>,
123+
/// Immutable metadata provided at creation
124+
#[serde(skip_serializing_if = "Option::is_none")]
125+
pub metadata: Option<Value>,
126+
/// Mutable runtime state exposed to clients
127+
#[serde(skip_serializing_if = "Option::is_none")]
128+
pub runtime_state: Option<Value>,
129+
/// Input parameters for this task
130+
#[serde(skip_serializing_if = "Option::is_none")]
131+
pub input: Option<Value>,
132+
/// Progress info when running
133+
#[serde(skip_serializing_if = "Option::is_none")]
134+
pub progress: Option<TaskProgress>,
135+
/// Final result when succeeded
136+
#[serde(skip_serializing_if = "Option::is_none")]
137+
pub result: Option<TaskResult>,
138+
/// Error information when failed
139+
#[serde(skip_serializing_if = "Option::is_none")]
140+
pub error: Option<TaskError>,
141+
/// True if a cancellation has been requested
142+
#[serde(default)]
143+
pub cancellation_requested: bool,
144+
/// Scheduling priority; larger means higher priority (convention)
145+
#[serde(skip_serializing_if = "Option::is_none")]
146+
pub priority: Option<i32>,
147+
/// Batch/group identifier for bulk operations
148+
#[serde(skip_serializing_if = "Option::is_none")]
149+
pub batch_group: Option<String>,
150+
/// Trace identifier for observability systems
151+
#[serde(skip_serializing_if = "Option::is_none")]
152+
pub trace_id: Option<String>,
153+
/// Protocol-level metadata for the task object
154+
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
155+
pub meta: Option<Meta>,
156+
/// Reserved for future SEP extensions
157+
#[serde(skip_serializing_if = "Option::is_none")]
158+
pub extensions: Option<JsonObject>,
159+
}
160+
161+
/// Query filter for listing tasks
162+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
163+
#[serde(rename_all = "camelCase")]
164+
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
165+
pub struct TaskQuery {
166+
#[serde(skip_serializing_if = "Option::is_none")]
167+
pub ids: Option<Vec<String>>,
168+
#[serde(skip_serializing_if = "Option::is_none")]
169+
pub status: Option<Vec<TaskStatus>>,
170+
#[serde(skip_serializing_if = "Option::is_none")]
171+
pub kind: Option<Vec<TaskKind>>,
172+
#[serde(skip_serializing_if = "Option::is_none")]
173+
pub parent_id: Option<String>,
174+
#[serde(skip_serializing_if = "Option::is_none")]
175+
pub labels_any: Option<Vec<String>>,
176+
#[serde(skip_serializing_if = "Option::is_none")]
177+
pub labels_all: Option<Vec<String>>,
178+
#[serde(skip_serializing_if = "Option::is_none")]
179+
pub created_after: Option<String>,
180+
#[serde(skip_serializing_if = "Option::is_none")]
181+
pub created_before: Option<String>,
182+
#[serde(skip_serializing_if = "Option::is_none")]
183+
pub limit: Option<u32>,
184+
#[serde(skip_serializing_if = "Option::is_none")]
185+
pub cursor: Option<String>,
186+
}
187+
188+
/// Paginated list of tasks
189+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
190+
#[serde(rename_all = "camelCase")]
191+
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
192+
pub struct TaskList {
193+
pub tasks: Vec<Task>,
194+
#[serde(skip_serializing_if = "Option::is_none")]
195+
pub next_cursor: Option<String>,
196+
#[serde(skip_serializing_if = "Option::is_none")]
197+
pub total: Option<u64>,
198+
}
199+
200+
/// Request payload to create a new task
201+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
202+
#[serde(rename_all = "camelCase")]
203+
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
204+
pub struct TaskCreateRequest {
205+
pub kind: TaskKind,
206+
#[serde(skip_serializing_if = "Option::is_none")]
207+
pub input: Option<Value>,
208+
#[serde(skip_serializing_if = "Option::is_none")]
209+
pub parent_id: Option<String>,
210+
#[serde(skip_serializing_if = "Option::is_none")]
211+
pub depends_on: Option<Vec<String>>,
212+
#[serde(skip_serializing_if = "Option::is_none")]
213+
pub labels: Option<Vec<String>>,
214+
#[serde(skip_serializing_if = "Option::is_none")]
215+
pub metadata: Option<Value>,
216+
#[serde(skip_serializing_if = "Option::is_none")]
217+
pub priority: Option<i32>,
218+
#[serde(skip_serializing_if = "Option::is_none")]
219+
pub batch_group: Option<String>,
220+
/// Protocol-level metadata for the request object
221+
#[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
222+
pub meta: Option<Meta>,
223+
/// Reserved for future SEP extensions
224+
#[serde(skip_serializing_if = "Option::is_none")]
225+
pub extensions: Option<JsonObject>,
226+
}
227+
228+
/// Request payload to update a task's runtime fields
229+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
230+
#[serde(rename_all = "camelCase")]
231+
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
232+
pub struct TaskUpdateRequest {
233+
pub id: String,
234+
#[serde(skip_serializing_if = "Option::is_none")]
235+
pub runtime_state: Option<Value>,
236+
#[serde(skip_serializing_if = "Option::is_none")]
237+
pub cancellation_requested: Option<bool>,
238+
#[serde(skip_serializing_if = "Option::is_none")]
239+
pub labels: Option<Vec<String>>,
240+
#[serde(skip_serializing_if = "Option::is_none")]
241+
pub priority: Option<i32>,
242+
}
243+
244+
/// Incremental progress event for streaming updates
245+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
246+
#[serde(rename_all = "camelCase")]
247+
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
248+
pub struct TaskProgressEvent {
249+
pub id: String,
250+
pub progress: TaskProgress,
251+
/// ISO8601 timestamp for the event
252+
pub timestamp: String,
253+
}
254+
255+
/// Terminal event signaling task completion
256+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
257+
#[serde(rename_all = "camelCase")]
258+
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
259+
pub struct TaskCompletionEvent {
260+
pub id: String,
261+
/// Allowed values: Succeeded, Failed, Cancelled
262+
pub status: TaskStatus,
263+
#[serde(skip_serializing_if = "Option::is_none")]
264+
pub result: Option<TaskResult>,
265+
#[serde(skip_serializing_if = "Option::is_none")]
266+
pub error: Option<TaskError>,
267+
/// ISO8601 timestamp for the event
268+
pub timestamp: String,
269+
}
270+

0 commit comments

Comments
 (0)