fix: align task response types with MCP spec (#658)
This commit is contained in:
parent
453032faed
commit
08a5b0551b
3 changed files with 349 additions and 134 deletions
|
|
@ -116,15 +116,15 @@ impl<H: ServerHandler> Service<RoleServer> for H {
|
|||
ClientRequest::GetTaskInfoRequest(request) => self
|
||||
.get_task_info(request.params, context)
|
||||
.await
|
||||
.map(ServerResult::GetTaskInfoResult),
|
||||
.map(ServerResult::GetTaskResult),
|
||||
ClientRequest::GetTaskResultRequest(request) => self
|
||||
.get_task_result(request.params, context)
|
||||
.await
|
||||
.map(ServerResult::TaskResult),
|
||||
.map(ServerResult::GetTaskPayloadResult),
|
||||
ClientRequest::CancelTaskRequest(request) => self
|
||||
.cancel_task(request.params, context)
|
||||
.await
|
||||
.map(ServerResult::empty),
|
||||
.map(ServerResult::CancelTaskResult),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -339,7 +339,8 @@ pub trait ServerHandler: Sized + Send + Sync + 'static {
|
|||
&self,
|
||||
request: GetTaskInfoParams,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<GetTaskInfoResult, McpError>> + Send + '_ {
|
||||
) -> impl Future<Output = Result<GetTaskResult, McpError>> + Send + '_ {
|
||||
let _ = (request, context);
|
||||
std::future::ready(Err(McpError::method_not_found::<GetTaskInfoMethod>()))
|
||||
}
|
||||
|
||||
|
|
@ -347,7 +348,7 @@ pub trait ServerHandler: Sized + Send + Sync + 'static {
|
|||
&self,
|
||||
request: GetTaskResultParams,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<TaskResult, McpError>> + Send + '_ {
|
||||
) -> impl Future<Output = Result<GetTaskPayloadResult, McpError>> + Send + '_ {
|
||||
let _ = (request, context);
|
||||
std::future::ready(Err(McpError::method_not_found::<GetTaskResultMethod>()))
|
||||
}
|
||||
|
|
@ -356,7 +357,7 @@ pub trait ServerHandler: Sized + Send + Sync + 'static {
|
|||
&self,
|
||||
request: CancelTaskParams,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<(), McpError>> + Send + '_ {
|
||||
) -> impl Future<Output = Result<CancelTaskResult, McpError>> + Send + '_ {
|
||||
let _ = (request, context);
|
||||
std::future::ready(Err(McpError::method_not_found::<CancelTaskMethod>()))
|
||||
}
|
||||
|
|
@ -543,7 +544,7 @@ macro_rules! impl_server_handler_for_wrapper {
|
|||
&self,
|
||||
request: GetTaskInfoParams,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<GetTaskInfoResult, McpError>> + Send + '_ {
|
||||
) -> impl Future<Output = Result<GetTaskResult, McpError>> + Send + '_ {
|
||||
(**self).get_task_info(request, context)
|
||||
}
|
||||
|
||||
|
|
@ -551,7 +552,7 @@ macro_rules! impl_server_handler_for_wrapper {
|
|||
&self,
|
||||
request: GetTaskResultParams,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<TaskResult, McpError>> + Send + '_ {
|
||||
) -> impl Future<Output = Result<GetTaskPayloadResult, McpError>> + Send + '_ {
|
||||
(**self).get_task_result(request, context)
|
||||
}
|
||||
|
||||
|
|
@ -559,7 +560,7 @@ macro_rules! impl_server_handler_for_wrapper {
|
|||
&self,
|
||||
request: CancelTaskParams,
|
||||
context: RequestContext<RoleServer>,
|
||||
) -> impl Future<Output = Result<(), McpError>> + Send + '_ {
|
||||
) -> impl Future<Output = Result<CancelTaskResult, McpError>> + Send + '_ {
|
||||
(**self).cancel_task(request, context)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2537,14 +2537,9 @@ impl RequestParamsMeta for CancelTaskParams {
|
|||
/// Deprecated: Use [`CancelTaskParams`] instead (SEP-1319 compliance).
|
||||
#[deprecated(since = "0.13.0", note = "Use CancelTaskParams instead")]
|
||||
pub type CancelTaskParam = CancelTaskParams;
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct GetTaskInfoResult {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub task: Option<crate::model::Task>,
|
||||
}
|
||||
/// Deprecated: Use [`GetTaskResult`] instead (spec alignment).
|
||||
#[deprecated(since = "0.15.0", note = "Use GetTaskResult instead")]
|
||||
pub type GetTaskInfoResult = GetTaskResult;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
|
@ -2720,9 +2715,10 @@ ts_union!(
|
|||
| EmptyResult
|
||||
| CreateTaskResult
|
||||
| ListTasksResult
|
||||
| GetTaskInfoResult
|
||||
| TaskResult
|
||||
| GetTaskResult
|
||||
| CancelTaskResult
|
||||
| CustomResult
|
||||
| GetTaskPayloadResult
|
||||
;
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use super::Meta;
|
||||
|
||||
/// Canonical task lifecycle status as defined by SEP-1686.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
|
|
@ -19,21 +21,10 @@ pub enum TaskStatus {
|
|||
Cancelled,
|
||||
}
|
||||
|
||||
/// Final result for a succeeded task (returned from `tasks/result`).
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct TaskResult {
|
||||
/// MIME type or custom content-type identifier.
|
||||
pub content_type: String,
|
||||
/// The actual result payload, matching the underlying request's schema.
|
||||
pub value: Value,
|
||||
/// Optional short summary for UI surfaces.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub summary: Option<String>,
|
||||
}
|
||||
|
||||
/// Primary Task object that surfaces metadata during the task lifecycle.
|
||||
///
|
||||
/// Per spec, `lastUpdatedAt` and `ttl` are required fields.
|
||||
/// `ttl` is nullable (`null` means unlimited retention).
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
|
|
@ -48,10 +39,9 @@ pub struct Task {
|
|||
/// ISO-8601 creation timestamp.
|
||||
pub created_at: String,
|
||||
/// ISO-8601 timestamp for the most recent status change.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub last_updated_at: Option<String>,
|
||||
pub last_updated_at: String,
|
||||
/// Retention window in milliseconds that the receiver agreed to honor.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
/// `None` (serialized as `null`) means unlimited retention.
|
||||
pub ttl: Option<u64>,
|
||||
/// Suggested polling interval (milliseconds).
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
|
@ -66,6 +56,43 @@ pub struct CreateTaskResult {
|
|||
pub task: Task,
|
||||
}
|
||||
|
||||
/// Response to a `tasks/get` request.
|
||||
///
|
||||
/// Per spec, `GetTaskResult = allOf[Result, Task]` — the Task fields are
|
||||
/// flattened at the top level, not nested under a `task` key.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct GetTaskResult {
|
||||
#[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
|
||||
pub meta: Option<Meta>,
|
||||
#[serde(flatten)]
|
||||
pub task: Task,
|
||||
}
|
||||
|
||||
/// Response to a `tasks/result` request.
|
||||
///
|
||||
/// Per spec, the result structure matches the original request type
|
||||
/// (e.g., `CallToolResult` for `tools/call`). This is represented as
|
||||
/// an open object. The payload is the original request's result
|
||||
/// serialized as a JSON value.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct GetTaskPayloadResult(pub Value);
|
||||
|
||||
/// Response to a `tasks/cancel` request.
|
||||
///
|
||||
/// Per spec, `CancelTaskResult = allOf[Result, Task]` — same shape as `GetTaskResult`.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub struct CancelTaskResult {
|
||||
#[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
|
||||
pub meta: Option<Meta>,
|
||||
#[serde(flatten)]
|
||||
pub task: Task,
|
||||
}
|
||||
|
||||
/// Paginated list of tasks
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
|
|
|||
Loading…
Reference in a new issue