use axum::{extract::State, http::StatusCode, response::IntoResponse};
use serde::Deserialize;
use serde_json::{Value, json};
use crate::routes::sessions::map_tool_result;
use crate::server::AppState;
async fn call(state: &AppState, tool: &str, args: Value) -> axum::response::Response {
let Some(handle) = state.mcp_handles().get("trusty-mpm").cloned() else {
tracing::error!("config route: no MCP handle registered for trusty-mpm");
return StatusCode::SERVICE_UNAVAILABLE.into_response();
};
map_tool_result(handle.call_tool_checked(tool, args).await)
}
pub async fn get_handler(State(state): State<AppState>) -> axum::response::Response {
call(&state, "config_read", json!({})).await
}
#[derive(Deserialize)]
pub struct ConfigBody {
#[serde(default)]
workspace_root_template: Option<String>,
#[serde(default)]
auto_resume: Option<bool>,
#[serde(default)]
default_model: Option<String>,
}
pub async fn post_handler(
State(state): State<AppState>,
axum::Json(body): axum::Json<ConfigBody>,
) -> axum::response::Response {
let mut args = serde_json::Map::new();
if let Some(t) = body.workspace_root_template {
args.insert("workspace_root_template".to_string(), json!(t));
}
if let Some(a) = body.auto_resume {
args.insert("auto_resume".to_string(), json!(a));
}
if let Some(m) = body.default_model {
args.insert("default_model".to_string(), json!(m));
}
call(&state, "config_write", Value::Object(args)).await
}
#[cfg(test)]
mod tests {
use super::*;
use axum::body::Body;
use axum::http::{Request, StatusCode};
use tower::ServiceExt;
use crate::server::{AppState, build_router};
fn router() -> axum::Router {
build_router(AppState::new(vec![]))
}
async fn assert_not_500(method: &str, uri: &str, body: Body) {
let req = Request::builder()
.method(method)
.uri(uri)
.header("content-type", "application/json")
.body(body)
.expect("request");
let resp = router().oneshot(req).await.expect("response");
assert_ne!(
resp.status(),
StatusCode::INTERNAL_SERVER_ERROR,
"{method} {uri} must not 500 when binary absent (got {})",
resp.status()
);
}
#[tokio::test]
async fn config_get_absent_binary_does_not_500() {
assert_not_500("GET", "/api/console/config/mpm", Body::empty()).await;
}
#[tokio::test]
async fn config_post_absent_binary_does_not_500() {
let body = Body::from(json!({ "auto_resume": true }).to_string());
assert_not_500("POST", "/api/console/config/mpm", body).await;
}
}