agentmux_srv/server/app_api/
mcp_health.rs1use axum::extract::State;
14use axum::http::StatusCode;
15use axum::response::{IntoResponse, Response};
16use axum::{Extension, Json};
17use serde::Deserialize;
18use serde_json::json;
19
20use crate::server::caller::Caller;
21use crate::server::state::AppState;
22
23#[derive(Debug, Deserialize)]
24pub(crate) struct SelfRequest {
25 agent: String,
27}
28
29fn own_pane(state: &AppState, caller: Option<&Caller>, agent: &str) -> Option<String> {
30 crate::server::actor::own_pane_by_token(&state.mstore, caller, agent.trim())
31}
32
33fn no_pane() -> Response {
34 let error = "no pane for this agent token: run this from the agent's own shell \
35 (it needs AGENTMUX_AGENT_TOKEN and AGENTMUX_AGENT_SLUG)";
36 (StatusCode::UNAUTHORIZED, Json(json!({ "error": error }))).into_response()
37}
38
39pub(crate) async fn handle_mcp_health(
41 State(state): State<AppState>,
42 caller: Option<Extension<Caller>>,
43 Json(req): Json<SelfRequest>,
44) -> Response {
45 let Some(block_id) = own_pane(&state, caller.as_deref(), &req.agent) else {
46 return no_pane();
47 };
48 let health = crate::backend::mcp_health::snapshot(&block_id);
49 Json(json!({
50 "block_id": block_id,
51 "health": health,
52 "safe_profile": crate::backend::mcp_health::is_safe_profile(&block_id),
53 "auto": crate::backend::mcp_health::auto_enabled(),
54 }))
55 .into_response()
56}
57
58pub(crate) async fn handle_mcp_heal(
62 State(state): State<AppState>,
63 caller: Option<Extension<Caller>>,
64 Json(req): Json<SelfRequest>,
65) -> Response {
66 use crate::backend::blockcontroller::persistent::PersistentSubprocessController;
67 let Some(block_id) = own_pane(&state, caller.as_deref(), &req.agent) else {
68 return no_pane();
69 };
70 let Some(ctrl) = crate::backend::blockcontroller::get_controller(&block_id) else {
71 return Json(json!({ "status": "no_process" })).into_response();
72 };
73 let Some(ctrl) = ctrl.as_any().downcast_ref::<PersistentSubprocessController>() else {
74 return Json(json!({ "status": "no_process" })).into_response();
75 };
76 let outcome = ctrl.mcp_heal_now();
77 tracing::info!(block_id = %block_id, agent = %req.agent, outcome = ?outcome, "agentmux-mcp doctor --heal");
78 match outcome {
79 Ok(()) => (StatusCode::ACCEPTED, Json(json!({ "status": "healing" }))).into_response(),
80 Err(why) => Json(json!({ "status": why })).into_response(),
81 }
82}