use rmcp::ErrorData;
use rmcp::model::Tool;
use serde_json::{Map, Value, json};
use crate::server::tool_trait::{McpTool, ToolContext, ToolOutput, get_str};
use crate::tool_defs::tool_def;
pub struct CtxTaskTool;
impl McpTool for CtxTaskTool {
fn name(&self) -> &'static str {
"ctx_task"
}
fn tool_def(&self) -> Tool {
tool_def(
"ctx_task",
"Multi-agent task orchestration.\n\
WORKFLOW: action=create → action=list to review → action=update to change state.\n\
Actions: create|update|list|get|cancel|message|info.\n\
States: working|input-required|completed|failed|canceled.\n\
ANTIPATTERN: not for code execution — use ctx_shell or ctx_execute.",
json!({
"type": "object",
"properties": {
"action": {
"type": "string",
"enum": ["create", "update", "list", "get", "cancel", "message", "info"],
"description": "create|update|list|get|cancel|message|info"
},
"task_id": { "type": "string", "description": "Task ID (for update|get|cancel|message)" },
"to_agent": { "type": "string", "description": "Target agent ID (for create)" },
"description": { "type": "string", "description": "Task description (for create)" },
"state": { "type": "string", "description": "New state (working|input-required|completed|failed|canceled)" },
"message": { "type": "string", "description": "Message or reason" }
},
"required": ["action"],
"allOf": [
{
"if": { "properties": { "action": { "const": "create" } }, "required": ["action"] },
"then": { "required": ["action", "to_agent"] }
},
{
"if": { "properties": { "action": { "const": "update" } }, "required": ["action"] },
"then": { "required": ["action", "task_id", "state"] }
},
{
"if": { "properties": { "action": { "const": "get" } }, "required": ["action"] },
"then": { "required": ["action", "task_id"] }
},
{
"if": { "properties": { "action": { "const": "cancel" } }, "required": ["action"] },
"then": { "required": ["action", "task_id"] }
},
{
"if": { "properties": { "action": { "const": "message" } }, "required": ["action"] },
"then": { "required": ["action", "task_id", "message"] }
}
]
}),
)
}
fn handle(
&self,
args: &Map<String, Value>,
ctx: &ToolContext,
) -> Result<ToolOutput, ErrorData> {
let action = get_str(args, "action").unwrap_or_else(|| "list".to_string());
let current_agent_id = ctx
.agent_id
.as_ref()
.map(|a| a.blocking_read().clone())
.unwrap_or_default();
let task_id = get_str(args, "task_id");
let to_agent = get_str(args, "to_agent");
let description = get_str(args, "description");
let state = get_str(args, "state");
let message = get_str(args, "message");
let result = crate::tools::ctx_task::handle(
&action,
current_agent_id.as_deref(),
task_id.as_deref(),
to_agent.as_deref(),
description.as_deref(),
state.as_deref(),
message.as_deref(),
);
Ok(ToolOutput {
text: result,
original_tokens: 0,
saved_tokens: 0,
mode: Some(action),
path: None,
changed: false,
shell_outcome: None,
content_blocks: None,
})
}
}