ssh_copy_file
Facilitates file transfer between local and remote servers using specified SSH connections. Supports directory creation at the target path for efficient file management.
Instructions
Copy files between local and remote servers or between remote servers
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| createDirectories | No | Create target directories if they don't exist | |
| sourceConnectionId | Yes | Source SSH connection ID (use "local" for local files) | |
| sourcePath | Yes | Source file path | |
| targetConnectionId | Yes | Target SSH connection ID (use "local" for local files) | |
| targetPath | Yes | Target file path |
Input Schema (JSON Schema)
{
"properties": {
"createDirectories": {
"default": true,
"description": "Create target directories if they don't exist",
"type": "boolean"
},
"sourceConnectionId": {
"description": "Source SSH connection ID (use \"local\" for local files)",
"type": "string"
},
"sourcePath": {
"description": "Source file path",
"type": "string"
},
"targetConnectionId": {
"description": "Target SSH connection ID (use \"local\" for local files)",
"type": "string"
},
"targetPath": {
"description": "Target file path",
"type": "string"
}
},
"required": [
"sourceConnectionId",
"sourcePath",
"targetConnectionId",
"targetPath"
],
"type": "object"
}
Implementation Reference
- src/index.ts:658-776 (handler)Main handler function implementing ssh_copy_file tool. Handles local-to-local, local-to-remote, remote-to-local, and remote-to-remote file transfers using fs.copyFile, NodeSSH.putFile/getFile, with directory creation and temp file for remote-remote.private async handleSSHCopyFile(args: unknown) { const params = CopyFileSchema.parse(args); try { // Handle different copy scenarios if (params.sourceConnectionId === 'local' && params.targetConnectionId === 'local') { // Local to local copy await fs.copyFile(params.sourcePath, params.targetPath); return { content: [ { type: 'text', text: `Successfully copied ${params.sourcePath} to ${params.targetPath} (local to local)`, }, ], }; } else if (params.sourceConnectionId === 'local') { // Local to remote const targetSSH = connectionPool.get(params.targetConnectionId); if (!targetSSH) { throw new McpError( ErrorCode.InvalidParams, `Target connection ID '${params.targetConnectionId}' not found` ); } if (params.createDirectories) { const targetDir = path.dirname(params.targetPath); await targetSSH.execCommand(`mkdir -p "${targetDir}"`); } await targetSSH.putFile(params.sourcePath, params.targetPath); return { content: [ { type: 'text', text: `Successfully copied ${params.sourcePath} to ${params.targetConnectionId}:${params.targetPath}`, }, ], }; } else if (params.targetConnectionId === 'local') { // Remote to local const sourceSSH = connectionPool.get(params.sourceConnectionId); if (!sourceSSH) { throw new McpError( ErrorCode.InvalidParams, `Source connection ID '${params.sourceConnectionId}' not found` ); } if (params.createDirectories) { const targetDir = path.dirname(params.targetPath); await fs.mkdir(targetDir, { recursive: true }); } await sourceSSH.getFile(params.targetPath, params.sourcePath); return { content: [ { type: 'text', text: `Successfully copied ${params.sourceConnectionId}:${params.sourcePath} to ${params.targetPath}`, }, ], }; } else { // Remote to remote const sourceSSH = connectionPool.get(params.sourceConnectionId); const targetSSH = connectionPool.get(params.targetConnectionId); if (!sourceSSH) { throw new McpError( ErrorCode.InvalidParams, `Source connection ID '${params.sourceConnectionId}' not found` ); } if (!targetSSH) { throw new McpError( ErrorCode.InvalidParams, `Target connection ID '${params.targetConnectionId}' not found` ); } // Use a temporary local file for remote-to-remote transfer const tempFile = `/tmp/mcp-ssh-temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; try { await sourceSSH.getFile(tempFile, params.sourcePath); if (params.createDirectories) { const targetDir = path.dirname(params.targetPath); await targetSSH.execCommand(`mkdir -p "${targetDir}"`); } await targetSSH.putFile(tempFile, params.targetPath); await fs.unlink(tempFile); // Clean up temp file return { content: [ { type: 'text', text: `Successfully copied ${params.sourceConnectionId}:${params.sourcePath} to ${params.targetConnectionId}:${params.targetPath}`, }, ], }; } catch (error) { // Clean up temp file on error try { await fs.unlink(tempFile); } catch {} throw error; } } } catch (error) { throw new McpError( ErrorCode.InternalError, `File copy failed: ${error instanceof Error ? error.message : String(error)}` ); } }
- src/index.ts:80-86 (schema)Zod schema defining input parameters for ssh_copy_file tool: source/target connection IDs ("local" or SSH ID), paths, and createDirectories option.const CopyFileSchema = z.object({ sourceConnectionId: z.string().describe('Source SSH connection ID (use "local" for local files)'), sourcePath: z.string().describe('Source file path'), targetConnectionId: z.string().describe('Target SSH connection ID (use "local" for local files)'), targetPath: z.string().describe('Target file path'), createDirectories: z.boolean().default(true).describe('Create target directories if they don\'t exist') });
- src/index.ts:280-292 (registration)Tool registration in ListTools handler: defines name 'ssh_copy_file', description, and inputSchema matching CopyFileSchema.name: 'ssh_copy_file', description: 'Copy files between local and remote servers or between remote servers', inputSchema: { type: 'object', properties: { sourceConnectionId: { type: 'string', description: 'Source SSH connection ID (use "local" for local files)' }, sourcePath: { type: 'string', description: 'Source file path' }, targetConnectionId: { type: 'string', description: 'Target SSH connection ID (use "local" for local files)' }, targetPath: { type: 'string', description: 'Target file path' }, createDirectories: { type: 'boolean', default: true, description: 'Create target directories if they don\'t exist' } }, required: ['sourceConnectionId', 'sourcePath', 'targetConnectionId', 'targetPath'] },
- src/index.ts:491-492 (registration)Registration in CallToolRequestSchema switch statement: routes 'ssh_copy_file' calls to handleSSHCopyFile handler.case 'ssh_copy_file': return await this.handleSSHCopyFile(args);