Skip to content

Commit 73e8965

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 2aa85c2 commit 73e8965

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
@@ -136,6 +136,11 @@ const MoveFileArgsSchema = z.object({
136136
destination: z.string(),
137137
});
138138

139+
const CopyFileArgsSchema = z.object({
140+
source: z.string(),
141+
destination: z.string(),
142+
});
143+
139144
const SearchFilesArgsSchema = z.object({
140145
path: z.string(),
141146
pattern: z.string(),
@@ -369,6 +374,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
369374
"Only works within allowed directories.",
370375
inputSchema: zodToJsonSchema(EditFileArgsSchema) as ToolInput,
371376
},
377+
{
378+
name: "copy_file",
379+
description:
380+
"Copy a file or directory from source to destination. For directories, copies all contents recursively. " +
381+
"If destination exists, the operation will fail. Both source and destination must be within allowed directories.",
382+
inputSchema: zodToJsonSchema(CopyFileArgsSchema) as ToolInput,
383+
},
372384
{
373385
name: "create_directory",
374386
description:
@@ -612,6 +624,41 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
612624
};
613625
}
614626

627+
case "copy_file": {
628+
const parsed = CopyFileArgsSchema.safeParse(args);
629+
if (!parsed.success) {
630+
throw new Error(`Invalid arguments for copy_file: ${parsed.error}`);
631+
}
632+
const validSourcePath = await validatePath(parsed.data.source);
633+
const validDestPath = await validatePath(parsed.data.destination);
634+
635+
// Check if destination already exists
636+
try {
637+
await fs.access(validDestPath);
638+
throw new Error(`Destination already exists: ${parsed.data.destination}`);
639+
} catch (error) {
640+
// If error is NOT about file not existing, re-throw it
641+
if (error instanceof Error && !error.message.includes('ENOENT')) {
642+
throw error;
643+
}
644+
}
645+
646+
// Check if source is a directory
647+
const sourceStats = await fs.stat(validSourcePath);
648+
649+
if (sourceStats.isDirectory()) {
650+
// For directories, use fs.cp with recursive option
651+
await fs.cp(validSourcePath, validDestPath, { recursive: true });
652+
} else {
653+
// For files, use copyFile which is optimized for large files
654+
await fs.copyFile(validSourcePath, validDestPath);
655+
}
656+
657+
return {
658+
content: [{ type: "text", text: `Successfully copied ${parsed.data.source} to ${parsed.data.destination}` }],
659+
};
660+
}
661+
615662
case "list_allowed_directories": {
616663
return {
617664
content: [{

0 commit comments

Comments
 (0)