use crate::config::Config;
use rmcp::model::ErrorData as McpError;
use serde_json::Value;
pub(crate) fn status_base_url(config: &Config) -> String {
format!("http://{}", config.config.proxy.http_listen_addr())
}
pub(crate) async fn http_get_json(config: &Config, path: &str) -> Result<Value, McpError> {
let base = status_base_url(config);
let trimmed = path.trim_start_matches('/').to_string();
let url = format!("{base}/{trimmed}");
let api_key = config.config.proxy.api_key.clone();
let path_buf = path.to_string();
let result = tokio::task::spawn_blocking(move || do_get(&url, api_key.as_deref()))
.await
.map_err(|e| McpError::internal_error(format!("spawn_blocking: {e:?}"), None))?;
result.map_err(|e| McpError::internal_error(format!("GET {path_buf} failed: {e:?}"), None))
}
pub(crate) async fn http_post_json(
config: &Config,
path: &str,
body: Value,
) -> Result<Value, McpError> {
let base = status_base_url(config);
let trimmed = path.trim_start_matches('/').to_string();
let url = format!("{base}/{trimmed}");
let api_key = config.config.proxy.api_key.clone();
let path_buf = path.to_string();
let result = tokio::task::spawn_blocking(move || do_post(&url, api_key.as_deref(), &body))
.await
.map_err(|e| McpError::internal_error(format!("spawn_blocking: {e:?}"), None))?;
result.map_err(|e| McpError::internal_error(format!("POST {path_buf} failed: {e:?}"), None))
}
fn do_get(url: &str, api_key: Option<&str>) -> Result<Value, String> {
let client = reqwest::blocking::Client::builder()
.timeout(std::time::Duration::from_secs(10))
.build()
.map_err(|e| format!("build client: {e:?}"))?;
let mut req = client.get(url).header("Accept", "application/json");
if let Some(k) = api_key {
req = req.header("x-api-key", k);
}
let resp = req.send().map_err(|e| format!("send: {e:?}"))?;
let status = resp.status();
let text = resp.text().map_err(|e| format!("read body: {e:?}"))?;
if !status.is_success() {
return Err(format!("status {}: {}", status, text));
}
serde_json::from_str(&text)
.map_err(|e| format!("parse json: {e:?} (body: {})", &text[..text.len().min(200)]))
}
fn do_post(url: &str, api_key: Option<&str>, body: &Value) -> Result<Value, String> {
let client = reqwest::blocking::Client::builder()
.timeout(std::time::Duration::from_secs(10))
.build()
.map_err(|e| format!("build client: {e:?}"))?;
let mut req = client
.post(url)
.header("Accept", "application/json")
.header("Content-Type", "application/json");
if let Some(k) = api_key {
req = req.header("x-api-key", k);
}
let resp = req.json(body).send().map_err(|e| format!("send: {e:?}"))?;
let status = resp.status();
let text = resp.text().map_err(|e| format!("read body: {e:?}"))?;
if !status.is_success() {
return Err(format!("status {}: {}", status, text));
}
serde_json::from_str(&text).map_err(|e| format!("parse json: {e:?}"))
}
pub(crate) async fn panel_overview(config: &Config) -> Result<Value, McpError> {
let metrics = http_get_json(config, "/metrics").await?;
let stats = http_get_json(config, "/stats").await?;
let circuit = http_get_json(config, "/circuit").await?;
Ok(serde_json::json!({
"metrics": metrics,
"stats": stats,
"circuit": circuit,
}))
}
pub(crate) async fn panel_cache(config: &Config) -> Result<Value, McpError> {
let stats = http_get_json(config, "/stats").await?;
let pool = http_get_json(config, "/pool").await?;
let integrity = http_get_json(config, "/cache/integrity").await?;
Ok(serde_json::json!({
"stats": stats,
"pool": pool,
"integrity": integrity,
}))
}
pub(crate) async fn panel_pool(config: &Config) -> Result<Value, McpError> {
http_get_json(config, "/pool").await
}
pub(crate) async fn panel_circuit(config: &Config) -> Result<Value, McpError> {
let circuit = http_get_json(config, "/circuit").await?;
let queue = http_get_json(config, "/queue").await?;
Ok(serde_json::json!({
"circuit": circuit,
"queue": queue,
}))
}
pub(crate) async fn panel_metrics(config: &Config) -> Result<Value, McpError> {
let metrics = http_get_json(config, "/metrics").await?;
let pool = http_get_json(config, "/pool").await?;
let queries = http_get_json(config, "/stats/queries").await?;
Ok(serde_json::json!({
"metrics": metrics,
"pool": pool,
"queries": queries,
}))
}
pub(crate) async fn panel_contexts(config: &Config) -> Result<Value, McpError> {
let current = http_get_json(config, "/contexts/current").await?;
let list = http_get_json(config, "/contexts").await?;
Ok(serde_json::json!({
"current": current,
"contexts": list,
}))
}
pub(crate) async fn panel_peer(config: &Config) -> Result<Value, McpError> {
http_get_json(config, "/peer/status").await
}
pub(crate) async fn panel_tokio(config: &Config) -> Result<Value, McpError> {
http_get_json(config, "/debug/tokio").await
}
pub(crate) async fn panel_health(config: &Config) -> Result<Value, McpError> {
http_get_json(config, "/health").await
}
pub(crate) async fn panel_cache_entries(config: &Config) -> Result<Value, McpError> {
http_get_json(config, "/cache/entries").await
}