Skip to content

Commit 2003e17

Browse files
committed
feat(filesystem): add copy_file tool for efficient file/directory copying
Implements a new copy_file tool that addresses issue #1581 by providing an efficient way to copy files and directories without requiring read/write operations for large files. ## Changes: - Add copy_file tool with support for both files and directories - Use fs.copyFile for files (optimized for large files) - Use fs.cp with recursive option for directories - Include proper validation and error handling - Update README documentation ## Implementation Details: - Validates both source and destination paths are within allowed directories - Checks if destination exists and fails appropriately - Follows existing tool patterns for consistency - Maintains security checks and path validation Fixes #1581
1 parent 531c1fe commit 2003e17

File tree

2 files changed

+55
-0
lines changed

2 files changed

+55
-0
lines changed

src/filesystem/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ Node.js server implementing Model Context Protocol (MCP) for filesystem operatio
7171
- `destination` (string)
7272
- Fails if destination exists
7373

74+
- **copy_file**
75+
- Copy a file or directory from source to destination
76+
- Inputs:
77+
- `source` (string)
78+
- `destination` (string)
79+
- For directories, copies all contents recursively
80+
- Fails if destination exists
81+
7482
- **search_files**
7583
- Recursively search for files/directories
7684
- Inputs:

src/filesystem/index.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,11 @@ const MoveFileArgsSchema = z.object({
143143
destination: z.string(),
144144
});
145145

146+
const CopyFileArgsSchema = z.object({
147+
source: z.string(),
148+
destination: z.string(),
149+
});
150+
146151
const SearchFilesArgsSchema = z.object({
147152
path: z.string(),
148153
pattern: z.string(),
@@ -479,6 +484,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
479484
"Only works within allowed directories.",
480485
inputSchema: zodToJsonSchema(EditFileArgsSchema) as ToolInput,
481486
},
487+
{
488+
name: "copy_file",
489+
description:
490+
"Copy a file or directory from source to destination. For directories, copies all contents recursively. " +
491+
"If destination exists, the operation will fail. Both source and destination must be within allowed directories.",
492+
inputSchema: zodToJsonSchema(CopyFileArgsSchema) as ToolInput,
493+
},
482494
{
483495
name: "create_directory",
484496
description:
@@ -818,6 +830,41 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
818830
};
819831
}
820832

833+
case "copy_file": {
834+
const parsed = CopyFileArgsSchema.safeParse(args);
835+
if (!parsed.success) {
836+
throw new Error(`Invalid arguments for copy_file: ${parsed.error}`);
837+
}
838+
const validSourcePath = await validatePath(parsed.data.source);
839+
const validDestPath = await validatePath(parsed.data.destination);
840+
841+
// Check if destination already exists
842+
try {
843+
await fs.access(validDestPath);
844+
throw new Error(`Destination already exists: ${parsed.data.destination}`);
845+
} catch (error) {
846+
// If error is NOT about file not existing, re-throw it
847+
if (error instanceof Error && !error.message.includes('ENOENT')) {
848+
throw error;
849+
}
850+
}
851+
852+
// Check if source is a directory
853+
const sourceStats = await fs.stat(validSourcePath);
854+
855+
if (sourceStats.isDirectory()) {
856+
// For directories, use fs.cp with recursive option
857+
await fs.cp(validSourcePath, validDestPath, { recursive: true });
858+
} else {
859+
// For files, use copyFile which is optimized for large files
860+
await fs.copyFile(validSourcePath, validDestPath);
861+
}
862+
863+
return {
864+
content: [{ type: "text", text: `Successfully copied ${parsed.data.source} to ${parsed.data.destination}` }],
865+
};
866+
}
867+
821868
case "list_allowed_directories": {
822869
return {
823870
content: [{

0 commit comments

Comments
 (0)