use std::path::Path;
use serde::Serialize;
use crate::config::ServerConfig;
use crate::protocol::{McpErrorCode, McpErrorResponse, ToolResult};
#[derive(Debug, Serialize)]
struct ListCachesResponse {
caches: Vec<CacheEntry>,
}
#[derive(Debug, Serialize)]
struct CacheEntry {
path: String,
has_manifest: bool,
}
pub async fn handle(config: &ServerConfig) -> ToolResult {
match enumerate_caches(&config.cache_root) {
Ok(json) => ToolResult::text(json),
Err(mcp_err) => mcp_err.into(),
}
}
fn enumerate_caches(cache_root: &Path) -> Result<String, McpErrorResponse> {
if !cache_root.is_dir() {
return Err(McpErrorResponse::canonical(McpErrorCode::CacheMissing));
}
let entries = std::fs::read_dir(cache_root).map_err(|e| {
eprintln!("Cannot read cache root: {e}");
McpErrorResponse::canonical(McpErrorCode::IoError)
})?;
let mut caches = Vec::new();
for entry in entries {
let entry = entry.map_err(|e| {
eprintln!("Error reading directory entry: {e}");
McpErrorResponse::canonical(McpErrorCode::IoError)
})?;
let file_type = entry.file_type().map_err(|e| {
eprintln!("Cannot read file type: {e}");
McpErrorResponse::canonical(McpErrorCode::IoError)
})?;
if !file_type.is_dir() {
continue;
}
let name = entry.file_name().to_string_lossy().to_string();
let manifest_path = entry.path().join("manifest.json");
let has_manifest = match std::fs::symlink_metadata(&manifest_path) {
Ok(meta) => meta.is_file(),
Err(e) => {
if e.kind() == std::io::ErrorKind::NotFound {
false
} else {
return Err(McpErrorResponse::canonical(McpErrorCode::IoError));
}
}
};
caches.push(CacheEntry {
path: name,
has_manifest,
});
}
caches.sort_by(|a, b| a.path.cmp(&b.path));
let payload = ListCachesResponse { caches };
serde_json::to_string(&payload).map_err(|e| {
eprintln!("Serialization failed: {e}");
McpErrorResponse::canonical(McpErrorCode::InternalError)
})
}