use crate::tools::{Tool, ToolResult}; use anyhow::Result; use async_trait::async_trait; use serde::Deserialize; use serde_json::json; use std::env; use std::fs; use std::path::{Path, PathBuf}; use path_clean::PathClean; #[derive(Deserialize)] struct FileArgs { path: String, } fn sanitize_path(path: &str, root: &Path) -> Result { let path = Path::new(path); let path = if path.is_absolute() { path.strip_prefix("/") .map_err(|_| anyhow::anyhow!("Invalid path"))? .to_path_buf() } else { path.to_path_buf() }; let full_path = root.join(path).clean(); if !full_path.starts_with(root) { return Err(anyhow::anyhow!("Path traversal detected")); } Ok(full_path) } pub struct ResourcesListTool; #[async_trait] impl Tool for ResourcesListTool { fn name(&self) -> &'static str { "resources/list" } fn description(&self) -> &'static str { "Lists directory contents." } fn schema(&self) -> serde_json::Value { json!({ "type": "object", "properties": { "path": { "type": "string", "description": "The path to the directory to list." } }, "required": ["path"] }) } async fn execute(&self, args: serde_json::Value) -> Result { let args: FileArgs = serde_json::from_value(args)?; let root = env::current_dir()?; let full_path = sanitize_path(&args.path, &root)?; let entries = fs::read_dir(full_path)?; let mut result = Vec::new(); for entry in entries { let entry = entry?; result.push(entry.file_name().to_string_lossy().to_string()); } Ok(ToolResult::success(serde_json::to_value(result)?)) } } pub struct ResourcesGetTool; #[async_trait] impl Tool for ResourcesGetTool { fn name(&self) -> &'static str { "resources/get" } fn description(&self) -> &'static str { "Reads file content." } fn schema(&self) -> serde_json::Value { json!({ "type": "object", "properties": { "path": { "type": "string", "description": "The path to the file to read." } }, "required": ["path"] }) } async fn execute(&self, args: serde_json::Value) -> Result { let args: FileArgs = serde_json::from_value(args)?; let root = env::current_dir()?; let full_path = sanitize_path(&args.path, &root)?; let content = fs::read_to_string(full_path)?; Ok(ToolResult::success(serde_json::to_value(content)?)) } }