1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
61
62
63
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//! Task routing trait for MCP Tasks integration.
//!
//! This trait provides the integration point for task-enabled servers
//! without creating a circular dependency with the `pmcp-tasks` crate.
//! The `pmcp-tasks` crate implements `TaskRouter` and provides the
//! concrete task lifecycle management, while `pmcp` defines the
//! contract here so that `ServerCoreBuilder`
//! can accept a task router without depending on `pmcp-tasks`.
use async_trait::async_trait;
use serde_json::Value;
use crate::error::Result;
/// Trait for routing MCP task requests.
///
/// This trait is implemented by `pmcp-tasks` to handle task lifecycle
/// operations without requiring `pmcp` to depend on `pmcp-tasks`.
///
/// All params and return values use `serde_json::Value` to avoid
/// circular crate dependencies. The implementing crate parses these
/// into strongly-typed structs (e.g., `TaskGetParams`, `CreateTaskResult`).
#[async_trait]
pub trait TaskRouter: Send + Sync {
/// Handle a task-augmented `tools/call` request.
///
/// When a client sends a `tools/call` request with a `task` field,
/// the server delegates to this method instead of calling the tool
/// handler directly. The router creates a task, spawns the tool
/// execution, and returns a `CreateTaskResult` as `Value`.
///
/// Returns the `CreateTaskResult` serialized as `Value`.
async fn handle_task_call(
&self,
tool_name: &str,
arguments: Value,
task_params: Value,
owner_id: &str,
progress_token: Option<Value>,
) -> Result<Value>;
/// Handle `tasks/get` request.
///
/// Returns the task status for the given task ID.
async fn handle_tasks_get(&self, params: Value, owner_id: &str) -> Result<Value>;
/// Handle `tasks/result` request.
///
/// Returns the task result (content) for a completed task.
async fn handle_tasks_result(&self, params: Value, owner_id: &str) -> Result<Value>;
/// Handle `tasks/list` request.
///
/// Returns a list of tasks visible to the given owner.
async fn handle_tasks_list(&self, params: Value, owner_id: &str) -> Result<Value>;
/// Handle `tasks/cancel` request.
///
/// Requests cancellation of the given task.
async fn handle_tasks_cancel(&self, params: Value, owner_id: &str) -> Result<Value>;
/// Resolve owner ID from authentication context fields.
///
/// The owner ID determines task visibility and access control.
/// Implementations typically derive this from the OAuth subject,
/// client ID, or session ID (in order of preference).
fn resolve_owner(
&self,
subject: Option<&str>,
client_id: Option<&str>,
session_id: Option<&str>,
) -> String;
/// Check if a tool requires task augmentation (`taskSupport: required`).
///
/// When a tool has `execution.taskSupport == "required"`, the client
/// must send a `task` field with the `tools/call` request.
fn tool_requires_task(&self, tool_name: &str, tool_execution: Option<&Value>) -> bool;
/// Get the server task capabilities as a `Value` for `experimental.tasks`.
///
/// This is inserted into the server's capabilities during initialization
/// so clients know the server supports the tasks protocol extension.
fn task_capabilities(&self) -> Value;
// --- Workflow task methods (Phase 4: Task-Prompt Bridge) ---
/// Create a workflow-backed task. Returns `CreateTaskResult` as `Value`.
///
/// Called by `TaskWorkflowPromptHandler` when a task-aware workflow prompt
/// is invoked. The implementation creates a task with the workflow's
/// initial progress stored in task variables.
///
/// # Arguments
///
/// * `workflow_name` - Name of the workflow (becomes the task title).
/// * `owner_id` - Owner identity for the new task.
/// * `progress` - Serialized `WorkflowProgress` to store in task variables.
///
/// # Default
///
/// Returns an error indicating workflow tasks are not supported.
async fn create_workflow_task(
&self,
_workflow_name: &str,
_owner_id: &str,
_progress: Value,
) -> Result<Value> {
Err(crate::error::Error::internal(
"workflow tasks not supported by this router",
))
}
/// Update task variables with workflow step results.
///
/// Called after each step completes to persist the step result
/// and updated progress to the task's variable store.
///
/// # Arguments
///
/// * `task_id` - ID of the task to update.
/// * `owner_id` - Owner identity for authorization.
/// * `variables` - JSON object of key-value pairs to set on the task.
///
/// # Default
///
/// Returns an error indicating workflow tasks are not supported.
async fn set_task_variables(
&self,
_task_id: &str,
_owner_id: &str,
_variables: Value,
) -> Result<()> {
Err(crate::error::Error::internal(
"workflow tasks not supported by this router",
))
}
/// Record a tool call result against a workflow task.
///
/// Called by `ServerCore` when a `tools/call` includes `_task_id` in `_meta`.
/// The implementation matches the tool name to a remaining workflow step
/// and updates task variables with the step result and updated progress.
///
/// Best-effort: if the tool does not match any step, the result is stored
/// under `_workflow.extra.<tool_name>` for observability.
///
/// # Default
///
/// Returns `Ok(())` -- no-op for routers that don't support workflow continuation.
async fn handle_workflow_continuation(
&self,
_task_id: &str,
_tool_name: &str,
_tool_result: Value,
_owner_id: &str,
) -> Result<()> {
Ok(())
}
/// Complete a workflow task with final result.
///
/// Called when all steps have been executed (or the workflow determines
/// completion). Sets the task status to `Completed` and stores the
/// final result.
///
/// # Arguments
///
/// * `task_id` - ID of the task to complete.
/// * `owner_id` - Owner identity for authorization.
/// * `result` - Final result value to store on the task.
///
/// # Default
///
/// Returns an error indicating workflow tasks are not supported.
async fn complete_workflow_task(
&self,
_task_id: &str,
_owner_id: &str,
_result: Value,
) -> Result<Value> {
Err(crate::error::Error::internal(
"workflow tasks not supported by this router",
))
}
}