2025-03-16 17:17:01 +08:00
# ifndef HUMANUS_TOOL_FILESYSTEM_H
# define HUMANUS_TOOL_FILESYSTEM_H
# include "base.h"
namespace humanus {
2025-03-20 01:12:15 +08:00
// https://github.com/modelcontextprotocol/servers/tree/HEAD/src/filesystem
2025-03-28 17:57:20 +08:00
struct Filesystem : BaseMCPTool {
2025-03-16 17:17:01 +08:00
inline static const std : : string name_ = " filesystem " ;
2025-03-19 18:44:54 +08:00
inline static const std : : string description_ = " ## Features \n \n - Read/write files \n - Create/list/delete directories \n - Move files/directories \n - Search files \n - Get file metadata " ;
2025-03-16 17:17:01 +08:00
inline static const json parameters_ = json : : parse ( R " json({
" type " : " object " ,
" properties " : {
2025-03-19 18:44:54 +08:00
" command " : {
2025-03-16 17:17:01 +08:00
" type " : " string " ,
2025-03-19 18:44:54 +08:00
" description " : " ### Commands \n \n - **read_file** \n - Read complete contents of a file \n - Input: `path` (string) \n - Reads complete file contents with UTF-8 encoding \n \n - **read_multiple_files** \n - Read multiple files simultaneously \n - Input: `paths` (string[]) \n - Failed reads won't stop the entire operation \n \n - **write_file** \n - Create new file or overwrite existing (exercise caution with this) \n - Inputs: \n - `path` (string): File location \n - `content` (string): File content \n \n - **edit_file** \n - Make selective edits using advanced pattern matching and formatting \n - Features: \n - Line-based and multi-line content matching \n - Whitespace normalization with indentation preservation \n - Fuzzy matching with confidence scoring \n - Multiple simultaneous edits with correct positioning \n - Indentation style detection and preservation \n - Git-style diff output with context \n - Preview changes with dry run mode \n - Failed match debugging with confidence scores \n - Inputs: \n - `path` (string): File to edit \n - `edits` (array): List of edit operations \n - `oldText` (string): Text to search for (can be substring) \n - `newText` (string): Text to replace with \n - `dryRun` (boolean): Preview changes without applying (default: false) \n - `options` (object): Optional formatting settings \n - `preserveIndentation` (boolean): Keep existing indentation (default: true) \n - `normalizeWhitespace` (boolean): Normalize spaces while preserving structure (default: true) \n - `partialMatch` (boolean): Enable fuzzy matching (default: true) \n - Returns detailed diff and match information for dry runs, otherwise applies changes \n - Best Practice: Always use dryRun first to preview changes before applying them \n \n - **create_directory** \n - Create new directory or ensure it exists \n - Input: `path` (string) \n - Creates parent directories if needed \n - Succeeds silently if directory exists \n \n - **list_directory** \n - List directory contents with [FILE] or [DIR] prefixes \n - Input: `path` (string) \n \n - **move_file** \n - Move or rename files and directories \n - Inputs: \n - `source` (string) \n - `destination` (string) \n - Fails if destination exists \n \n - **search_files** \n - Recursively search for files/directories \n - Inputs: \n - `path` (string): Starting directory \n - `pattern` (string): Search pattern \n - `excludePatterns` (string[]): Exclude any patterns. Glob formats are supported. \n - Case-insensitive matching \n - Returns full paths to matches \n \n - **get_file_info** \n - Get detailed file/directory metadata \n - Input: `path` (string) \n - Returns: \n - Size \n - Creation time \n - Modified time \n - Access time \n - Type (file/directory) \n - Permissions \n \n - **list_allowed_directories** \n - List all directories the server is allowed to access \n - No input required \n - Returns: \n - Directories that this server can read/write from " ,
2025-03-16 17:17:01 +08:00
" enum " : [
" read_file " ,
" read_multiple_files " ,
" write_file " ,
" edit_file " ,
" create_directory " ,
" list_directory " ,
" move_file " ,
" search_files " ,
" get_file_info " ,
" list_allowed_directories "
]
2025-03-17 01:58:37 +08:00
} ,
2025-03-16 17:17:01 +08:00
" path " : {
" type " : " string " ,
2025-03-19 18:44:54 +08:00
" description " : " The path to the file or directory to operate on. Only works within allowed directories. Required by all commands except `read_multiple_files`, `move_file` and `list_allowed_directories`. "
2025-03-16 17:17:01 +08:00
} ,
" paths " : {
" type " : " array " ,
" description " : " An array of paths to files to operate on. Only works within allowed directories. Required by `read_multiple_files`. " ,
" items " : {
" type " : " string "
}
} ,
" content " : {
" type " : " string " ,
" description " : " The content to write to the file. Required by `write_file`. "
} ,
" edits " : {
" type " : " array " ,
2025-03-17 01:58:37 +08:00
" description " : " Each edit replaces exact line sequences with new content. Required by `edit_file`. "
2025-03-16 17:17:01 +08:00
} ,
2025-03-19 18:44:54 +08:00
" dryRun " : {
" type " : " boolean " ,
" description " : " Preview changes without applying. Default: false. Required by `edit_file`. "
} ,
" options " : {
" type " : " object " ,
" description " : " Optional formatting settings. Required by `edit_file`. " ,
" properties " : {
" preserveIndentation " : {
" type " : " boolean " ,
" description " : " Keep existing indentation. Default: true. Required by `edit_file`. "
} ,
" normalizeWhitespace " : {
" type " : " boolean " ,
" description " : " Normalize spaces while preserving structure. Default: true. Required by `edit_file`. "
} ,
" partialMatch " : {
" type " : " boolean " ,
" description " : " Enable fuzzy matching. Default: true. Required by `edit_file`. "
}
}
} ,
2025-03-16 17:17:01 +08:00
" source " : {
" type " : " string " ,
" description " : " The source path to move or rename. Required by `move_file`. "
} ,
" destination " : {
" type " : " string " ,
" description " : " The destination path to move or rename. Required by `move_file`. "
} ,
" pattern " : {
" type " : " string " ,
" description " : " The pattern to search for. Required by `search_files`. "
2025-03-19 18:44:54 +08:00
} ,
" excludePatterns " : {
" type " : " array " ,
" description " : " An array of patterns to exclude from the search. Glob formats are supported. Required by `search_files`. " ,
" items " : {
" type " : " string "
}
2025-03-16 17:17:01 +08:00
}
} ,
2025-03-19 18:44:54 +08:00
" required " : [ " command " ]
2025-03-16 17:17:01 +08:00
} ) json " );
2025-03-19 18:44:54 +08:00
inline static std : : set < std : : string > allowed_commands = {
2025-03-17 14:07:41 +08:00
" read_file " ,
" read_multiple_files " ,
" write_file " ,
" edit_file " ,
" create_directory " ,
" list_directory " ,
" move_file " ,
" search_files " ,
" get_file_info " ,
" list_allowed_directories "
} ;
2025-03-28 17:57:20 +08:00
Filesystem ( ) : BaseMCPTool ( name_ , description_ , parameters_ ) { }
2025-03-16 17:17:01 +08:00
ToolResult execute ( const json & args ) override {
try {
if ( ! _client ) {
return ToolError ( " Failed to initialize shell client " ) ;
}
2025-03-16 22:56:03 +08:00
2025-03-19 18:44:54 +08:00
std : : string command ;
if ( args . contains ( " command " ) ) {
if ( args [ " command " ] . is_string ( ) ) {
command = args [ " command " ] . get < std : : string > ( ) ;
2025-03-16 22:56:03 +08:00
} else {
2025-03-19 18:44:54 +08:00
return ToolError ( " Invalid command format " ) ;
2025-03-16 22:56:03 +08:00
}
} else {
2025-03-19 18:44:54 +08:00
return ToolError ( " 'command' is required " ) ;
2025-03-16 22:56:03 +08:00
}
2025-03-19 18:44:54 +08:00
if ( allowed_commands . find ( command ) = = allowed_commands . end ( ) ) {
return ToolError ( " Unknown command ' " + command + " '. Please use one of the following commands: " +
std : : accumulate ( allowed_commands . begin ( ) , allowed_commands . end ( ) , std : : string ( ) ,
2025-03-17 14:07:41 +08:00
[ ] ( const std : : string & a , const std : : string & b ) {
return a + ( a . empty ( ) ? " " : " , " ) + b ;
} ) ) ;
}
2025-03-19 18:44:54 +08:00
json result = _client - > call_tool ( command , args ) ;
2025-03-16 17:17:01 +08:00
bool is_error = result . value ( " isError " , false ) ;
2025-03-17 16:35:11 +08:00
// Return different ToolResult based on whether there is an error
2025-03-16 17:17:01 +08:00
if ( is_error ) {
return ToolError ( result . value ( " content " , json : : array ( ) ) ) ;
} else {
return ToolResult ( result . value ( " content " , json : : array ( ) ) ) ;
}
} catch ( const std : : exception & e ) {
return ToolError ( std : : string ( e . what ( ) ) ) ;
}
}
} ;
}
# endif // HUMANUS_TOOL_FILE_SAVER_H