From bf31947c9204732b74f871f9a97254e31eba7b72 Mon Sep 17 00:00:00 2001 From: Ali Hashemi Date: Fri, 4 Jul 2025 20:55:40 -0300 Subject: [PATCH 1/3] feat: add new list_directory_with_sizes tool --- src/handler.rs | 3 +++ src/tools.rs | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/handler.rs b/src/handler.rs index f8637a7..91fad90 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -150,6 +150,9 @@ impl ServerHandler for MyServerHandler { FileSystemTools::SearchFilesContentTool(params) => { SearchFilesContentTool::run_tool(params, &self.fs_service).await } + FileSystemTools::ListDirectoryWithSizesTool(params) => { + ListDirectoryWithSizesTool::run_tool(params, &self.fs_service).await + } } } } diff --git a/src/tools.rs b/src/tools.rs index 3fbab26..fea0583 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -4,6 +4,7 @@ mod edit_file; mod get_file_info; mod list_allowed_directories; mod list_directory; +mod list_directory_with_sizes; mod move_file; mod read_files; mod read_multiple_files; @@ -18,6 +19,7 @@ pub use edit_file::{EditFileTool, EditOperation}; pub use get_file_info::GetFileInfoTool; pub use list_allowed_directories::ListAllowedDirectoriesTool; pub use list_directory::ListDirectoryTool; +pub use list_directory_with_sizes::ListDirectoryWithSizesTool; pub use move_file::MoveFileTool; pub use read_files::ReadFileTool; pub use read_multiple_files::ReadMultipleFilesTool; @@ -45,7 +47,8 @@ tool_box!( ZipFilesTool, UnzipFileTool, ZipDirectoryTool, - SearchFilesContentTool + SearchFilesContentTool, + ListDirectoryWithSizesTool ] ); @@ -68,6 +71,7 @@ impl FileSystemTools { | FileSystemTools::ListDirectoryTool(_) | FileSystemTools::ReadMultipleFilesTool(_) | FileSystemTools::SearchFilesContentTool(_) + | FileSystemTools::ListDirectoryWithSizesTool(_) | FileSystemTools::SearchFilesTool(_) => false, } } From 3500a02dd4dd8de4713ed358f50668175a0552b5 Mon Sep 17 00:00:00 2001 From: Ali Hashemi Date: Fri, 4 Jul 2025 20:55:55 -0300 Subject: [PATCH 2/3] feat: add list_directory_with_sizes --- src/tools/list_directory_with_sizes.rs | 92 ++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 src/tools/list_directory_with_sizes.rs diff --git a/src/tools/list_directory_with_sizes.rs b/src/tools/list_directory_with_sizes.rs new file mode 100644 index 0000000..4bad383 --- /dev/null +++ b/src/tools/list_directory_with_sizes.rs @@ -0,0 +1,92 @@ +use rust_mcp_sdk::macros::{mcp_tool, JsonSchema}; +use rust_mcp_sdk::schema::{schema_utils::CallToolError, CallToolResult}; +use std::fmt::Write; +use std::path::Path; + +use crate::fs_service::utils::format_bytes; +use crate::fs_service::FileSystemService; + +#[mcp_tool( + name = "list_directory_with_sizes", + description = concat!("Get a detailed listing of all files and directories in a specified path, including sizes. " , + "Results clearly distinguish between files and directories with [FILE] and [DIR] prefixes. " , + "This tool is useful for understanding directory structure and " , + "finding specific files within a directory. Only works within allowed directories."), + destructive_hint = false, + idempotent_hint = false, + open_world_hint = false, + read_only_hint = true +)] +#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, JsonSchema)] +pub struct ListDirectoryWithSizesTool { + /// The path of the directory to list. + pub path: String, +} + +impl ListDirectoryWithSizesTool { + async fn format_directory_entries( + &self, + mut entries: Vec, + ) -> std::result::Result { + let mut file_count = 0; + let mut dir_count = 0; + let mut total_size: u64 = 0; + + // Estimate initial capacity: assume ~50 bytes per entry + summary + let mut output = String::with_capacity(entries.len() * 50 + 120); + + // Sort entries by file name + entries.sort_by(|a, b| a.file_name().cmp(&b.file_name())); + + // build the output string + for entry in &entries { + let file_name = entry.file_name(); + let file_name = file_name.to_string_lossy(); + + if entry.path().is_dir() { + writeln!(output, "[DIR] {:<30}", file_name).map_err(CallToolError::new)?; + dir_count += 1; + } else if entry.path().is_file() { + let metadata = entry.metadata().await.map_err(CallToolError::new)?; + + let file_size = metadata.len(); + writeln!( + output, + "[FILE] {:<30} {:>10}", + file_name, + format_bytes(file_size) + ) + .map_err(CallToolError::new)?; + file_count += 1; + total_size += file_size; + } + } + + // Append summary + writeln!( + output, + "\nTotal: {} files, {} directories", + file_count, dir_count + ) + .map_err(CallToolError::new)?; + writeln!(output, "Total size: {}", format_bytes(total_size)).map_err(CallToolError::new)?; + + Ok(output) + } + + pub async fn run_tool( + params: Self, + context: &FileSystemService, + ) -> std::result::Result { + let entries = context + .list_directory(Path::new(¶ms.path)) + .await + .map_err(CallToolError::new)?; + + let output = params + .format_directory_entries(entries) + .await + .map_err(CallToolError::new)?; + Ok(CallToolResult::text_content(output, None)) + } +} From 48594f3ae6c4f6ee6b60bc458fe31c3d99e11903 Mon Sep 17 00:00:00 2001 From: Ali Hashemi Date: Fri, 4 Jul 2025 21:32:59 -0300 Subject: [PATCH 3/3] chore: fix clippy warnings --- src/tools/list_directory_with_sizes.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/tools/list_directory_with_sizes.rs b/src/tools/list_directory_with_sizes.rs index 4bad383..e36801d 100644 --- a/src/tools/list_directory_with_sizes.rs +++ b/src/tools/list_directory_with_sizes.rs @@ -36,7 +36,7 @@ impl ListDirectoryWithSizesTool { let mut output = String::with_capacity(entries.len() * 50 + 120); // Sort entries by file name - entries.sort_by(|a, b| a.file_name().cmp(&b.file_name())); + entries.sort_by_key(|a| a.file_name()); // build the output string for entry in &entries { @@ -44,7 +44,7 @@ impl ListDirectoryWithSizesTool { let file_name = file_name.to_string_lossy(); if entry.path().is_dir() { - writeln!(output, "[DIR] {:<30}", file_name).map_err(CallToolError::new)?; + writeln!(output, "[DIR] {file_name:<30}").map_err(CallToolError::new)?; dir_count += 1; } else if entry.path().is_file() { let metadata = entry.metadata().await.map_err(CallToolError::new)?; @@ -65,8 +65,7 @@ impl ListDirectoryWithSizesTool { // Append summary writeln!( output, - "\nTotal: {} files, {} directories", - file_count, dir_count + "\nTotal: {file_count} files, {dir_count} directories" ) .map_err(CallToolError::new)?; writeln!(output, "Total size: {}", format_bytes(total_size)).map_err(CallToolError::new)?;