Skip to content

Commit 15121c8

Browse files
authored
feat: add list_directory_with_sizes MCP Tool for Directory Listing with File Sizes (#27)
* feat: add new list_directory_with_sizes tool * feat: add list_directory_with_sizes * chore: fix clippy warnings
1 parent 01f956e commit 15121c8

File tree

3 files changed

+99
-1
lines changed

3 files changed

+99
-1
lines changed

src/handler.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ impl ServerHandler for MyServerHandler {
150150
FileSystemTools::SearchFilesContentTool(params) => {
151151
SearchFilesContentTool::run_tool(params, &self.fs_service).await
152152
}
153+
FileSystemTools::ListDirectoryWithSizesTool(params) => {
154+
ListDirectoryWithSizesTool::run_tool(params, &self.fs_service).await
155+
}
153156
}
154157
}
155158
}

src/tools.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod edit_file;
44
mod get_file_info;
55
mod list_allowed_directories;
66
mod list_directory;
7+
mod list_directory_with_sizes;
78
mod move_file;
89
mod read_files;
910
mod read_multiple_files;
@@ -18,6 +19,7 @@ pub use edit_file::{EditFileTool, EditOperation};
1819
pub use get_file_info::GetFileInfoTool;
1920
pub use list_allowed_directories::ListAllowedDirectoriesTool;
2021
pub use list_directory::ListDirectoryTool;
22+
pub use list_directory_with_sizes::ListDirectoryWithSizesTool;
2123
pub use move_file::MoveFileTool;
2224
pub use read_files::ReadFileTool;
2325
pub use read_multiple_files::ReadMultipleFilesTool;
@@ -45,7 +47,8 @@ tool_box!(
4547
ZipFilesTool,
4648
UnzipFileTool,
4749
ZipDirectoryTool,
48-
SearchFilesContentTool
50+
SearchFilesContentTool,
51+
ListDirectoryWithSizesTool
4952
]
5053
);
5154

@@ -68,6 +71,7 @@ impl FileSystemTools {
6871
| FileSystemTools::ListDirectoryTool(_)
6972
| FileSystemTools::ReadMultipleFilesTool(_)
7073
| FileSystemTools::SearchFilesContentTool(_)
74+
| FileSystemTools::ListDirectoryWithSizesTool(_)
7175
| FileSystemTools::SearchFilesTool(_) => false,
7276
}
7377
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use rust_mcp_sdk::macros::{mcp_tool, JsonSchema};
2+
use rust_mcp_sdk::schema::{schema_utils::CallToolError, CallToolResult};
3+
use std::fmt::Write;
4+
use std::path::Path;
5+
6+
use crate::fs_service::utils::format_bytes;
7+
use crate::fs_service::FileSystemService;
8+
9+
#[mcp_tool(
10+
name = "list_directory_with_sizes",
11+
description = concat!("Get a detailed listing of all files and directories in a specified path, including sizes. " ,
12+
"Results clearly distinguish between files and directories with [FILE] and [DIR] prefixes. " ,
13+
"This tool is useful for understanding directory structure and " ,
14+
"finding specific files within a directory. Only works within allowed directories."),
15+
destructive_hint = false,
16+
idempotent_hint = false,
17+
open_world_hint = false,
18+
read_only_hint = true
19+
)]
20+
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, JsonSchema)]
21+
pub struct ListDirectoryWithSizesTool {
22+
/// The path of the directory to list.
23+
pub path: String,
24+
}
25+
26+
impl ListDirectoryWithSizesTool {
27+
async fn format_directory_entries(
28+
&self,
29+
mut entries: Vec<tokio::fs::DirEntry>,
30+
) -> std::result::Result<String, CallToolError> {
31+
let mut file_count = 0;
32+
let mut dir_count = 0;
33+
let mut total_size: u64 = 0;
34+
35+
// Estimate initial capacity: assume ~50 bytes per entry + summary
36+
let mut output = String::with_capacity(entries.len() * 50 + 120);
37+
38+
// Sort entries by file name
39+
entries.sort_by_key(|a| a.file_name());
40+
41+
// build the output string
42+
for entry in &entries {
43+
let file_name = entry.file_name();
44+
let file_name = file_name.to_string_lossy();
45+
46+
if entry.path().is_dir() {
47+
writeln!(output, "[DIR] {file_name:<30}").map_err(CallToolError::new)?;
48+
dir_count += 1;
49+
} else if entry.path().is_file() {
50+
let metadata = entry.metadata().await.map_err(CallToolError::new)?;
51+
52+
let file_size = metadata.len();
53+
writeln!(
54+
output,
55+
"[FILE] {:<30} {:>10}",
56+
file_name,
57+
format_bytes(file_size)
58+
)
59+
.map_err(CallToolError::new)?;
60+
file_count += 1;
61+
total_size += file_size;
62+
}
63+
}
64+
65+
// Append summary
66+
writeln!(
67+
output,
68+
"\nTotal: {file_count} files, {dir_count} directories"
69+
)
70+
.map_err(CallToolError::new)?;
71+
writeln!(output, "Total size: {}", format_bytes(total_size)).map_err(CallToolError::new)?;
72+
73+
Ok(output)
74+
}
75+
76+
pub async fn run_tool(
77+
params: Self,
78+
context: &FileSystemService,
79+
) -> std::result::Result<CallToolResult, CallToolError> {
80+
let entries = context
81+
.list_directory(Path::new(&params.path))
82+
.await
83+
.map_err(CallToolError::new)?;
84+
85+
let output = params
86+
.format_directory_entries(entries)
87+
.await
88+
.map_err(CallToolError::new)?;
89+
Ok(CallToolResult::text_content(output, None))
90+
}
91+
}

0 commit comments

Comments
 (0)