agentmux_mcp/tools/
cron.rs1use super::*;
7
8pub(super) async fn call(name: &str, arguments: &Value, cx: &ToolCtx<'_>) -> Result<String> {
9 let ToolCtx { local_url, auth_key, block_id, client, .. } = *cx;
10 match name {
11 "CronCreate" => {
12 require_agent_env(local_url, auth_key, block_id)?;
13 let name = arguments.get("name").and_then(|v| v.as_str()).filter(|s| !s.is_empty())
14 .ok_or_else(|| anyhow::anyhow!("missing required parameter: name"))?;
15 let expression = arguments.get("expression").and_then(|v| v.as_str()).filter(|s| !s.is_empty())
16 .ok_or_else(|| anyhow::anyhow!("missing required parameter: expression"))?;
17 let prompt = arguments.get("prompt").and_then(|v| v.as_str()).filter(|s| !s.is_empty())
18 .ok_or_else(|| anyhow::anyhow!("missing required parameter: prompt"))?;
19 let target = arguments.get("to").and_then(|v| v.as_str()).filter(|s| !s.is_empty())
20 .ok_or_else(|| anyhow::anyhow!("missing required parameter: to (target agent id)"))?;
21 let max_fires = arguments.get("max_fires").and_then(|v| v.as_i64()).filter(|&n| n > 0);
22 let max_age_secs = arguments.get("max_age_secs").and_then(|v| v.as_i64()).filter(|&n| n > 0);
23 let self_id = std::env::var("AGENTMUX_AGENT_ID").ok().filter(|s| !s.is_empty()).unwrap_or_default();
24 let target_uid = resolve_agent_name_at_boundary(&client, local_url, auth_key, target).await?;
27
28 let url = format!("{}/agentmux/cron", local_url.trim_end_matches('/'));
29 let body = serde_json::json!({
30 "name": name, "expression": expression, "prompt": prompt,
31 "target": target, "target_uid": target_uid.unwrap_or_default(),
32 "created_by": self_id, "max_fires": max_fires,
33 "max_age_secs": max_age_secs,
34 });
35 let resp = client.post(&url).header(AUTH_KEY_HEADER, auth_key).json(&body).send().await
36 .map_err(|e| anyhow::anyhow!("cron create request failed: {e}"))?;
37 let status = resp.status();
38 let text = resp.text().await.unwrap_or_default();
39 if !status.is_success() {
40 anyhow::bail!("CronCreate failed: HTTP {status} — {text}");
41 }
42 let v: serde_json::Value = serde_json::from_str(&text).unwrap_or_default();
43 let job = &v["job"];
44 Ok(format!(
45 "Created cron job '{}' (id={})\nExpression: {} UTC\nNext fire: {}\nTarget: {}",
46 job["name"].as_str().unwrap_or(name),
47 job["id"].as_str().unwrap_or("?"),
48 expression,
49 job["next_fire"].as_str().unwrap_or("unknown"),
50 target,
51 ))
52 }
53 "CronDelete" => {
54 require_agent_env(local_url, auth_key, block_id)?;
55 let id = arguments.get("id").and_then(|v| v.as_str()).filter(|s| !s.is_empty())
56 .ok_or_else(|| anyhow::anyhow!("missing required parameter: id"))?;
57 let url = format!("{}/agentmux/cron/{}", local_url.trim_end_matches('/'), id);
58 let resp = client.delete(&url).header(AUTH_KEY_HEADER, auth_key).send().await
59 .map_err(|e| anyhow::anyhow!("cron delete request failed: {e}"))?;
60 let status = resp.status();
61 if status.as_u16() == 404 {
62 return Ok(format!("Job '{id}' not found (already deleted or wrong id)"));
63 }
64 if !status.is_success() {
65 let text = resp.text().await.unwrap_or_default();
66 anyhow::bail!("CronDelete failed: HTTP {status} — {text}");
67 }
68 Ok(format!("Deleted cron job {id}"))
69 }
70 "CronList" => {
71 require_agent_env(local_url, auth_key, block_id)?;
72 let url = format!("{}/agentmux/cron", local_url.trim_end_matches('/'));
73 let resp = client.get(&url).header(AUTH_KEY_HEADER, auth_key).send().await
74 .map_err(|e| anyhow::anyhow!("cron list request failed: {e}"))?;
75 let status = resp.status();
76 let text = resp.text().await.unwrap_or_default();
77 if !status.is_success() {
78 anyhow::bail!("CronList failed: HTTP {status} — {text}");
79 }
80 let v: serde_json::Value = serde_json::from_str(&text).unwrap_or_default();
81 let jobs = v["jobs"].as_array().cloned().unwrap_or_default();
82 if jobs.is_empty() {
83 return Ok("No cron jobs configured.".to_string());
84 }
85 let mut lines = vec![format!("{} cron job(s):", jobs.len())];
86 for j in &jobs {
87 let status_str = if j["enabled"].as_bool().unwrap_or(false) { "enabled" } else { "paused" };
88 let next = j["next_fire"].as_str().unwrap_or("—");
89 let fires = j["fire_count"].as_i64().unwrap_or(0);
90 let max = j["max_fires"].as_i64().map(|n| format!("/{n}")).unwrap_or_default();
91 let age_bound = j["expires_in_secs"].as_i64().map(|n| format!(" expires_in={n}s")).unwrap_or_default();
92 lines.push(format!(
93 " {} {} [{}] fires={fires}{max} next={} expr='{}'{age_bound}",
94 j["id"].as_str().unwrap_or("?"),
95 j["name"].as_str().unwrap_or("?"),
96 status_str,
97 next,
98 j["expression"].as_str().unwrap_or("?"),
99 ));
100 }
101 Ok(lines.join("\n"))
102 }
103 "CronPause" => {
104 require_agent_env(local_url, auth_key, block_id)?;
105 let id = arguments.get("id").and_then(|v| v.as_str()).filter(|s| !s.is_empty())
106 .ok_or_else(|| anyhow::anyhow!("missing required parameter: id"))?;
107 cron_set_enabled(client, local_url, auth_key, id, "pause").await
108 }
109 "CronResume" => {
110 require_agent_env(local_url, auth_key, block_id)?;
111 let id = arguments.get("id").and_then(|v| v.as_str()).filter(|s| !s.is_empty())
112 .ok_or_else(|| anyhow::anyhow!("missing required parameter: id"))?;
113 cron_set_enabled(client, local_url, auth_key, id, "resume").await
114 }
115 _ => Err(not_in_family()),
116 }
117}